Print /proc/slabinfo by creating a kernel routine - c

+
trying to learn/tinker with the Linux kernel by following some PDFs and online resources.
I wanted to print cache allocations using kmalloc_caches[], print somewhat similar info present by cating /proc/slabinfo
So far, I have come to understand I have implement s_show (on line 5421 in /mm/slub.c) on kernel version 3.3
I understand most of the function and any function calls made inside it. But what puzzles me are the arguments passed to it. Cause, when I search where s_show() is called, this is what I find:
static const struct seq_operations slabinfo_op = {
.start = s_start,
.next = s_next,
.stop = s_stop,
.show = s_show,
};
Now, I understand somewhat of what is going on here, but I still don't get where the arguments are coming from.
s_show() function :
static int s_show(struct seq_file *m, void *p)
{
unsigned long nr_partials = 0;
unsigned long nr_slabs = 0;
unsigned long nr_inuse = 0;
unsigned long nr_objs = 0;
unsigned long nr_free = 0;
struct kmem_cache *s;
int node;
s = list_entry(p, struct kmem_cache, list);
for_each_online_node(node) {
struct kmem_cache_node *n = get_node(s, node);
if (!n)
continue;
nr_partials += n->nr_partial;
nr_slabs += atomic_long_read(&n->nr_slabs);
nr_objs += atomic_long_read(&n->total_objects);
nr_free += count_partial(n, count_free);
}
nr_inuse = nr_objs - nr_free;
seq_printf(m, "%-17s %6lu %6lu %6u %4u %4d", s->name, nr_inuse,
nr_objs, s->size, oo_objects(s->oo),
(1 << oo_order(s->oo)));
seq_printf(m, " : tunables %4u %4u %4u", 0, 0, 0);
seq_printf(m, " : slabdata %6lu %6lu %6lu", nr_slabs, nr_slabs,
0UL);
seq_putc(m, '\n');
return 0;
}

The seq_file argument is really the file into which you're outputting the data you want to print. It is automatically constructed.
However, the interesting thing is the p argument. To understand where it comes from, see this code:
static void *s_start(struct seq_file *m, loff_t *pos)
{
loff_t n = *pos;
mutex_lock(&cache_chain_mutex);
if (!n)
print_slabinfo_header(m);
return seq_list_start(&cache_chain, *pos);
}
static void *s_next(struct seq_file *m, void *p, loff_t *pos)
{
return seq_list_next(p, &cache_chain, pos);
}
static void s_stop(struct seq_file *m, void *p)
{
mutex_unlock(&cache_chain_mutex);
}
The s_start and s_next functions return what is given as the p argument. You may want to read the code of seq_list_start and seq_list_next.

Related

Why this Lua program that limits memory usage crashes?

I'm working on Exercise 27.4 from the 4th edition of Programming in Lua. I am using Lua 5.4.4, and my solution is the following.
/*
Exercise 27.4: Write a library that allows a script to limit the total amount of memory used
by its Lua state. It may offer a single function, `setlimit`, to set that limit.
The library should set its own allocation function. This function, before calling the
original allocator, checks the total memory in use and returns NULL if the requested memory
exceeds the limit.
(Hint: the library can use the user data of the allocation function to keep its state:
the byte count, the current memory limit, etc.; remember to use the original user data
when calling the original allocation function.)
*/
#include "lauxlib.h"
#include <stdlib.h>
typedef struct {
void *o_ud;
lua_Alloc o_alloc;
size_t mem;
size_t limit;
} memlimit_ud;
static void *memlimit_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
memlimit_ud *m_ud = ud;
size_t mem = m_ud->mem;
if (mem + nsize - osize > m_ud->limit) {
return NULL;
}
void *res = m_ud->o_alloc(m_ud->o_ud, ptr, osize, nsize);
if (res) m_ud->mem += nsize - osize;
return res;
}
static int l_setlimit(lua_State *L) {
int n = luaL_checkinteger(L, 1) * 1024;
memlimit_ud *ud;
lua_getallocf(L, (void **) &ud);
ud->limit = n;
return 0;
}
static const struct luaL_Reg memlimit[] = {
{"setlimit", l_setlimit},
{ NULL, NULL},
};
int luaopen_memlimit(lua_State *L) {
memlimit_ud *ud = lua_newuserdata(L, sizeof(memlimit_ud));
luaL_ref(L, LUA_REGISTRYINDEX);
ud->o_alloc = lua_getallocf(L, &ud->o_ud);
ud->mem = 0;
ud->limit = -1;
lua_setallocf(L, memlimit_alloc, ud);
luaL_newlib(L, memlimit);
return 1;
}
However, it throws a segment fault when the script ends. I found this question, and have tried to change lua_newuserdata to malloc, but it still doesn't work. While debugging, I noticed that it was trying to call memlimit_alloc before it crashed, but the function seemed like didn't exist (the former calls to this function were successful).
How can I fix this?

