Linked List : list_is_last

  • In this program, you are going to learn

  • How to test whether a list is the last entry in list ?

  • In this example, we are going to test whether a list is the first entry in list.

  • Add the list of header files to refer the APIs used in this program.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
  • Add the modules macro which lists the information about the license, author and description.

MODULE_LICENSE("GPL");
MODULE_AUTHOR("linux_usr");
MODULE_DESCRIPTION("Linked List");
  • list_head is used to initialize the list.

static struct list_head my_list;
  • INIT_LIST_HEAD is used to initialize a list_head structure.

INIT_LIST_HEAD(&my_list);

INIT_LIST_HEAD(&new_node->list);
  • Add the module init function to execute the function once when the module is loaded to the linux kernel.

                static int __init linkedlist_init(void)
                {
                        struct list_node *entry;

                        pr_info("Driver loaded\n");

                        INIT_LIST_HEAD(&my_list);

                        insert_func(2);

                        display();

last_entry = list_last_entry(&my_list, struct list_node, list);
pr_info("The last entry has value : %d\n", last_entry->data);

if (list_is_last(&last_entry->list, &my_list))
    pr_info("The first entry is the last entry!\n");

                        return 0;
                }
  • list_last_entry will give the last entry of the list.

last_entry = list_last_entry(&my_list, struct list_node, list);
  • list_is_last tests whether a list is the last entry in list.

list_is_last(&last_entry->list, &my_list);
  • Add module exit function which is executed once the module is unloaded from the kernel.

static void __exit linkedlist_exit(void)
{
    struct list_node *ptr, *next;

    list_for_each_entry_safe(ptr, next, &my_list, list) {
        list_del(&ptr->list);
        kfree(ptr);
    }

    pr_info("Driver unloaded\n");
}
  • insert_rear function inserts a new node at the end with the given value into the linked list.

void insert_rear(int value)
{
    struct list_node * new_node;

    new_node = kmalloc(sizeof(struct list_node), GFP_KERNEL);

    if (!new_node) {
        pr_err("Memory allocation failed\n");

        return;
    }

    new_node->data = value;
    INIT_LIST_HEAD(&new_node->list);
    list_add_tail(&new_node->list, &my_list);
}
  • display function iterates through the linked list using list_for_each_entry. It prints the data in each node to the kernel log.

void display(void)
{
    struct list_node * ptr;

    pr_info("Linked list: ");
    list_for_each_entry(ptr, &my_list, list) {
            printk(KERN_CONT "%d -> ", ptr->data);
    }

    printk(KERN_CONT "NULL\n");
}
  • Add the module init and exit which is called when the module is loaded and unloaded.

module_init(linkedlist_init);
module_exit(linkedlist_exit);
 1#include <linux/init.h>
 2#include <linux/module.h>
 3#include <linux/kernel.h>
 4#include <linux/list.h>
 5#include <linux/slab.h>
 6
 7MODULE_LICENSE("GPL");
 8MODULE_AUTHOR("linux_usr");
 9MODULE_DESCRIPTION("Linked List");
10
11struct list_node {
12	int data;
13	struct list_head list;
14};
15
16static struct list_head my_list;
17
18void insert_func(int value)
19{
20	struct list_node *new_node;
21
22	new_node = kmalloc(sizeof(struct list_node), GFP_KERNEL);
23	
24	if (!new_node) {
25		pr_err("Memory allocation failed\n");
26		return;
27	}
28
29	new_node->data = value;
30	INIT_LIST_HEAD(&new_node->list);
31	list_add_tail(&new_node->list, &my_list);
32}
33
34void display(void)
35{
36	struct list_node *ptr;
37
38	pr_info("Linked list: ");
39	list_for_each_entry(ptr, &my_list, list) {
40		printk(KERN_CONT "%d -> ", ptr->data);
41	}
42
43	printk(KERN_CONT "NULL\n");
44}
45
46
47static int __init linkedlist_init(void)
48{
49	struct list_node *last_entry;
50
51	pr_info("Driver loaded\n");
52
53	INIT_LIST_HEAD(&my_list);
54
55	insert_func(2);
56	
57	display();
58
59	last_entry = list_last_entry(&my_list, struct list_node, list);
60	pr_info("The last entry has value : %d\n", last_entry->data);
61
62	if (list_is_last(&last_entry->list, &my_list)) 
63		pr_info("The first entry is the last entry!\n");
64
65	return 0;
66}
67
68static void __exit linkedlist_exit(void)
69{
70	struct list_node *ptr, *next;
71
72	list_for_each_entry_safe(ptr, next, &my_list, list) {
73		list_del(&ptr->list);
74		kfree(ptr);
75	}
76
77	pr_info("Driver unloaded\n");
78}
79
80module_init(linkedlist_init);
81module_exit(linkedlist_exit);
1obj-m += linked_list.o
2all:
3	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
4clean:
5	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • Run Make to compile the module.

$make
make -C /lib/modules/5.4.0-150-generic/build M=$HOME/kernel_linked_list modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-150-generic'
  CC [M]  $HOME/kernel_linked_list/linked_list.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC [M]  $HOME/kernel_linked_list/linked_list.mod.o
  LD [M]  $HOME/kernel_linked_list/linked_list.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-150-generic'
  • Run ls to check if linked_list.ko is generated or not.

$ ls -l
total 36
-rw-rw-r-- 1 test test  154 Feb 26 13:18 Makefile
-rw-rw-r-- 1 test test   47 Feb 26 13:18 modules.order
-rw-rw-r-- 1 test test    0 Feb 26 13:18 Module.symvers
-rw-rw-r-- 1 test test  820 Feb 26 13:18 linked_list.c
-rw-rw-r-- 1 test test 5880 Feb 26 13:18 linked_list.ko
-rw-rw-r-- 1 test test   47 Feb 26 13:18 linked_list.mod
-rw-rw-r-- 1 test test  919 Feb 26 13:18 linked_list.mod.c
-rw-rw-r-- 1 test test 3448 Feb 26 13:18 linked_list.mod.o
-rw-rw-r-- 1 test test 3320 Feb 26 13:18 linked_list.o
  • Run insmod to load the module.

$ sudo insmod ./linked_list.ko
  • Check the kernel messages to see if the kernel module is loaded or not.

$ dmesg
[ 6502.274246] Driver loaded
[ 6502.274247] Linked list: 2 -> NULL
[ 6502.274249] The last entry has value : 2
[ 6502.274249] The first entry is the last entry!
  • Run rmmod to unload the module.

$ sudo rmmod linked_list
  • Check dmesg to see if the module is unloaded from kernel.

$ dmesg
[ 6502.274246] Driver loaded
[ 6502.274247] Linked list: 2 -> NULL
[ 6502.274249] The last entry has value : 2
[ 6502.274249] The first entry is the last entry!
[ 6506.537914] Driver unloaded

API

Learning

INIT_LIST_HEAD

To initialize a list_head structure

LIST_HEAD

To initialize the list

list_for_each_entry

To iterate over list of given type

list_add

To insert a new entry after the specified head

list_add_tail

To insert a new entry before the specified head

list_last_entry

To retrieve the last list entry

list_is_last

To test whether a list is the last entry in list

list_for_each_entry_safe

To iterate over list of given type safe against removal of list entry

list_del

To delete entry from list