Search code examples
cvirtual-machinekernel-moduleioctl

Read function of kernel extension module is giving Resource temporarily unavailable


I'm having trouble trying to build a kernel module. It's a character device module named message_slot. According to the instructions, it should support up to 256 minors (all have the same major- 235). Each minor represents a different device file and can have up to 2^20 channels. In order to do so I used a list of binary trees, such that each entry represents a different device file.

I have two user programs: message_writer.c and message_reader.c which were successfully compiled with -o sender and -o reader respectivly.

My problem is related to the reader fuction. After doing the whole process of make, insmod, mknod /dev/slot1 c 235 1, sudo chmod o+rw /dev/slot1 and finally ./sender /dev/slot1 1 message1 - I got the following indication that "message" was actually writen to the slot1 message slot in channel 1 (printed using printk): Write Done. curr_channel->bytes = 8

However, when issuing ./reader /dev/slot1 1 I get the following message: Resource temporarily unavailable using printk in my read function, and assigning msg_len = curr_channel->bytes (which is the same channel that was just writen in the sender function), I find that msg_len is actually 0 (expected to be 8).

Attached is message_slot.c, message_slot.h and message_reader.c:

message_slot.c

#undef __KERNEL__
#define __KERNEL__
#undef MODULE
#define MODULE


#include <linux/kernel.h>  
#include <linux/module.h>   
#include <linux/fs.h>
#include <linux/init.h>      
#include <linux/uaccess.h>  
#include <linux/string.h> 
#include <linux/slab.h> 
#include "message_slot.h"
MODULE_LICENSE("GPL");


struct channel* search_channel(struct channel*, int);
void insert_channel(struct channel*, struct channel*);
void cleanup_trees(struct channel*);

// We will hold a list containing all the minors
// Each minor is an entry in the minors list
// Actually, each entry will hold the root of a binary tree
// Each node in each binary tree is a channel
// Therefore, our minors list is a list of (root) channels
static struct channel *minors[257]; // 256 (non zero) message slots at most

// Implementation of binary tree functions: search, insert

struct channel* search_channel(struct channel* root, int x) {
    struct channel* curr = root;
    while (curr != NULL) {
        if (curr->channel_number == x) {
            return curr;  // Found the channel with the given channel_number
        } else if (x < curr->channel_number) {
            curr = curr->left;  // Go to the left subtree
        } else {
            curr = curr->right;  // Go to the right subtree
        }
    }
    return NULL;  // Channel with the given channel_number (x) not found
}

void insert_channel(struct channel* root, struct channel* node) {
    struct channel* curr;
    if (root == NULL) {
        // The tree is empty, make the node as the root
        root = node;
    } 
    else {
        curr = root;
        while (1) {
            if ((node->channel_number) < (curr->channel_number)) {
                if (curr->left == NULL) {
                    curr->left = node;  // Insert node as the left child
                    break;
                } 
                else {
                    curr = curr->left;  // Move to the left subtree
                }
            } 
            else {
                if (curr->right == NULL) {
                    curr->right = node;  // Insert node as the right child
                    break;
                } 
                else {
                    curr = curr->right;  // Move to the right subtree
                }
            }
        }
    }
}

// A clean up function, which will be called for each entry in minors when we exit the module

void cleanup_trees(struct channel* root) {
    if (root == NULL) {
        return;  // Base case: empty tree or leaf node
    }

    // Post-order traversal to delete nodes recursively
    cleanup_trees(root->left);
    cleanup_trees(root->right);

    // Delete the current node
    kfree(root);
}

// Device Functions

// device_open: assigning privat data to Null. nothing else required.
static int device_open(struct inode* inode, struct file* file) {
    file->private_data = NULL;
    printk("End of device open\n"); 
    return SUCCESS;
}


static long device_ioctl(struct file* file, unsigned int ioctl_command_id, unsigned long ioctl_param){
    struct channel *curr;
    struct channel *root;
    int minor;
    printk("in ioctl\n");
    if (ioctl_command_id != MSG_SLOT_CHANNEL || ioctl_param == 0){
        printk("Channel/ Command error\n");
        return -EINVAL;
    }
    minor = iminor(file->f_inode);
    root = minors[minor];
    curr = search_channel(root, ioctl_param);
    if(curr == NULL){
        curr = kmalloc(sizeof(struct channel), GFP_KERNEL);
        curr->channel_number = ioctl_param;
        curr->left = NULL;
        curr->right = NULL;
        curr->bytes = 0;
        insert_channel(root, curr);
    }
    file->private_data = curr;
    printk("number of bits in file->private_data->bytes = %d\n", curr->bytes);
    return SUCCESS;
}