Send message through Ring (Circular) Buffer between Threads (in C)

I need to send a message from Main thread to my Created Thread using WinAPI and Ring Buffer.
I defined structures and wrote functions for my Ring buffer.
Ring Buffer - it contains head, tail, size and pointer to the structure Descriptor which has length of Data and data itself. As I need to send 2 parameters to CreateThread function, I created the third structure ThreadParams to keep 2 parameters.
I want to leave this structures how they are now, not changeable.
typedef struct _Descriptor
{
uint32_t dataLen;
void * data;
} Descriptor;
typedef struct _ringBuffer
{
Descriptor *bufferData;
int head;
int tail;
int size;
} ringBuffer;
typedef struct _ThreadParams
{
void * ptr1;
void * ptr2;
} ThreadParams;
There are my realisations of Ring Buffer functions:
void bufferFree(ringBuffer *buffer)
{
free(buffer->bufferData);
}
void ringInitialization(ringBuffer *buffer, int size)
{
buffer->size = size;
buffer->head = 0;
buffer->tail = 0;
buffer->bufferData = (Descriptor*)malloc(sizeof(Descriptor) * size);
}
int pushBack(ringBuffer *buffer, void * data) // fill buffer
{
buffer->bufferData[buffer->tail++] = *(Descriptor*)data;
if (buffer->tail == buffer->size)
{
buffer->tail = 0;
}
return 0;
}
int popFront(ringBuffer *buffer)
{
if (buffer->head != buffer->tail)
{
buffer->head++;
if (buffer->head == buffer->size)
{
buffer->head = 0;
}
}
return 0;
}
My main: I checked that I can send a few bytes (the memory is shared between threads), now I need to send a big message (> BUFF_SIZE) though Ring Buffer what I'm trying to do in while() cycle. Here is the question: how should I do it? My thing doesn't work because I catch an exception in printf() function (memory acces violation).
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <strsafe.h>
#include <stdint.h>
#define RING_SIZE 256
#define BUFFER_SIZE 1024
DWORD WINAPI HandleSendThread(LPVOID params);
uint8_t * getPointer(uint8_t *buffer, uint32_t index)
{
uint8_t * ptr = ((uint8_t*)buffer) + index * BUFFER_SIZE;
return ptr;
}
int main(int argc, char * argv[])
{
//Descriptor * ringData = (Descriptor *)malloc(sizeof(Descriptor) * RING_SIZE);
ringBuffer ring;
ringInitialization(&ring, RING_SIZE);
void * packetBuffer = malloc(BUFFER_SIZE * RING_SIZE);
uint8_t * currentBuffer = getPointer(packetBuffer, 0);
uint8_t * str = "Mr. and Mrs. Dursley, of number four, Privet Drive, were proud to say that they were perfectly normal, thank you very much. They were the last people you'd expect to be involved in anything strange or mysterious, because they just didn't hold with such nonsense. Mr.Dursley was the director of a firm called Grunnings, which made drills.He was a big, beefy man with hardly any neck, although he did have a very large mustache.Mrs.Dursley was thin and blonde and had nearly twice the usual amount of neck, which came in very useful as she spent so much of her time craning over garden fences, spying on the neighbors.The Dursleys had a small son called Dudley and in their opinion there was no finer boy anywhere.";
strcpy(currentBuffer, str);
ring.bufferData[0].data = currentBuffer;
ring.bufferData[0].dataLen = strlen(str);
int currentSize = 0;
int ringSize = RING_SIZE;
while(ring.bufferData[0].data != '\0')
{
for (int i = currentSize; i < ringSize; i + RING_SIZE)
{
pushBack(&ring, currentBuffer);
printf("h = %s, tail = %s, dataBuffer = %s\n", (char*)ring.head, (char*)ring.tail, (char*)ring.bufferData[i].data);
}
currentSize = ringSize;
ringSize = 2 * ringSize;
popFront(&ring);
}
ThreadParams params = { &ring, packetBuffer };
HANDLE MessageThread = 0;
MessageThread = CreateThread(NULL, 0, HandleSendThread, &params, 0, NULL);
if (MessageThread == NULL)
{
ExitProcess(MessageThread);
}
WaitForSingleObject(MessageThread, INFINITE);
CloseHandle(MessageThread);
system("pause");
return 0;
}
And my CreateThread function:
DWORD WINAPI HandleSendThread(LPVOID params)
{
ringBuffer * ring = ((ThreadParams*)params)->ptr1;
void * buffer = ((ThreadParams*)params)->ptr2;
//ring->bufferData[0].dataLen = sizeof(buffer) + sizeof(ring->bufferData[0])*1024;
printf("Shared memory check: ringBuffer data = \"%s\", \nlength = %d\n", (char*)ring->bufferData[0].data, ring->bufferData[0].dataLen);
return 0;
}
Your most immediate problem is the inconsistency between the code in pushBack(), which expects data to point to a Descriptor, and the code in your main function, which passes in a pointer to a string instead.
If you had declared pushBack() properly, i.e.,
void pushBack(ringBuffer *buffer, Descriptor * data)
{
buffer->bufferData[buffer->tail++] = *data;
if (buffer->tail == buffer->size)
{
buffer->tail = 0;
}
}
Then the compiler would have been able to warn you about the discrepancy.
You also have an infinite loop here:
for (int i = currentSize; i < ringSize; i + RING_SIZE)
You probably meant
for (int i = currentSize; i < ringSize; i += RING_SIZE)
... although it still doesn't look to me like it will do anything sensible. Nor do I understand the purpose of the outer loop, which compares a pointer to a character.
Found a solution
int main(int argc, char * argv[])
{
ringBuffer ring;
ringInitialization(&ring, RING_SIZE);
void * packetBuffer = malloc(BUFFER_SIZE * RING_SIZE);
Descriptor temp = { 0 };
uint8_t * currentBuffer = getPointer(packetBuffer, 0);
uint8_t * str = "Mr. and Mrs. Dursley, of number four, Privet Drive, were proud to say that they were perfectly normal, thank you very much. They were the last people you'd expect to be involved in anything strange or mysterious, because they just didn't hold with such nonsense. Mr.Dursley was the director of a firm called Grunnings, which made drills.He was a big, beefy man with hardly any neck, although he did have a very large mustache.Mrs.Dursley was thin and blonde and had nearly twice the usual amount of neck, which came in very useful as she spent so much of her time craning over garden fences, spying on the neighbors.The Dursleys had a small son called Dudley and in their opinion there was no finer boy anywhere.";
strcpy(currentBuffer, str);
temp.dataLen = strlen(str);
temp.data = currentBuffer;
pushBack(&ring, &temp);
ThreadParams params = { &ring, packetBuffer };
HANDLE MessageThread = 0;
MessageThread = CreateThread(NULL, 0, HandleSendThread, &params, 0, NULL);
if (MessageThread == NULL)
{
ExitProcess(MessageThread);
}
WaitForSingleObject(MessageThread, INFINITE);
CloseHandle(MessageThread);
system("pause");
return 0;
}
DWORD WINAPI HandleSendThread(LPVOID params)
{
ringBuffer * ring = ((ThreadParams*)params)->ptr1;
void * buffer = ((ThreadParams*)params)->ptr2;
Descriptor * temp = &ring->bufferData[ring->head];
for (int i = 0; i < temp->dataLen; i++)
{
printf("%c", ((char*)temp->data)[i]);
}
printf("\n");
return 0;
}

