Linux kernel linked lists - and sorting - c

I'm implementing some system calls which are mainly supposed to maintain a list of processes, and a list of open files with each process. However I'm having a hard time debugging the code, which requires recompiling over and over again.
Here are probably my main problems:
When and how can I initialize the list keeping the processes? Currently I'm checking if the list is empty before initializing but it doesn't work. I also tried head == head->prev->next but failed.
What's the correct way to call the list_sort considering the related 'compare' function? I'm getting the error
[ 1827.710802] BUG: unable to handle kernel paging request at fffff550
[ 1827.710808] IP: [] compare+0x8/0x20
[ 1827.710814] *pde = 00a32067 *pte = 00000000
This is the code. I'd really appreciate you reviewing it, especially the init system call whose correct behavior the others rely on.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/fdtable.h>
#include <linux/list_sort.h>
#include <linux/slab.h>
struct fileDescriptor {
int fd;
struct list_head list;
};
struct processInfo {
struct task_struct task;
int len_files;
struct fileDescriptor listHead;
struct list_head list;
};
struct processInfo listHead;
int compare(void* priv, struct list_head *a, struct list_head *b)
{
struct processInfo *p1 = container_of(a, struct processInfo, list);
struct processInfo *p2 = container_of(b, struct processInfo, list);
if (p1->task.pid > p2->task.pid) return -1;
else return 1;
}
asmlinkage long sys_init_process_list(pid_t p)
{
struct pid* pid;
struct task_struct *task;
struct files_struct *processFiles;
struct fdtable *filesTable;
struct processInfo *newProcess;
if(list_empty(&listHead.list))
{
printk("____list_empty_____\n");
INIT_LIST_HEAD(&listHead.list);
}
else
printk("____list_not_empty_____\n");
pid = find_get_pid(p);
if (pid == NULL)
{
sys_init_process_list(1);
return 1;
}
else
{
struct list_head *list;
struct task_struct *childTask;
struct fileDescriptor *newfd;
int i = 0;
task = pid_task(pid, PIDTYPE_PID);
newProcess = (struct processInfo*) kmalloc(sizeof(*newProcess), GFP_KERNEL);
//copy task structure
newProcess->task = *task;
//adding file descriptors
INIT_LIST_HEAD(&(newProcess->listHead).list);
processFiles = task->files;
filesTable = files_fdtable(processFiles);
while (filesTable->fd[i] != NULL)
{
newfd = (struct fileDescriptor*) kmalloc(sizeof(*newfd), GFP_KERNEL);
newfd->fd = i;
INIT_LIST_HEAD(&newfd->list);
list_add_tail(&(newfd->list), &(newProcess->listHead).list);
i++;
}
newProcess->len_files = i;
INIT_LIST_HEAD(&newProcess->list);
// add the new node to mylist
list_add_tail(&(newProcess->list), &(listHead.list));
//adding childern
list_for_each(list, &task->children)
{
childTask = list_entry(list, struct task_struct, sibling);
sys_init_process_list(childTask->pid);
}
return 0;
}
}
//////////////////////////////////////////
asmlinkage long sys_sort_process_list(void)
{
if(list_empty(&listHead.list))
{
printk("empty list\n");
return 1;
}
list_sort(NULL, &listHead.list, compare);
return 0;
}
////////////////////////////////////////
asmlinkage long sys_print_process_list(void)
{
struct processInfo *aProcess;
if (sys_sort_process_list())
{
printk("empty list\n");
return 1;
}
list_for_each_entry(aProcess, &listHead.list, list)
{
printk("%d, %d\n", (aProcess->task).pid, aProcess->len_files);
}
return 0;
}
//////////////////////////////////////////////////
asmlinkage long sys_clear_process_list(void)
{
struct processInfo *aProcess, *tmp;
if(list_empty(&listHead.list))
{
printk("empty list\n");
return 1;
}
printk("deleting the list");
list_for_each_entry_safe(aProcess, tmp, &listHead.list, list)
{
printk("freeing %d", (aProcess->task).pid);
list_del(&aProcess->list);
kfree(aProcess);
}
return 0;
}
EDIT: I managed to somehow solve my problem with 'static definition' which is done at compile time, namely I turned
struct processInfo listHead;
to
struct processInfo listHead =
{
.list = LIST_HEAD_INIT(listHead.list)
}
and now everything seems to work just fine. I'm not sure if this is the best way though, so I'm going to keep the question open so other possible solutions may be added.

