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!
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)