why realloc() doesn't work when size larger than 8192 bytes?

typedef struct {
void *elems;//address of the memory block
int elemSize; //
int logicLen;//number of existing elements in vector
int allocLen;//allocated space for the vector
} vector;
static void InsertNumbers(vector *numbers, long n, long d)
{
long k;
long residue;
for (k = 0; k < d; k++) {
residue = (long) (((long long)k * (long long) n) % d);
VectorAppend(numbers, &residue);
}
}
void VectorAppend(vector *v, const void *elemAddr)
{
void *target=(char*)v->elems + (v->logicLen * v->elemSize);
if(v->logicLen==v->allocLen){
v->allocLen*=2;
v->elems=realloc(v->elems,v->allocLen*v->elemSize);
assert(v->elems!=NULL);
}
memcpy(target,elemAddr,v->elemSize);
v->logicLen++;
}
Then, I use the following sentence to call InsertNumbers()
vector aVector;
VectorNew(&aVector, sizeof(long),4);
long first=139269,second=3021377;
InsertNumbers(&aVector,first , second);
It seems like 3021377 is too big...
in v->elems=realloc(v->elems,v->allocLen*v->elemSize); I find that when v->allocLen=4096, the program crashes and says:This may be due to a corruption of the heap
why?
This is not a problem with the code you posted, this is a problem somewhere else.
What happens is your program corrupts the heap, and then realloc detects that the heap is corrupted.
You will want to detect the corruption as follows:
Make sure you enable debugging symbols
Run your program through Valgrind
Edit: There is a serious error is in the code you added.
void VectorAppend(vector *v, const void *elemAddr)
{
void *target = (char *) v->elems + v->logicLen * v->elemSize;
if (v->logicLen == v->allocLen) {
v->allocLen *= 2;
// Once you call 'realloc', the value of 'elems' might change
// This means that 'target' is now INVALID
// 'target' is based on the old value of 'elems'
v->elems = realloc(v->elems,v->allocLen*v->elemSize);
assert(v->elems != NULL);
}
memcpy(target, elemAddr, v->elemSize);
v->logicLen++;
}
To fix it, move the calculation for target below the reallocation:
void VectorAppend(vector *v, const void *elemAddr)
{
if (v->logicLen == v->allocLen) {
v->allocLen *= 2;
v->elems = realloc(v->elems,v->allocLen*v->elemSize);
assert(v->elems != NULL);
}
void *target = (char *) v->elems + v->logicLen * v->elemSize;
memcpy(target, elemAddr, v->elemSize);
v->logicLen++;
}
Another error: There is an error in your comments, which is part of the code and I'd recommend taking comments seriously.
VectorNew(&aVector, sizeof(long), 4); // allocate 4*4 bytes
The comment should not say, "allocate 4*4" bytes because that is misleading: someday you'll compile the program on a 64-bit system that isn't Windows, and it will be 8x4 bytes. You're better off removing the comment and just reading the code.