Unlike to many other object, zero-initialization of linked list leaves it in invalid state. That is, none of linked list's functions can be used for zero-initialized list. Even list_empty one.
Proper initialization of linked list includes LIST_HEAD_INIT (as a struct initializer) or INIT_LIST_HEAD (at runtime).

Related

I am learning data structures and stuck at implementation of queues using linked-list in c

I have written the below piece of code for implementing the queues and their operations(enqueue). The program compiled well with no errors but when the input is given for insertion(enqueue operation) the program stops working and shows a.exe has stopped working
The program contains create_node() function which returns a node, linkedlist initialize function, queue initialize function both of these functions are void functions, and insertion at the end of the linked list function is written which in turn calls the enqueue function.
Note: There's no display function to print all the elements to queue
I think there might be something wrong with initialization functions but I am not sure about it.
#include <stdlib.h>
#include <stdio.h>
typedef struct node node;
struct node
{
int id;
node *link;
};
typedef struct list
{
node *head;
node *tail;
int number_of_nodes;
} List;
typedef struct queue
{
List *ptr_list;
} Queue;
static node *create_node(int id,node *link)
{
node *temp = (node*)malloc(sizeof(node));
temp->id=id;
temp->link=link;
return temp;
}
void list_initialize(List *ptr_list)
{
ptr_list = (List*)malloc(sizeof(List));
ptr_list->head=ptr_list->tail=NULL;
ptr_list->number_of_nodes = 0;
}
void list_insert_rear(List *ptr_list, int id)
{L
node *temp = create_node(id,NULL);
if(ptr_list->tail==NULL)
{
ptr_list->head=ptr_list->tail=NULL;
ptr_list->number_of_nodes++;
}
else
{
ptr_list->tail->link=temp;
ptr_list->tail=temp;
ptr_list->number_of_nodes++;
}
}
void queue_initialize(Queue *queue_list)
{
queue_list = (Queue*)malloc(sizeof(Queue));
list_initialize(queue_list->ptr_list);
}
void queue_enqueue(Queue *ptr, int id)
{
list_insert_rear(ptr->ptr_list,id);
}
int main()
{
Queue queue;
queue_initialize(&queue);
int choice, id, t;
int loop = 1;
while (loop)
{
scanf("%d", &choice);
switch (choice)
{
case 0:
scanf("%d", &id);
queue_enqueue(&queue, id);
break;
default:
loop =0;
break;
}
}
}
This is a problem I actually ran in to a few times and can be tricky to spot. In list_initialize, you should use List **ptr_list.
In the first line of this function, you are changing the pointer, but only in the scope of this variable, so the pointer you put in doesn't actually point to the data anymore, this can be solved by making it a pointer to a pointer. (It's kinda hard to explain)
The function would be (didn't test it):
void list_initialize(List **ptr_list)
{
List *ptr_listnew;
ptr_listnew = (List*)malloc(sizeof(List));
ptr_listnew->head=ptr_listnew->tail=NULL;
ptr_listnew->number_of_nodes = 0;
*ptr_list = ptr_listnew;
}
This is also the case in queue_initialize, which requires the same kind of fix.
You are confusing/combining struct allocation and struct initialization so that your functions do neither well. (See my top comments). As a result, your struct pointers aren't initialized properly and you're getting UB (undefined behavior), most likely a SIGSEGV (segfault).
Although a single function can do both, separating them can give you some insight.
Side note: Don't cast the result of malloc. See: Do I cast the result of malloc?
Here's a refactored version. Original code is marked with #if 0 and new code is marked with #if 1. Bug/fixes are annotated.
#include <stdlib.h>
#include <stdio.h>
typedef struct node node;
struct node {
int id;
node *link;
};
typedef struct list {
node *head;
node *tail;
int number_of_nodes;
} List;
// NOTE/STYLE: use more descriptive names
typedef struct queue {
#if 0
List *ptr_list;
#else
List *queue_list;
#endif
} Queue;
static node *
create_node(int id, node *link)
{
node *temp = (node *) malloc(sizeof(node));
temp->id = id;
temp->link = link;
return temp;
}
void
list_initialize(List *ptr_list)
{
// NOTE/BUG: you are blowing away caller's pointer value
#if 0
ptr_list = malloc(sizeof(List));
#endif
ptr_list->head = ptr_list->tail = NULL;
ptr_list->number_of_nodes = 0;
}
#if 1
List *
list_create(void)
{
List *ptr_list;
ptr_list = malloc(sizeof(List));
list_initialize(ptr_list);
return ptr_list;
}
#endif
void
list_insert_rear(List *ptr_list, int id, int time)
{
node *temp = create_node(id, NULL);
if (ptr_list->tail == NULL) {
ptr_list->head = ptr_list->tail = NULL;
ptr_list->number_of_nodes++;
}
else {
ptr_list->tail->link = temp;
ptr_list->tail = temp;
ptr_list->number_of_nodes++;
}
}
void
queue_initialize(Queue *queue_list)
{
// NOTE/BUG: you are blowing away caller's pointer value
#if 0
queue_list = malloc(sizeof(Queue));
#endif
queue_list->queue_list = list_create();
}
#if 1
Queue *
queue_create(void)
{
Queue *queue_list;
queue_list = malloc(sizeof(Queue));
queue_initialize(queue_list);
return queue_list;
}
#endif
// NOTE/BUG: wrong prototype
#if 0
void
queue_enqueue(Queue *queue, int id)
#else
void
queue_enqueue(Queue *queue, int id, int time)
#endif
{
// NOTE/BUG: not enough arguments for call
#if 0
list_insert_rear(queue->queue_list, id);
#else
list_insert_rear(queue->queue_list, id, time);
#endif
}
int
main(void)
{
Queue queue;
queue_initialize(&queue);
int choice, id, t;
int loop = 1;
#if 1
t = 0;
#endif
while (loop) {
scanf("%d", &choice);
switch (choice) {
case 0:
scanf("%d", &id);
queue_enqueue(&queue, id, t);
++t;
break;
default:
loop = 0;
break;
}
}
#if 1
return 0;
#endif
}

Linux Kernel List, Freeing memory

I am currently learning some Linux Driver writing, and i try to implement list with the Linux Kernel list API.
My code just allocate a list and add 2 elements to it.
All go Ok until i try to free the memory i allocate, here i got a segmentation fault, and from what i test, it come from list_del call.
But i cannot see where i did wrong.
Here is my code :
struct drv_cdev{
int devno;
};
struct drv_dev_itf {
struct drv_cdev cdev; //DATA
struct list_head cdev_list;
};
struct drv_dev {
int number;
struct list_head list_master;
struct drv_dev_itf drv_dev_itf;
};
void add_node(struct drv_dev *dev, int num)
{
struct drv_dev_itf *new_cdev;
new_cdev = kmalloc(sizeof(struct drv_dev_itf*), GFP_KERNEL);
new_cdev->cdev.devno = num;
INIT_LIST_HEAD(&new_cdev->cdev_list);
list_add(&new_cdev->cdev_list, &dev->list_master);
}
void destroy_list(struct drv_dev *dev)
{
struct list_head *position;
struct drv_dev_itf *data_structure;
list_for_each (position, &dev->list_master) {
data_structure = list_entry(position, struct drv_dev_itf, cdev_list);
printk("Devno testing : %d\n", data_structure->cdev.devno);
//dev_node_release(driver_class, &data_structure->cdev);
list_del(position);
printk("List head deleted\n");
/*kfree(data_structure);
printk("List data_structure deleted\n");*/
}
}
static int __init test_init(void)
{
struct drv_dev *dev;
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
INIT_LIST_HEAD(&dev->list_master);
add_node(dev, 1);
add_node(dev, 2);
destroy_list(dev);
kfree(dev);
return 0;
}
Thanks in advance for your answers, have a nice day.

kernel crash for spinlock concurrency in linux-kernel

I want to clear a list which type is defined by Kernel. I have two main structs, num_wrapper and num. num_wapper has a list of num, and kernel crashs when I do the del_all_node() function.
I try to mark the list_del, and the kernel will not be crash. I don't understand why there will be crash problem since I have use spin_lock to protect this num_list.
Any tips will be appreciate.
The following is the simplified code.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/slab.h>
struct num_wrapper {
struct list_head num_list;
spinlock_t list_lock;
u8 check_num;
};
struct num {
struct list_head node;
int number;
struct num_wrapper* num_w_ptr;
};
s32 del_all_node(struct num_wrapper *number_wrap)
{
struct num *tmp;
struct num *num_head;
spin_lock(&number_wrap->list_lock);
list_for_each_entry_safe(num_head, tmp, &number_wrap->num_list, node) {
printk("num_head is %d\n", num_head->number);
list_del(&num_head->node);//this line seems to have problem
}
spin_unlock(&number_wrap->list_lock);
return 0;
}
static int __init hello_init(void)
{
/*Setup Scenario*/
struct num_wrapper *number_wrap = kzalloc(sizeof(struct num_wrapper)
, GFP_KERNEL);
struct num *number = kzalloc(sizeof(struct num), GFP_KERNEL);
number->number = 10;
number_wrap->check_num = 20;
INIT_LIST_HEAD(&number->node);
INIT_LIST_HEAD(&number_wrap->num_list);
list_add_tail(&number->node, &number_wrap->num_list);
del_all_node(number_wrap);
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Good, haha\n");
}
module_init(hello_init);
module_exit(hello_exit);
update
After doing some debug, I seems to find root cause...
my scenario is as follows :
I have a private data struct which is included in the net_device struct.
And the following is the simplified scenario:
struct xx_if *xx_if; //this is private data in net_device
struct xx_if *tmp;
list_for_each_entry_safe(xx_if, tmp, xx_if_wrap->if_list, list) {
free_netdev(xx_if->ndev);
list_del(&xx_if->list);
}
Since free_netdev will also free the private data xx_if, the code broken...
My fix is change the sequence to these two statements, and it fix the crash problem.
Still strange thing is I have check whether xx_if is NULL, but still lead to crash if I don't interchange these two statements.
I don't clearly understand following code:
INIT_LIST_HEAD(&number->node);
INIT_LIST_HEAD(&number_wrap->num_list);
list_add_tail(&number->node, &number_wrap->num_list);
You init two different structures, then you add one type of list to another type of list.
Is that the way you can do?
I think, that you need something like this:
struct num{
u8 check_num;
struct list_head list;
};
struct num_wrapper{
struct num* num_ptr;
spinlock_t list_lock;
};
int init_num_wrapper(struct num_wrapper** prt){
if(!ptr && *ptr){
return -EINVAL;
}
*ptr = kzalloc(sizeof(struct num_wrapper), GFP_KERNEL);
if(!*ptr){
return -ENOMEM;
}
INIT_LIST_HEAD(& (*ptr)->num_ptr->list);
... init spinlock
return 0;
}
int add_num(num_wrapper* prt_wrap, u8 check_num){
... checking pointers
struct num num* = NULL;
num = kmalloc(sizeof(struct num), GFP_KERNEL);
if(! num){
return -ENOMEM;
}
INIT_LIST_HEAD(&num->list);
num->check_num = check_num;
spin_lock(&prt_wrap->list_lock);
list_add_tail(&num->list, &prt_wrap->num_ptr.list);
spin_unlock(&prt_wrap->list_lock);
return 0;
}
int remove_all_nodes(num_wrapper* prt_wrap){
... checking pointer
struct num *tmp = NULL;
struct num *num_head = NULL;
spin_lock(&number_wrap->list_lock);
list_for_each_entry_safe(num_head, tmp, &prt_wrap->list, list)
{
printk("num_head is %d\n", num_head->number);
list_del(&num_head->node);//this line seems to have problem
}
spin_unlock(&num_wrapper->list_lock);
return 0;
}
Update
Then, you can use above functions for manipulating of num_wrapper.
For example:
//...
struct num_wrapper* nums = NULL;
init_num_wrapper(&nums); // after this call, you will have inited nums var, which can be used with others functions for manipulating with num_wrapper list.
u8 num = 2;
add_num(nums, num); // after this call new node with num will be added to num_wrapper
//...

Infinite "Signal 11 being dropped" loop with Valgrind while trying to add to linked list

I'm trying to create a simple singly linked list in C, and have encountered an infinite "Singal 11 being dropped" loop while running my program in Valgrind.
My .h file:
#ifndef TEST_H
#define TEST_H
struct fruit {
char name[20];
};
struct node {
struct fruit * data;
struct node * next;
};
struct list {
struct node * header;
unsigned count;
};
#endif
My .c file:
#include "test.h"
#include <stdio.h>
#include <string.h>
void init_list(struct list my_list)
{
my_list.header = NULL;
my_list.count = 0;
}
void add_to_list(struct list my_list, struct fruit my_fruit)
{
struct node my_node;
struct node nav_node;
my_node.data = &my_fruit;
my_node.next = NULL;
if(my_list.count == 0) { /* set head node if list is empty */
my_list.header = &my_node;
my_list.count++;
} else {
nav_node = *my_list.header;
while (nav_node.next != NULL) { /* traverse list until end */
nav_node = *nav_node.next;
}
nav_node.next = &my_node;
my_list.count++;
}
}
int main()
{
struct fruit fruit_array[5];
struct list fruit_list;
int i;
strcpy(fruit_array[0].name, "Apple");
strcpy(fruit_array[1].name, "Mango");
strcpy(fruit_array[2].name, "Banana");
strcpy(fruit_array[3].name, "Pear");
strcpy(fruit_array[4].name, "Orange");
init_list(fruit_list);
for(i=0; i < 5; i++) {
add_to_list(fruit_list, fruit_array[i]);
}
return 0;
}
I'm assuming the issue stems from my list traversal in add_to_list, but I'm unsure about what I'm doing wrong.
Thanks!
You're passing structs by value into functions. This will create a copy of the struct in the function, and changes to the copy will not occur on the struct in the calling function.
You should read about pointers in your favorite c-language book.

Difference between LIST_HEAD_INIT and INIT_LIST_HEAD

I'm trying to understand the Linux kernel linked list API.
According to Linux Kernel Linked List I should initialize the list head by INIT_LIST_HEAD but here (Linux Kernel Program) it's suggested to use LIST_HEAD_INIT instead.
Here's a working code I wrote, but I'm not sure if I did it in proper way. Could someone verify that it's OK?
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
typedef struct edge_attr {
int d;
struct list_head list;
} edge_attributes_t;
typedef struct edge {
int id;
edge_attributes_t *attributes;
} edge_t;
int main () {
int i;
struct list_head *pos;
edge_attributes_t *elem;
edge_t *a = (edge_t*)malloc(sizeof(edge_t));
a->id = 12;
a->attributes = (edge_attributes_t*) malloc(sizeof(edge_attributes_t));
INIT_LIST_HEAD(&a->attributes->list);
for (i=0; i<5; ++i) {
elem = (edge_attributes_t*)malloc(sizeof(edge_attributes_t));
elem->d = i;
list_add(&elem->list, &a->attributes->list);
}
list_for_each(pos, &(a->attributes->list)) {
elem = list_entry(pos, edge_attributes_t, list);
printf("%d \n", elem->d);
}
return 0;
}
LIST_HEAD_INIT is a static initializer, INIT_LIST_HEAD is a function. They both initialise a list_head to be empty.
If you are statically declaring a list_head, you should use LIST_HEAD_INIT, eg:
static struct list_head mylist = LIST_HEAD_INIT(mylist);
You should use INIT_LIST_HEAD() for a list head that is dynamically allocated, usually part of another structure. There are many examples in the kernel source.
A quick LXR search shows:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
So INIT_LIST_HEAD gets a struct list_head * and initializes it, while LIST_HEAD_INIT returns the address of the passed pointer in a suitable fashioned for use as an initializer for a list:
struct list_head lst1;
/* .... */
INIT_LIST_HEAD(&lst1);
struct list_head lst2 = LIST_HEAD_INIT(lst2);

Resources