static ssize_t device_read(struct file* file, char __user* buffer, size_t length, loff_t* offset){
    struct channel *curr_channel;
    int msg_len;
    int i;
    curr_channel = (struct channel *)(file->private_data);
    if (curr_channel == NULL){
        printk("No channel has been set\n");
        return -EINVAL;
    }
    msg_len = curr_channel->bytes;
    printk("msg_len is %d\n", msg_len);
    if (length < msg_len || buffer == NULL){
        printk("Buffer length is too short to hold the message\n");
        return -ENOSPC;
    }
    if (msg_len == 0){
        printk("No message in this channel\n");
        return -EWOULDBLOCK;
    }
    for (i=0 ; i < msg_len; i++){
        put_user(curr_channel->message[i],&buffer[i]);
    }
    return msg_len;
}


static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset){
    struct channel *curr_channel;
    char *msg;
    int i;
    curr_channel = (struct channel *)(file->private_data);
    if (curr_channel == NULL){
        printk("No channel has been set for this file\n");
        return -EINVAL;
    }
    if (length == 0 || length > MAX_BUF_LEN){
        printk("Invalid message length\n");
        return -EMSGSIZE;
    }
    if (buffer == NULL){
        printk("Null buffer\n");
        return -EINVAL;
    }
    msg = curr_channel->message;
    for (i=0; i< length; i++){
        get_user(msg[i],&buffer[i]);
    }
    curr_channel->bytes = (int)length;
    for (i=0 ; i < curr_channel->bytes ; i++){
        curr_channel->message[i] = msg[i];
    }
    printk ("Write Done. curr_channel->bytes = %d\n", curr_channel->bytes);
    return length;
}

static int device_release(struct inode* inode, struct file* file){
    return SUCCESS;
}


struct file_operations Fops =
    {
        .owner = THIS_MODULE,
        .read = device_read,
        .write = device_write,
        .open = device_open,
        .unlocked_ioctl = device_ioctl,
        .release = device_release,
};

static int __init ms_init(void){
    int rc;
    int i;
    if ((rc = register_chrdev(MAJOR_NUM,DEVICE_NAME,&Fops)) < 0){
        printk("Registration failed\n");
        return FAIL;
    }
    for (i=0 ; i < 257 ; i ++){
        minors[i] = NULL;
    }
    printk("Registration Successed\n");
    return SUCCESS;
}

static void __exit ms_cleanup(void){ // should make sure to clean each and every channel with the cleanup_trees function
    struct channel *cur_root;
    int i;
    for (i=0 ; i < 257; i++){
        cur_root = minors[i];
        if (cur_root != NULL){
            cleanup_trees(minors[i]);
        }
    }
    unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
    printk("Seccussfuly unregistered\n");
}

module_init(ms_init);
module_exit(ms_cleanup);

message_slot.h

#ifndef MESSAGE_SLOT_H
#define MESSAGE_SLOT_H

#include <linux/ioctl.h>
#define MAJOR_NUM 235
#define MSG_SLOT_CHANNEL _IOW(MAJOR_NUM, 0, unsigned int)
#define DEVICE_NAME "message_slot"
#define MAX_BUF_LEN 128
#define SUCCESS 0
#define FAIL -1

// We will define the struct channel 
// Considering the fact that we keep all channels in a binary tree
// Also required: channel number, the current message

struct channel {
    int channel_number;
    char message[128];
    int bytes;
    struct channel *right;
    struct channel *left;
};
#endif

message_reader.c

#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "message_slot.h"