Using MD5 in kernel space of Linux

I am trying to use the kernel space implementation of the md5 algorithm (md5.h and md5.c). It turns out that md5.h does not declare the functions found in md5.c, so I could not simply include md5.h into my c file. I also wanted to avoid altering md5.h to declare the functions, since that could have unintended consequences. Is there any other way to use md5 in kernel space?
Use Crypto API instead of rolling your own.
#include <crypto/hash.h>
bool md5_hash(char *result, char* data, size_t len){
struct shash_desc *desc;
desc = kmalloc(sizeof(*desc), GFP_KERNEL);
desc->tfm = crypto_alloc_shash("md5", 0, CRYPTO_ALG_ASYNC);
if(desc->tfm == NULL)
return false;
crypto_shash_init(desc);
crypto_shash_update(desc, data, len);
crypto_shash_final(desc, result);
crypto_free_shash(desc->tfm);
return true;
}
Beware, the code posted by wande chen will cause kernel heap corruption.
struct shash_desc *desc;
desc = kmalloc(sizeof(*desc), GFP_KERNEL);
desc->tfm = crypto_alloc_shash("md5", 0, CRYPTO_ALG_ASYNC);
This is because md5 engine will use memory past struct shash_desc for storing md5 context.
The correct way to allocate struct shash_desc can be found here.
*shash = crypto_alloc_shash(name, 0, 0);
size = sizeof(struct shash_desc) + crypto_shash_descsize(*shash);
*sdesc = kmalloc(size, GFP_KERNEL);
struct sdesc {
struct shash_desc shash;
char ctx[];
};
static struct sdesc *init_sdesc(struct crypto_shash *alg)
{
struct sdesc *sdesc;
int size;
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kmalloc(size, GFP_KERNEL);
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
return sdesc;
}
static int calc_hash(struct crypto_shash *alg,
const unsigned char *data, unsigned int datalen,
unsigned char *digest)
{
struct sdesc *sdesc;
int ret;
sdesc = init_sdesc(alg);
if (IS_ERR(sdesc)) {
pr_info("can't alloc sdesc\n");
return PTR_ERR(sdesc);
}
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
kfree(sdesc);
return ret;
}
static int do_md5(const unsigned char *data, unsigned char *out_digest)
{
struct crypto_shash *alg;
char *hash_alg_name = "md5";
unsigned int datalen = strlen(data); // remove the null byte
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
if(IS_ERR(alg)){
pr_info("can't alloc alg %s\n", hash_alg_name);
return PTR_ERR(alg);
}
unsigned char hash[32];
calc_hash(alg, data, datalen, hash);
// Very dirty print of 8 first bytes for comparaison with sha256sum
printk(KERN_INFO "HASH(%s, %i): %02x%02x%02x%02x%02x%02x%02x%02x\n",
data, datalen, hash[0] & 0xFFu, hash[1] & 0xFFu, hash[2] & 0xFFu, hash[3] & 0xFFu, hash[4] & 0xFFu,
hash[5] & 0xFFu, hash[6] & 0xFFu, hash[7] & 0xFFu);
char c[3];
for (int i = 0; i < 16; i++)
{
sprintf( c, "%02x", hash[i] & 0xFFu);
memcpy(out_digest+i*2, c, 2);
}
crypto_free_shash(alg);
return 0;
}
tested.

