I am writing a LKM to create a character device driver.
Linux Kernel: 4.4.0-93-generic in VirtualBox, 2GB ram and SWAP is 300Kb
Problem 1
If I write a C program that handles the fd in dev_write, it's all good, it reads as it should, but if I try to use head -n 1 < /dev/opsysmem it does not output anything.
Problem 2
If I repeatedly send a message big enough, everything is fine, my 2MiB buffer fills up and then the following messages are discarded. However, if the message is smaller (i.e 1 char each), it stops after about 10000 nodes. Is this a problem with my implementation of the linked list, a known linux problem or it's just me not observing something in my code?
When I encounter Problem 2, the vCPU throttles in sinusoidal manner
Here are my functions for read, write:
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
Node *msg;
int ret_val;
char* message;
unsigned int message_length;
// Entering critical section
down(&sem); //wait state
msg = pop(&l, 0);
// No message? No wait!
if(!msg) {
up(&sem);
return -EAGAIN;
}
message_length = msg->length;
// Debug only
if(DEBUG) {
message = (char*) kmalloc(message_length + 1, GFP_KERNEL);
memset(message, 0, message_length + 1);
memcpy(message, msg->string, message_length);
if(DEBUG) {
printk(KERN_INFO "The message is: %s", message);
}
kfree(message);
}
// Since we have a message, let's send it!
current_size -= message_length;
if(DEBUG) {
printk(KERN_INFO "List size decreased by :%u (bytes); current: %d \n", msg->length, current_size);
}
// copy_to_user has the format ( * to, *from, size) and returns 0 on success
ret_val = copy_to_user(buffer, msg->string, message_length);
if (!ret_val) {
remove_element(&l, 0);
up(&sem);
return ret_val;
} else {
up(&sem);
if(DEBUG) {
printk(KERN_INFO "opsysmem: Failed to send characters to the user\n");
}
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) {
// buffer larger than 2 * 1024 bytes
if(len > MAX_MESSAGE_SIZE) {
return -EINVAL;
}
// Enter critical section
down(&sem); //wait state
msleep(10000);
// buffer is larger than the total list memory (2MiB)
if(current_size + len > MAX_LIST_SIZE) {
down(&sem);
return -EAGAIN;
}
current_size += len;
if(DEBUG) {
printk(KERN_INFO "Current list size %d \n", current_size);
}
push(&l, buffer, len);
up(&sem);
// Exit critical section
return len;
}
And here is my linked list
typedef struct Node {
unsigned int length;
char* string;
struct Node *next;
} Node;
typedef struct list{
struct Node *node;
} list;