int main(int argc, char** argv){
    int fd;
    char temp[MAX_BUF_LEN];
    unsigned int channel_number;
    int msg_len;
    if (argc != 3){
        perror("Wrong number of arguments");
        exit(1);
    }
    if ((fd = open(argv[1],O_RDWR )) < 0){
        perror("Could not open the specified file");
        exit(1);
    }
    channel_number = atoi(argv[2]);
    if (ioctl(fd, MSG_SLOT_CHANNEL,channel_number) < 0){
        perror("Error issuing ioctl");
        exit(1);
    }
    msg_len = read(fd,temp,MAX_BUF_LEN);
    printf("message_len is %d\n", msg_len);
    if (msg_len < 0){
        perror("Error reading the message");
        exit(1);
    }
    close(fd);
    if (write(1,temp,msg_len) < 0){
        perror("Fail to write to standrad output");
        exit(1);
    }
    exit(0);
}

And the message after running strace ./reader /dev/slot1 1 :

execve("./reader", ["./reader", "/dev/slot1", "1"], 0x7ffcf0397a20 /* 22 vars */) = 0
brk(NULL)                               = 0x564947f72000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=75005, ...}) = 0
mmap(NULL, 75005, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5640f07000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@>\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1905632, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5640f05000
mmap(NULL, 1918592, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5640d30000
mmap(0x7f5640d52000, 1417216, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f5640d52000
mmap(0x7f5640eac000, 323584, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17c000) = 0x7f5640eac000
mmap(0x7f5640efb000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ca000) = 0x7f5640efb000
mmap(0x7f5640f01000, 13952, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5640f01000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f5640f06540) = 0
mprotect(0x7f5640efb000, 16384, PROT_READ) = 0
mprotect(0x564946a5f000, 4096, PROT_READ) = 0
mprotect(0x7f5640f44000, 4096, PROT_READ) = 0
munmap(0x7f5640f07000, 75005)           = 0
openat(AT_FDCWD, "/dev/slot1", O_RDONLY) = 3
ioctl(3, _IOC(_IOC_WRITE, 0xeb, 0, 0x4), 0x1) = 0
read(3, 0x7ffc2388cc20, 128)            = -1 EAGAIN (Resource temporarily unavailable)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
brk(NULL)                               = 0x564947f72000
brk(0x564947f93000)                     = 0x564947f93000
dup(2)                                  = 4
fcntl(4, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
fstat(4, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
write(4, "Error reading the message: Resou"..., 60Error reading the message: Resource temporarily unavailable
) = 60
close(4)                                = 0
write(1, "message_len is -1", 17message_len is -1)       = 17
exit_group(1)                           = ?
+++ exited with 1 +++

Any help will be appreciated!


Solution

  • A few issues ...


    device_open does an unconditional:

    file->private_data = NULL;
    

    This [subsequently] gets set correctly when the writer is invoked.

    But, when the reader is invoked, the previous value (set by the writer) is lost (i.e. the memory is leaked) because of the unconditional setting to NULL.

    Since this is where you get the data from on a read, you have to ensure that you preserve/use the prior value.


    Also, it is possible for a read/write operation to get a legitimate EAGAIN [EWOULDBLOCK] error. In your reader/writer, you have to detect this and loop.


    UPDATE:

    In the reader program, it issues an ioctl call.

    This initializes private_data in the driver. But, the address for sender and reader are now different.

    That is, the driver read operation does not take the null pointer return (e.g. EINVAL) but the EWOULDBLOCK return because this second instance is empty.

    So, the remedy is the same [as I originally mentioned].

    The driver must preserve private_data so that both sender and reader use the same pointer value (e.g.) for the struct channel instance.


    UPDATE #2:

    After downloading, building, and running ...

    The primary issue is that in insert_channel, if root is NULL, it sets root to curr.

    But, this is [only] local to the function.

    It does not set the correct element of the global minors array.

    At present, only the caller of insert_channel knows the correct index to use with that array (e.g. minor).

    So, the simplest solution is to have caller (e.g. device_ioctl) set this.

    Below are the changed files. They are annotated with the bugs and fixes.

    Note that I had to synthesize an appropriate Makefile and a script to show the system log.

    In the code below, I've used cpp conditionals to denote old vs. new code:

    #if 0
    // old code
    #else
    // new code
    #endif
    
    #if 1
    // new code
    #endif
    

    Note: this can be cleaned up by running the file through unifdef -k


    FILE: message_slot.c

    #undef __KERNEL__
    #define __KERNEL__
    #undef MODULE
    #define MODULE
    
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/uaccess.h>
    #include <linux/string.h>
    #include <linux/slab.h>
    #include "message_slot.h"
    MODULE_LICENSE("GPL");
    
    struct channel *search_channel(struct channel *, int);
    void insert_channel(struct channel *, struct channel *);
    void cleanup_trees(struct channel *);
    
    // We will hold a list containing all the minors
    // Each minor is an entry in the minors list
    // Actually, each entry will hold the root of a binary tree
    // Each node in each binary tree is a channel
    // Therefore, our minors list is a list of (root) channels
    // 256 (non zero) message slots at most
    static struct channel *minors[257];
    
    #define printk(_fmt...) \
        printk("GURGLE " _fmt)
    
    // Implementation of binary tree functions: search, insert
    
    struct channel *
    search_channel(struct channel *root, int x)
    {
        struct channel *curr = root;
    
        printk("search_channel: ENTER root=%p x=%d\n",root,x);
    
        while (curr != NULL) {
            printk("search_channel: curr=%p channel_number=%d\n",
                curr,curr->channel_number);
    
            if (curr->channel_number == x) {
                // Found the channel with the given channel_number
    #if 0
                return curr;
    #else
                break;
    #endif
            }
            else if (x < curr->channel_number) {
                // Go to the left subtree
                curr = curr->left;
            }
            else {
                // Go to the right subtree
                curr = curr->right;
            }
        }
    
        // Channel with the given channel_number (x) not found
    #if 0
        return NULL;
    #else
        printk("search_channel: EXIT curr=%p\n",curr);
        return curr;
    #endif
    }
    
    void
    insert_channel(struct channel *root, struct channel *node)
    {
        struct channel *curr;
    
        printk("insert_channel: ENTER root=%p node=%p\n",root,node);
    
    // NOTE/BUG: caller must set this value in the global minors
        // The tree is empty, make the node as the root
        if (root == NULL) {
            root = node;
        }
        else {
            curr = root;
            while (1) {
                if (node->channel_number < curr->channel_number) {
                    // Insert node as the left child
                    if (curr->left == NULL) {
                        curr->left = node;
                        break;
                    }
    
                    // Move to the left subtree
                    curr = curr->left;
                }
    
                else {
                    // Insert node as the right child
                    if (curr->right == NULL) {
                        curr->right = node;
                        break;
                    }
    
                    // Move to the right subtree
                    curr = curr->right;
                }
            }
        }
    }
    
    // A clean up function, which will be called for each entry in minors when we
    // exit the module
    void
    cleanup_trees(struct channel *root)
    {
        if (root == NULL) {
            // Base case: empty tree or leaf node
            return;
        }
    
        // Post-order traversal to delete nodes recursively
        cleanup_trees(root->left);
        cleanup_trees(root->right);
    
        // Delete the current node
        kfree(root);
    }
    
    // Device Functions
    
    // device_open: assigning privat data to Null. nothing else required.
    static int
    device_open(struct inode *inode, struct file *file)
    {
        file->private_data = NULL;
        printk("device_open: End of device open\n");
        return SUCCESS;
    }
    
    static long
    device_ioctl(struct file *file, unsigned int ioctl_command_id,
        unsigned long ioctl_param)
    {
        struct channel *curr;
        struct channel *root;
        int minor;
    
        printk("device_ioctl: ENTER\n");
        if (ioctl_command_id != MSG_SLOT_CHANNEL || ioctl_param == 0) {
            printk("device_ioctl: Channel/ Command error\n");
            return -EINVAL;
        }
    
        minor = iminor(file->f_inode);
        root = minors[minor];
    
        curr = search_channel(root, ioctl_param);
    
        if (curr == NULL) {
            curr = kmalloc(sizeof(struct channel), GFP_KERNEL);
            curr->channel_number = ioctl_param;
            curr->left = NULL;
            curr->right = NULL;
            curr->bytes = 0;
            printk("device_ioctl: NEW curr=%p channel_number=%d\n",
                curr,curr->channel_number);
    
    #if 0
    // NOTE/BUG: setting a new root in insert_channel does _not_ set minors
            insert_channel(root, curr);
    #else
    // NOTE/FIX: we should do it here as [only] we know the array index
            if (root == NULL)
                minors[minor] = curr;
            else
                insert_channel(root, curr);
    #endif
        }
    
        file->private_data = curr;
        printk("device_ioctl: EXIT curr=%p\n",curr);
    
        return SUCCESS;
    }
    
    static ssize_t
    device_read(struct file *file, char __user *buffer, size_t length,
        loff_t *offset)
    {
        struct channel *curr_channel;
        int msg_len;
        int i;
    
        curr_channel = file->private_data;
        printk("device_read: curr_channel=%p\n",curr_channel);
    
        if (curr_channel == NULL) {
            printk("device_read: No channel has been set\n");
            return -EINVAL;
        }
    
        msg_len = curr_channel->bytes;
        printk("device_read: msg_len is %d\n", msg_len);
        if (length < msg_len || buffer == NULL) {
            printk("device_read: Buffer length is too short to hold the message\n");
            return -ENOSPC;
        }
    
        if (msg_len == 0) {
            printk("device_read: No message in this channel\n");
            return -EWOULDBLOCK;
        }
    
        for (i = 0; i < msg_len; i++) {
            put_user(curr_channel->message[i], &buffer[i]);
        }
    
        return msg_len;
    }
    
    static ssize_t
    device_write(struct file *file, const char __user *buffer, size_t length,
        loff_t *offset)
    {
        struct channel *curr_channel;
        char *msg;
        int i;
    
        curr_channel = (struct channel *) (file->private_data);
        printk("device_write: curr_channel=%p\n",curr_channel);
    
        if (curr_channel == NULL) {
            printk("device_write: No channel has been set for this file\n");
            return -EINVAL;
        }
    
        if (length == 0 || length > MAX_BUF_LEN) {
            printk("device_write: Invalid message length\n");
            return -EMSGSIZE;
        }
    
        if (buffer == NULL) {
            printk("device_write: Null buffer\n");
            return -EINVAL;
        }
    
        msg = curr_channel->message;
        printk("device_write: msg=%p\n",msg);
        for (i = 0; i < length; i++) {
            get_user(msg[i], &buffer[i]);
            printk("device_write: msg[%d]=%2.2X\n",i,msg[i]);
        }
        curr_channel->bytes = (int) length;
    
        printk("Write Done. curr_channel->bytes = %d\n", curr_channel->bytes);
        return length;
    }
    
    static int
    device_release(struct inode *inode, struct file *file)
    {
        return SUCCESS;
    }
    
    struct file_operations Fops = {
        .owner = THIS_MODULE,
        .read = device_read,
        .write = device_write,
        .open = device_open,
        .unlocked_ioctl = device_ioctl,
        .release = device_release,
    };
    
    static int __init
    ms_init(void)
    {
        int rc;
        int i;
    
        if ((rc = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops)) < 0) {
            printk("Registration failed\n");
            return FAIL;
        }
    
        for (i = 0; i < 257; i++) {
            minors[i] = NULL;
        }
    
        printk("Registration Successed\n");
        return SUCCESS;
    }
    
    // should make sure to clean each and every channel with the cleanup_trees
    // function
    static void __exit
    ms_cleanup(void)
    {
        struct channel *cur_root;
        int i;
    
        for (i = 0; i < 257; i++) {
            cur_root = minors[i];
            if (cur_root != NULL)
                cleanup_trees(cur_root);
        }
    
        unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
        printk("Successfuly unregistered\n");
    }
    
    module_init(ms_init);
    module_exit(ms_cleanup);
    

    FILE: message_reader.c

    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "message_slot.h"
    
    int
    main(int argc, char **argv)
    {
        int fd;
        char temp[MAX_BUF_LEN];
        unsigned int channel_number;
        int msg_len;
    
        if (argc != 3) {
            perror("Wrong number of arguments");
            exit(1);
        }
        if ((fd = open(argv[1], O_RDWR)) < 0) {
            perror("Could not open the specified file");
            exit(1);
        }
        channel_number = atoi(argv[2]);
        if (ioctl(fd, MSG_SLOT_CHANNEL, channel_number) < 0) {
            perror("Error issuing ioctl");
            exit(1);
        }
        msg_len = read(fd, temp, MAX_BUF_LEN);
        printf("reader: message_len is %d\n", msg_len);
        if (msg_len < 0) {
            perror("Error reading the message");
            exit(1);
        }
        close(fd);
    
    #if 0
        if (write(1, temp, msg_len) < 0) {
            perror("Fail to write to standrad output");
            exit(1);
        }
    #else
        fwrite(temp,1,msg_len,stdout);
        printf("\n");
    #endif
    
        return 0;
    }
    

    FILE: Makefile

    # devtree/Makefile -- make file for devtree
    #
    # SO: read function of kernel extension module is giving resource temporarily
    # SO: unavaila
    # SITE: stackoverflow.com
    # SO: 76408531
    
    MOD = message_slot
    
    MAJOR_NUM := 235
    DEVFILE := /dev/slot1
    
    KPATH := /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)
    obj-m = $(MOD).o
    
    ifeq ($(SUDO),"")
      SUDO := sudo
    endif
    
    ###LOGMSG := grep GURGLE /var/log/messages ; true
    LOGMSG := $(SUDO) perl ./showlog
    
    all: reader sender
        $(MAKE) -C $(KPATH) M=$(PWD) modules
    
    clean:
        $(MAKE) -C $(KPATH) M=$(PWD) clean
        rm -f reader sender
        $(SUDO) rmmod $(MOD).ko; true
        $(SUDO) rm -f $(DEVFILE)
    
    insmod: all
        sync
        $(SUDO) rmmod $(MOD).ko; true
        $(SUDO) insmod $(MOD).ko
    
    log: 
        $(LOGMSG)
    
    devices:
        $(SUDO) rm -f $(DEVFILE)
        $(SUDO) mknod $(DEVFILE) c $(MAJOR_NUM) 1
        $(SUDO) chmod o+rw $(DEVFILE)
    
    install: insmod devices
    
    test_sender: sender
        $(GDB) ./sender $(DEVFILE) 1 message1
        $(LOGMSG)
    
    test_reader: reader
        $(GDB) ./reader $(DEVFILE) 1
        $(LOGMSG)
    
    reader: message_reader.c
        gcc -o reader message_reader.c
    
    sender: message_sender.c
        gcc -o sender message_sender.c
    

    FILE: showlog

    #!/usr/bin/perl
    # showlog -- show kernel logfile
    
    master(@ARGV);
    exit(0);
    
    sub master
    {
        my(@argv) = @_;
    
        foreach $arg (@argv) {
            next unless ($arg =~ s/^-//);
            $sym = "opt_" . $arg;
            $$sym = 1;
        }
    
        ###@body = (`journalctl --lines=10000`);
        @body = (`dmesg`);
        printf("showlog: BODY %d\n",scalar(@body));
    
        foreach $buf (@body) {
            chomp($buf);
            ###printf("TRACE buf='%s'\n",$buf);
    
            next unless ($buf =~ /GURGLE/);
    
            if ($buf =~ /Registration.Success/) {
                undef(@out)
                    unless ($opt_a);
                $hit = 1;
            }
    
            push(@out,$buf)
                if ($hit);
        }
    
        foreach $buf (@out) {
            print($buf,"\n");
        }
    }
    

    Here is the full test output:

    18:50:03.673318386 NEWDAY 06/05/23
    18:50:03.673318386 ph: starting 716334 ...
    18:50:03.674014091 ph: ARGV make SUDO=shazam clean install test_sender test_reader ...
    
    make -C /lib/modules/5.3.11-100.fc29.x86_64/build M=/home/cae/tmp/devtree clean
    make[1]: Entering directory '/usr/src/kernels/5.3.11-100.fc29.x86_64'
      CLEAN   /home/cae/tmp/devtree/Module.symvers
    make[1]: Leaving directory '/usr/src/kernels/5.3.11-100.fc29.x86_64'
    rm -f reader sender
    shazam rmmod message_slot.ko; true
    shazam rm -f /dev/slot1
    gcc -o reader message_reader.c
    gcc -o sender message_sender.c
    make -C /lib/modules/5.3.11-100.fc29.x86_64/build M=/home/cae/tmp/devtree modules
    make[1]: Entering directory '/usr/src/kernels/5.3.11-100.fc29.x86_64'
      CC [M]  /home/cae/tmp/devtree/message_slot.o
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /home/cae/tmp/devtree/message_slot.mod.o
      LD [M]  /home/cae/tmp/devtree/message_slot.ko
    make[1]: Leaving directory '/usr/src/kernels/5.3.11-100.fc29.x86_64'
    sync
    shazam rmmod message_slot.ko; true
    rmmod: ERROR: Module message_slot is not currently loaded
    shazam insmod message_slot.ko
    shazam rm -f /dev/slot1
    shazam mknod /dev/slot1 c 235 1
    shazam chmod o+rw /dev/slot1
    ./sender /dev/slot1 1 message1
    shazam perl ./showlog
    showlog: BODY 1272
    [6713278.237239] GURGLE Registration Successed
    [6713278.264249] GURGLE device_open: End of device open
    [6713278.264258] GURGLE device_ioctl: ENTER
    [6713278.264260] GURGLE search_channel: ENTER root=000000009a5e2b86 x=1
    [6713278.264261] GURGLE search_channel: EXIT curr=000000009a5e2b86
    [6713278.264262] GURGLE device_ioctl: NEW curr=000000000fd62f73 channel_number=1
    [6713278.264263] GURGLE device_ioctl: EXIT curr=000000000fd62f73
    [6713278.264266] GURGLE device_write: curr_channel=000000000fd62f73
    [6713278.264266] GURGLE device_write: msg=0000000096063cbd
    [6713278.264267] GURGLE device_write: msg[0]=6D
    [6713278.264268] GURGLE device_write: msg[1]=65
    [6713278.264269] GURGLE device_write: msg[2]=73
    [6713278.264270] GURGLE device_write: msg[3]=73
    [6713278.264271] GURGLE device_write: msg[4]=61
    [6713278.264271] GURGLE device_write: msg[5]=67
    [6713278.264272] GURGLE device_write: msg[6]=65
    [6713278.264273] GURGLE device_write: msg[7]=31
    [6713278.264274] GURGLE Write Done. curr_channel->bytes = 8
    ./reader /dev/slot1 1
    reader: message_len is 8
    message1
    shazam perl ./showlog
    showlog: BODY 1278
    [6713278.237239] GURGLE Registration Successed
    [6713278.264249] GURGLE device_open: End of device open
    [6713278.264258] GURGLE device_ioctl: ENTER
    [6713278.264260] GURGLE search_channel: ENTER root=000000009a5e2b86 x=1
    [6713278.264261] GURGLE search_channel: EXIT curr=000000009a5e2b86
    [6713278.264262] GURGLE device_ioctl: NEW curr=000000000fd62f73 channel_number=1
    [6713278.264263] GURGLE device_ioctl: EXIT curr=000000000fd62f73
    [6713278.264266] GURGLE device_write: curr_channel=000000000fd62f73
    [6713278.264266] GURGLE device_write: msg=0000000096063cbd
    [6713278.264267] GURGLE device_write: msg[0]=6D
    [6713278.264268] GURGLE device_write: msg[1]=65
    [6713278.264269] GURGLE device_write: msg[2]=73
    [6713278.264270] GURGLE device_write: msg[3]=73
    [6713278.264271] GURGLE device_write: msg[4]=61
    [6713278.264271] GURGLE device_write: msg[5]=67
    [6713278.264272] GURGLE device_write: msg[6]=65
    [6713278.264273] GURGLE device_write: msg[7]=31
    [6713278.264274] GURGLE Write Done. curr_channel->bytes = 8
    [6713278.298388] GURGLE device_open: End of device open
    [6713278.298399] GURGLE device_ioctl: ENTER
    [6713278.298402] GURGLE search_channel: ENTER root=000000000fd62f73 x=1
    [6713278.298403] GURGLE search_channel: curr=000000000fd62f73 channel_number=1
    [6713278.298404] GURGLE search_channel: EXIT curr=000000000fd62f73
    [6713278.298405] GURGLE device_ioctl: EXIT curr=000000000fd62f73
    [6713278.298408] GURGLE device_read: curr_channel=000000000fd62f73
    [6713278.298410] GURGLE device_read: msg_len is 8
    
    18:50:07.223792314 ph: complete (ELAPSED: 00:00:03.548927068)