Code for writing and reading on a device file from a kernel module?

I have tried the following code many times .
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
#include<linux/semaphore.h>
MODULE_LICENSE("DUAL BSD/GPL");
static int dev_open(struct inode *,struct file *);
static int dev_release(struct inode *,struct file *);
ssize_t dev_read(struct file *,char *, size_t ,loff_t *);
ssize_t dev_write(struct file *,const char *,size_t ,loff_t *);
static int major;
int dev_major = 0;
int dev_minor = 0;
struct cdev *cdev;
struct device {
char array[100];
struct semaphore sem;
}chr_arr;
struct file_operations dev_ops = {
.owner = THIS_MODULE,
.read = dev_read,
.write = dev_write,
.open = dev_open,
.release = dev_release
};
ssize_t dev_read(struct file *filp,char *buf,size_t count,loff_t *offset)
{
int i;
i=copy_to_user(buf,chr_arr.array,count);
printk(KERN_ALERT"buff:%s",buf);
return i;
}
ssize_t dev_write(struct file *filp,const char *buf,size_t count,loff_t *offset)
{
//printk(KERN_ALERT"\nsorry,byebye");
int j;
//msg_ptr = kmalloc(count,GFP_KERNEL);
//for(j=0;j<count;j++)
if(count>100)
return -1;
j = copy_from_user(chr_arr.array,buf,count);
//printk(KERN_ALERT"msg_ptr:%s",msg_ptr);
return j;
}
static int dev_open(struct inode *inode,struct file *filp)
{
filp->private_data = inode->i_cdev;
if(down_interruptible(&chr_arr.sem))
{
printk(KERN_INFO " could not hold semaphore");
return -1;
}
//printk(KERN_ALERT"ah ha the device is open !now we can go further");
return 0;
}
static int dev_release(struct inode *inode,struct file *filp)
{
up(&chr_arr.sem);
//module_put(THIS_MODULE);
return 0;
}
static int init_device(void)
{
int result;
dev_t dev_no,dev;
result = alloc_chrdev_region(&dev_no,0,1,"chr_dev");
if(result < 0)
{
printk("sorry no major number left");
return result;
}
major = MAJOR(dev_no);
dev = MKDEV(major,0);
cdev = cdev_alloc();
cdev->ops = &dev_ops;
sema_init(&chr_arr.sem,1);
printk("the major number allocated is %d\n",major);
result = cdev_add(cdev,dev,1);
if(result < 0 )
{
printk(KERN_INFO "Unable to allocate cdev");
return result;
}
return 0;
}
static void clean_device(void)
{
cdev_del(cdev);
unregister_chrdev_region(major,1);
}
module_init(init_device);
module_exit(clean_device);
but its giving me the following warning.
CC [M] /home/karan/practice/scrw/scrw1.o
In file included from /usr/src/linux-2.6.34.10-0.6/arch/x86/include/asm/uaccess.h:571:0,
from /home/karan/practice/scrw/scrw1.c:4:
In function ‘copy_from_user’,inlined from ‘write’ at /home/karan/practice/scrw/scrw1.c:43:6:
/usr/src/linux-2.6.34.10-0.6/arch/x86/include/asm/uaccess_32.h:212:26: warning: call to ‘copy_from_user_overflow’ declared with attribute warning: copy_from_user() buffer size is not provably correct
Building modules, stage 2.
MODPOST 1 modules
CC /home/karan/practice/scrw/scrw1.mod.o
LD [M] /home/karan/practice/scrw/scrw1.ko
and then when I try to write echo hi > /dev/my_dev the screen freezes after 30 secs or so.
The problem is that you should return the number of bytes read/written in your read/write methods, the return value of copy_{from,to}_user() is 0 if everything goes well. Return e.g. count in your write method if copying succeeds:
unsigned long ret;
printk(KERN_INFO "Inside write \n");
if (count > sizeof(char_arr.array) - 1)
return -EINVAL;
ret = copy_from_user(char_arr.array, buff, count);
if (ret)
return -EFAULT;
char_arr.array[count] = '\0';
return count;
You should also make sure that a terminating '\0' character is appended when you copy to your buffer (if you'd like to deal only with strings). If it is binary data you deal with store its length in your struct as well.
An example read method:
ssize_t dev_read(struct file *filp,char *buf,size_t count,loff_t *offset)
{
int len = count >= strlen(chr_arr.array) ? strlen(chr_arr.array) : count;
if (*offset >= strlen(chr_arr.array))
return 0;
if (copy_to_user(buf,chr_arr.array,len))
return -EFAULT;
return len;
}
Edit: messed up example code, fixing it.
Edit2: example read method.

Resources