Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have a Linux Kernel Module that checks for the presence of a specific USB device and performs a printk upon a match. This code works fine and performs as i expect.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/list.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL");
static int HARD_VEND;
static int HARD_PROD;
static char *HARD_SERI;
struct USBCred
{
int vendor;
int product;
char serial[128];
};
static struct USBCred create_creds_device(struct usb_device *dev)
{
struct USBCred creds;
creds.vendor = dev->descriptor.idVendor;
creds.product = dev->descriptor.idProduct;
if((dev->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, dev->serial);
}
return creds;
}
static struct USBCred create_creds_hub(struct usb_bus *bus)
{
struct USBCred creds;
creds.vendor = bus->root_hub->descriptor.idVendor;
creds.product = bus->root_hub->descriptor.idProduct;
if((bus->root_hub->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, bus->root_hub->serial);
}
return creds;
}
static int check_usb_creds(struct USBCred usb_data)
{
if(usb_data.vendor != HARD_VEND && usb_data.product != HARD_PROD && strcmp(usb_data.serial, HARD_SERI))
{
return 1;
} else
{
printk(KERN_INFO "*********** RootPug Module - Match ***********");
printk(KERN_INFO "Vendor ID = %x, HC Vendor ID = %x", usb_data.vendor, HARD_VEND);
printk(KERN_INFO "Product ID = %x, HC Product ID = %x", usb_data.product, HARD_PROD);
printk(KERN_INFO "Serial = %s, HC Serial= %s", usb_data.serial, HARD_SERI);
return 0;
}
}
static int __init usb_fun_init (void)
{
int id;
int chix;
struct USBCred cred;
struct usb_bus *bus;
struct usb_device *dev, *childdev = NULL;
HARD_VEND = 0x26bd;
HARD_PROD = 0x9917;
HARD_SERI = "070172966462EB10";
mutex_lock(&usb_bus_idr_lock);
idr_for_each_entry(&usb_bus_idr, bus, id)
{
cred = create_creds_hub(bus);
//print_USBCred(cred);
check_usb_creds(cred);
dev = bus->root_hub;
usb_hub_for_each_child(dev, chix, childdev)
{
if(childdev)
{
usb_lock_device(childdev);
cred = create_creds_device(childdev);
//print_USBCred(cred);
check_usb_creds(cred);
usb_unlock_device(childdev);
}
}
}
mutex_unlock(&usb_bus_idr_lock);
return 0;
}
static void __exit usb_fun_exit (void)
{
printk(KERN_INFO "***************** RootPug Module - Exit *****************\n");
}
module_init(usb_fun_init);
module_exit(usb_fun_exit);
When moving over to an LSM, the LSM registers without an issue, and the hook registers correctly, yet the code is not behaving as I would like, and I can not understand why. I have added debugging statements to understand at which points the code is getting to, but it seems the idr_for_each_entry() is not being executed, as the debug statment ithin that loop is not being printed. I have used a flag based system to provide a mechanism for having -EPERM as the default return value, being overwritten if the usb is found.
I cannot understand why only the mutex is being performed, with no check to the hubs or usb devices, yet the same code works as an LKM. The actual code that is in question is within the appcl_inode_create hook. I have left out the other hooks for brevity, but they currently do nothing except return the expected value of 0.
#include <linux/init.h>
#include <linux/kd.h>
#include <linux/kernel.h>
#include <linux/tracehook.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/lsm_hooks.h>
#include <linux/xattr.h>
#include <linux/security.h>
#include <linux/capability.h>
#include <linux/unistd.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/proc_fs.h>
#include <linux/magic.h>
#include <linux/ctype.h>
#include <linux/swap.h>
#include <linux/spinlock.h>
#include <linux/syscalls.h>
#include <linux/dcache.h>
#include <linux/file.h>
#include <linux/fdtable.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/tty.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/parser.h>
#include <linux/nfs_mount.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/posix-timers.h>
#include <linux/syslog.h>
#include <linux/user_namespace.h>
#include <linux/export.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/gfp.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/list.h>
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/fsnotify.h>
#include <linux/path.h>
#include <linux/fdtable.h>
#include <linux/binfmts.h>
#include <linux/time.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include "include/appcl_lsm.h"
//#include "include/audit.h"
MODULE_LICENSE("GPLv3");
MODULE_AUTHOR("Jack Cuthbertson");
static int HARD_VEND;
static int HARD_PROD;
static char *HARD_SERI;
struct USBCred
{
int vendor;
int product;
char serial[128];
};
static struct USBCred create_creds_device(struct usb_device *dev)
{
struct USBCred creds;
creds.vendor = dev->descriptor.idVendor;
creds.product = dev->descriptor.idProduct;
if((dev->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, dev->serial);
}
return creds;
}
static struct USBCred create_creds_hub(struct usb_bus *bus)
{
struct USBCred creds;
creds.vendor = bus->root_hub->descriptor.idVendor;
creds.product = bus->root_hub->descriptor.idProduct;
if((bus->root_hub->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, bus->root_hub->serial);
}
return creds;
}
static int check_usb_creds(struct USBCred usb_data)
{
if(usb_data.vendor != HARD_VEND && usb_data.product != HARD_PROD && strcmp(usb_data.serial, HARD_SERI))
{
printk(KERN_INFO "*********** RootPlug Module - Non Match ***********");
printk(KERN_INFO "Vendor ID = %x, HC Vendor ID = %x", usb_data.vendor, HARD_VEND);
printk(KERN_INFO "Product ID = %x, HC Product ID = %x", usb_data.product, HARD_PROD);
printk(KERN_INFO "Serial = %s, HC Serial= %s", usb_data.serial, HARD_SERI);
return 1;
} else
{
printk(KERN_INFO "*********** RootPlug Module - Match ***********");
printk(KERN_INFO "Vendor ID = %x, HC Vendor ID = %x", usb_data.vendor, HARD_VEND);
printk(KERN_INFO "Product ID = %x, HC Product ID = %x", usb_data.product, HARD_PROD);
printk(KERN_INFO "Serial = %s, HC Serial= %s", usb_data.serial, HARD_SERI);
return 0;
}
}
static int appcl_lsm_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
{
int id;
int chix;
int flag = -EPERM;
struct USBCred cred;
struct usb_bus *bus;
struct usb_device *dev, *childdev = NULL;
HARD_VEND = 0x26bd;
HARD_PROD = 0x9917;
HARD_SERI = "070172966462EB10";
printk(KERN_ALERT "Rootplug: Beginning check\n");
mutex_lock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Mutex locked the bus list\n");
//loop though all usb buses
idr_for_each_entry(&usb_bus_idr, bus, id)
{
printk(KERN_ALERT "Rootplug: Checking hubs.\n");
cred = create_creds_hub(bus);
//Check creds of usb buses
if(check_usb_creds(cred) == 0)
{
mutex_unlock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Unlock hub success!\n");
flag = 0;
} else {
printk(KERN_ALERT "Rootplug: Unlock hub fail!\n");
}
dev = bus->root_hub;
usb_hub_for_each_child(dev, chix, childdev)
{
if(childdev)
{
printk(KERN_ALERT "Rootplug: Checking USB devices\n");
usb_lock_device(childdev);
cred = create_creds_device(childdev);
if(check_usb_creds(cred) == 0)
{
usb_unlock_device(childdev);
mutex_unlock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Unlock USB success!\n");
} else {
usb_unlock_device(childdev);
printk(KERN_ALERT "Rootplug: Unlock USB Failure!\n");
}
} else {
printk(KERN_ALERT "Rootplug: No child dev\n");
}
}
}
mutex_unlock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Unlock failed - Flag: %i\n", flag);
return flag;
}
static struct security_hook_list appcl_hooks[] = {
/*
* XATTR HOOKS
*/
LSM_HOOK_INIT(inode_setxattr, appcl_lsm_inode_setxattr),
LSM_HOOK_INIT(inode_post_setxattr, appcl_lsm_inode_post_setxattr),
LSM_HOOK_INIT(inode_getxattr, appcl_lsm_inode_getxattr),
LSM_HOOK_INIT(inode_removexattr, appcl_lsm_inode_removexattr),
LSM_HOOK_INIT(d_instantiate, appcl_lsm_d_instantiate),
LSM_HOOK_INIT(inode_setsecurity, appcl_lsm_inode_setsecurity),
LSM_HOOK_INIT(inode_init_security, appcl_lsm_inode_init_security),
/*
* INODE HOOKS
*/
LSM_HOOK_INIT(inode_alloc_security, appcl_lsm_inode_alloc_security),
LSM_HOOK_INIT(inode_free_security, appcl_lsm_inode_free_security),
/*
* General permission mask
*/
LSM_HOOK_INIT(inode_permission, appcl_lsm_inode_permission),
/*
* Specific permission hooks
*/
LSM_HOOK_INIT(inode_create, appcl_lsm_inode_create),
LSM_HOOK_INIT(inode_rename, appcl_lsm_inode_rename),
LSM_HOOK_INIT(inode_link, appcl_lsm_inode_link),
LSM_HOOK_INIT(inode_unlink, appcl_lsm_inode_unlink),
LSM_HOOK_INIT(inode_symlink, appcl_lsm_inode_symlink),
LSM_HOOK_INIT(inode_mkdir, appcl_lsm_inode_mkdir),
LSM_HOOK_INIT(inode_rmdir, appcl_lsm_inode_rmdir),
LSM_HOOK_INIT(inode_mknod, appcl_lsm_inode_mknod),
LSM_HOOK_INIT(inode_readlink, appcl_lsm_inode_readlink),
LSM_HOOK_INIT(inode_follow_link, appcl_lsm_inode_follow_link),
//LSM_HOOK_INIT(inode_setattr, appcl_lsm_inode_setattr),
//LSM_HOOK_INIT(inode_getattr, appcl_lsm_inode_getattr),
/*
* FILE HOOKS
*/
LSM_HOOK_INIT(file_alloc_security, appcl_lsm_file_alloc_security),
LSM_HOOK_INIT(file_free_security, appcl_lsm_file_free_security),
LSM_HOOK_INIT(file_permission, appcl_lsm_file_permission),
//LSM_HOOK_INIT(file_fcntl, appcl_lsm_file_fcntl),
LSM_HOOK_INIT(file_open, appcl_lsm_file_open),
LSM_HOOK_INIT(file_receive, appcl_lsm_file_receive),
/*
* CRED HOOKS
*/
LSM_HOOK_INIT(cred_alloc_blank, appcl_lsm_cred_alloc_blank),
LSM_HOOK_INIT(cred_free, appcl_lsm_cred_free),
LSM_HOOK_INIT(cred_prepare, appcl_lsm_cred_prepare),
LSM_HOOK_INIT(cred_transfer, appcl_lsm_cred_transfer),
LSM_HOOK_INIT(bprm_set_creds, appcl_lsm_bprm_set_creds),
/*
* SUPERBLOCK HOOKS
*/
LSM_HOOK_INIT(sb_alloc_security, appcl_lsm_sb_alloc_security),
LSM_HOOK_INIT(sb_free_security, appcl_lsm_sb_free_security),
};
static __init int appcl_init(void)
{
printk(KERN_ALERT "Rootplug: Module loading... \n");
/*
* Set security attributes for initial task
*/
security_add_hooks(appcl_hooks, ARRAY_SIZE(appcl_hooks), "appcl");
printk(KERN_ALERT "Rootplug: module initialised\n");
return 0;
}
DEFINE_LSM(appcl) = {
.name = "appcl",
.init = appcl_init,
};
The function that handled the matching used a different alert level for the printk function. As such, the messages were not displayed to the console at that stage in the boot process.
What was:
printk(KERN_INFO "message");
Should be:
printk(KERN_ALERT "message");
The matching function was being called, after recompiling the kernel with the hooks forced to return 0, i could see from /var/log/* that the function was being executed and outputting the correct information.
Kernel module code:
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/workqueue.h>
MODULE_LICENSE("GPL");
static struct workqueue_struct *queue;
static void work_func(struct work_struct *work)
{
int i = 0;
while (i < 5) {
printk(KERN_INFO "%d\n", i);
usleep_range(1000000, 1000001);
i++;
}
}
DECLARE_WORK(work, work_func);
int init_module(void)
{
queue = create_workqueue("myworkqueue");
queue_work(queue, &work);
return 0;
}
void cleanup_module(void)
{
cancel_work_sync(&work);
destroy_workqueue(queue);
}
If I do:
insmod mymod.ko
rmmod mymod
rmmod hangs on cancel_work_sync, which first waits for the work to finish, until the counting is over.
Is it possible to immediately cancel that work item?
Minimal runnable example here.
Tested in Linux kernel 4.9.
There is another way to stop kthread with signals. This approach is better than yours because it doesn't require your thread to wake up regularly and poll the stop variable with kthread_should_stop(). No wasting CPU time, it allows your thread to sleep as long as it neeeded.
static int kthread_handle(void *param)
{
allow_signal(SIGINT);
allow_signal(SIGKILL);
for (;;)
{
// ...
// Some blocking functions such as kernel_read()/kernel_write()
// ...
if (signal_pending(current))
{
goto end;
}
// ...
// Some interruptible functions
// ...
if (mutex_lock_interruptible(...) == -EINTR)
{
goto end;
}
}
end:
while (!kthread_should_stop())
{
schedule();
}
return 0;
}
static int __init drv_init(void)
{
// Create and start kernel thread
kthread = kthread_run(kthread_handle, NULL, "kthread");
return 0;
}
static void __exit drv_exit(void)
{
send_sig(SIGKILL, kthread, 1);
kthread_stop(kthread);
}
module_init(drv_init);
module_exit(drv_exit);
I don't know how to send signals to work queues, so the solution is only for kthreads by now.
Atomic control variable
I could not find a way to stop work in a workqueue, but using a simple control variable is a possible solution.
#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h> /* atomic_t */
#include <linux/workqueue.h>
MODULE_LICENSE("GPL");
static struct workqueue_struct *queue;
static atomic_t run = ATOMIC_INIT(1);
static void work_func(struct work_struct *work)
{
int i = 0;
while (atomic_read(&run)) {
printk(KERN_INFO "%d\n", i);
usleep_range(1000000, 1000001);
i++;
if (i == 10)
i = 0;
}
}
DECLARE_WORK(work, work_func);
int init_module(void)
{
queue = create_workqueue("myworkqueue");
queue_work(queue, &work);
return 0;
}
void cleanup_module(void)
{
atomic_set(&run, 0);
destroy_workqueue(queue);
}
kthread kthread_stop
Work queues are based on kthreads, and a work queue is basically useless in that example, so we could use the kthreads directly.
kthread_stop waits for the thread to return.
See also:
Proper way of handling threads in kernel?
How to wait for a linux kernel thread (kthread)to exit?
Signal handling in kthreads seems to have been a polemic subject, and is now not possible: https://unix.stackexchange.com/questions/355280/how-signals-are-handled-in-kernel
#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static struct task_struct *kthread;
static int work_func(void *data)
{
int i = 0;
while (!kthread_should_stop()) {
printk(KERN_INFO "%d\n", i);
usleep_range(1000000, 1000001);
i++;
if (i == 10)
i = 0;
}
return 0;
}
int init_module(void)
{
kthread = kthread_create(work_func, NULL, "mykthread");
wake_up_process(kthread);
return 0;
}
void cleanup_module(void)
{
kthread_stop(kthread);
}
Timer
Run in interrupt context directly, so more accurate, but more restricted.
See also: How to use timers in Linux kernel device drivers?
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>
MODULE_LICENSE("GPL");
static void callback(unsigned long data);
static unsigned long onesec;
DEFINE_TIMER(mytimer, callback, 0, 0);
static void callback(unsigned long data)
{
pr_info("%u\n", (unsigned)jiffies);
mod_timer(&mytimer, jiffies + onesec);
}
int init_module(void)
{
onesec = msecs_to_jiffies(1000);
mod_timer(&mytimer, jiffies + onesec);
return 0;
}
void cleanup_module(void)
{
del_timer(&mytimer);
}
I wrote this linux char driver just to control open call,
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/device.h>
#include <linux/cdev.h>
MODULE_LICENSE("GPL");
#define CLASS_NAME "myclass"
#define MINOR_NUM 0
#define MINOR_CNT 1
static struct class *myclass=NULL;
static struct device *mydevice=NULL;
static dev_t mycdevt;
static struct cdev *mycdev;
static struct semaphore *sem;
static int myopen(struct inode *inod, struct file *fp)
{
down(sem);
printk(KERN_INFO "critical section\n");
return 0;
}
static int myclose(struct inode *inod, struct file *fp )
{
up(sem);
printk(KERN_INFO "critical section freed\n");
return 0;
}
static ssize_t myread(struct file *fp, char *buf, size_t len, loff_t *off)
{
return 0;
}
static ssize_t mywrite(struct file *fp, char *buf, size_t len, loff_t *off)
{
return 0;
}
static struct file_operations fops =
{
.open = myopen,
.release = myclose,
.read = myread,
.write = mywrite,
};
static int __init myinit(void)
{
int ret;
ret = alloc_chrdev_region ( &mycdevt, MINOR_NUM, MINOR_CNT, "mycdevt");
if(ret<0)
{
printk(KERN_INFO "chardev can't be allocated\n");
// goto label;//todo
}
mycdev = cdev_alloc();//instead of cdev_alloc, we can use cdev_init(&mycdev, &fops);
if(mycdev == NULL)
{
printk(KERN_INFO"cdev_alloc failed\n");
// goto label;//todo
}
mycdev->ops = &fops;
ret = cdev_add(mycdev, mycdevt, 1);
if(ret < 0)
{
printk(KERN_INFO"cdev_add failed\n");
// goto label;//todo
}
myclass = class_create(THIS_MODULE, CLASS_NAME);
if(myclass == NULL)
{
printk(KERN_INFO"class create failed\n");
// goto label;//todo
}
mydevice = device_create(myclass, NULL, mycdevt, NULL, "mydevice");
if(mydevice == NULL)
{
printk(KERN_INFO"device create failed\n");
// goto label;//todo
}
sema_init(sem, 1);//here is the problem
printk(KERN_INFO"myinit done\n");
return 0;
}
static void __exit myexit(void)
{
device_destroy(myclass, mycdevt);
class_unregister(myclass);
class_destroy(myclass);
cdev_del(mycdev);
unregister_chrdev(MAJOR(mycdevt), "mycdevt");
printk(KERN_INFO "exited\n");
}
module_init(myinit);
module_exit(myexit);
I am following ldd3 book, and tried to use simple APIs to use semahore for an app.
What happened is my kernel crashed when it called sema_init function. I read that semaphore is used in mutex mode
http://www.makelinux.net/ldd3/chp-5-sect-3
I also read that semaphore is not a mutex as there is no ownership. I have yet to explore that 'ownership' thing, but for now I am not able to make a simple semaphore.
What wrong I am doing here?
There's no actual semaphore.
This line:
static struct semaphore *sem;
creates a pointer to a semaphore, not a semaphore itself. It's almost certain initialized to NULL.
You probably want something like
static struct semaphore sem;
and therefore
sema_init( &sem, 1);
along with
up( &sem );
and
down( &sem );
Note that these calls are taking the address of an actual semaphore.
I am writing a small kernel module for measuring the time that a network packet takes to exit a node.
This module is a hook in the netfilter library.
For each packet it receives it calculates an hash, gets the tstamp from skbuff and the actual timestamp, and saves all this data in a linked list.
To pass this data to userspace I've created a proc device, and when the user reads from the device I send one of the entries of the linked list.
To make changes to the list (read and write) I have a spinlock. The problem is that sometimes when I read from the proc device while I am processing packets the system crash.
I think that the problem is in the function "dump_data_to_proc", more specifically when try to acquire the spinlock. I've made some tests and it only crashes(softlockup) when running in a tplink router. When I run the module in a "normal" pc(single core) it don't crash,
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/spinlock.h>
#include <net/ipv6.h>
#include <linux/proc_fs.h> /* Necessary because of proc fs */
#include <asm/uaccess.h> /* for copy_from_user */
#include "kmodule_measure_process_time.h"
#include "hash.c"
//DEBUG >=5 is very slow in the tplink
#define DEBUG 2
#define PROCFS_MAX_SIZE 64
#define PROCFS_NAME "measures"
#define MAXIMUM_SAMPLES 10000
static struct nf_hook_ops nfho;
unsigned int total_packets_processed= 0;
unsigned int total_packets_discarded=0;
int temp_counter=0;
struct values_list *HEAD;
spinlock_t list_lock ;
static int hello_proc(struct seq_file *m, void *v) {
seq_printf(m, " stats Mod initialized.\n");
return 0;
}
static int proc_open(struct inode *inode, struct file *file) {
return single_open(file, hello_proc, NULL);
}
ssize_t dump_data_to_proc(struct file *filp, char *buffer, size_t length, loff_t *offset){
int bytesRead = 0;
struct values_list *temp=NULL;
int bytesError=0;
char buff[PROCFS_MAX_SIZE];
spin_lock(&list_lock);
temp=HEAD;
if(temp!=NULL){
HEAD = temp->next;
}
spin_unlock(&list_lock);
if(temp!=NULL){
bytesRead = snprintf(buff, PROCFS_MAX_SIZE ,"%u|%llu|%llu\n", temp->hash,temp->arrival_timestap, temp->exit_timestap);
length = length - bytesRead+1;
kfree(temp);
temp_counter--;
}
bytesError= copy_to_user(buffer, buff, bytesRead);
if(bytesError!=0){
#if DEBUG >0
printk(KERN_INFO "Error: failed to copy to user");
#endif
}
return bytesRead;
}
static const struct file_operations proc_fops = {
.owner = THIS_MODULE,
.open = proc_open,
.read = dump_data_to_proc,
.llseek = seq_lseek,
.release = single_release,
};
static unsigned int hook_func(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
uint32_t hash=0;
ktime_t now_timeval;
struct timespec now;
u64 timestamp_arrival_time=0;
u64 timestamp_now=0;
struct ipv6hdr * ipheader;
struct values_list *node;
int number_of_samples=0;
spin_lock(&list_lock);
number_of_samples=temp_counter;
spin_unlock(&list_lock);
if(number_of_samples > MAXIMUM_SAMPLES){
#if DEBUG > 5
printk(KERN_INFO "Discarded one sample because the list is full.\n");
#endif
total_packets_discarded++; // probably this should be inside a spinlock
return NF_ACCEPT;
}
//calculate arrival time and actual time in ns
timestamp_arrival_time = ktime_to_ns(skb->tstamp);
getnstimeofday(&now);
now_timeval = timespec_to_ktime(now);
timestamp_now = ktime_to_ns(now_timeval);
//get Ipv6 addresses
ipheader = (struct ipv6hdr *)skb_network_header(skb);
hash=simple_hash((char *)&ipheader->saddr,sizeof(struct in6_addr)*2,hash);
total_packets_processed++;
node = (struct values_list *) kmalloc(sizeof(struct values_list),GFP_ATOMIC);
if(!node){
#if DEBUG >0
printk(KERN_INFO "Error cannot malloc\n");
#endif
return NF_ACCEPT;
}
node->hash=hash;
node->arrival_timestap=timestamp_arrival_time;
node->exit_timestap=timestamp_now;
spin_lock(&list_lock);
node->next=HEAD;
HEAD=node;
temp_counter++;
spin_unlock(&list_lock);
return NF_ACCEPT;
}
static int __init init_main(void)
{
nfho.hook = hook_func;
nfho.hooknum = NF_INET_POST_ROUTING;
nfho.pf = PF_INET6;
nfho.priority = NF_IP_PRI_FIRST;
nf_register_hook(&nfho);
#if DEBUG >0
printk(KERN_INFO " kernel module: Successfully inserted protocol module into kernel.\n");
#endif
proc_create(PROCFS_NAME, 0, NULL, &proc_fops);
spin_lock_init(&list_lock);
//Some distros/devices disable timestamping of packets
net_enable_timestamp();
return 0;
}
static void __exit cleanup_main(void)
{
struct values_list *temp;
nf_unregister_hook(&nfho);
#if DEBUG >0
printk(KERN_INFO " kernel module: Successfully unloaded protocol module.\n");
printk(KERN_INFO "Number of packets processed:%d\n",total_packets_processed);
printk(KERN_INFO "Number of packets discarded:%d\n",total_packets_discarded);
#endif
remove_proc_entry(PROCFS_NAME, NULL);
while(HEAD!=NULL){
temp=HEAD;
HEAD= HEAD->next;
kfree(temp);
}
}
module_init(init_main);
module_exit(cleanup_main);
/* * Declaring code as GPL. */
MODULE_LICENSE("GPLv3");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
There are 2 problems with your code:
Use Linux kernel macro for your code. http://makelinux.com/ldd3/chp-11-sect-5 . Just add struct list_head as element to your struct values_list and use list_entry, list_add and other
Netfilter hools are run in softirq context, so you must use spin_lock_irqsave and spin_unlock_irqrestore. This is most likely reason why your system crashes with softlockup. Read carefully http://makelinux.com/ldd3/chp-5-sect-5
I'm newbies with the module linux.
I try to create a counter module where the counter is increment on timer callback.
The result of the counter must be send to an other module (a memory module).
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/fcntl.h>
#include <asm/system.h>
#include <asm/uaccess.h>
MODULE_AUTHOR("Helene");
MODULE_DESCRIPTION("Module memory");
MODULE_SUPPORTED_DEVICE("none");
MODULE_LICENSE("Dual BSD/GPL");
/* Global variables of the driver */
/* Buffer to store data */
char *memory_buffer;
int result;
struct file_operations memory_fops;
int memory_open(struct inode *inode, struct file *filp) {
// printk(KERN_DEBUG "Opening memory module\n");
return 0;
}
int memory_release(struct inode *inode, struct file *filp) {
// printk(KERN_DEBUG "Releasing of memory module\n");
return 0;
}
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos){
// printk(KERN_DEBUG "Reading memory module : %s\n", buf);
if (*f_pos > 0)
return 0;
if (count > strlen(memory_buffer))
count = strlen(memory_buffer);
copy_to_user(buf,memory_buffer,count);
*f_pos = *f_pos + count;
return count;
}
ssize_t memory_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos) {
// printk(KERN_DEBUG "Writing memory module : %s\n", buf);
copy_from_user(memory_buffer, buf, count);
return count;
}
static int __init memory_init(void) {
/* Registering device */
result = register_chrdev(0, "memory", &memory_fops);
if (result < 0) {
printk(KERN_DEBUG "memory: cannot obtain major number \n");
return result;
}
/* Allocating memory for the buffer */
memory_buffer = kmalloc(1, GFP_KERNEL);
if (!memory_buffer) {
result = -ENOMEM;
goto fail;
}
memset(memory_buffer, 0, 1);
printk(KERN_ALERT "Inserting memory module : %d\n", result);
return 0;
fail:
//memory_exit();
return result;
}
static void __exit memory_exit(void) {
/* Freeing the major number */
unregister_chrdev(result, "memory");
/* Freeing buffer memory */
if (memory_buffer) {
kfree(memory_buffer);
}
printk(KERN_DEBUG "Removing memory module\n");
}
struct file_operations memory_fops = {
owner: THIS_MODULE,
read: memory_read,
write: memory_write,
open: memory_open,
release: memory_release
};
module_init(memory_init);
module_exit(memory_exit);
The memory module works. My problem is when I call the function : filp_open/fp->f_op->write/filp_close on the function timer callback.
I have test these functions out of the timer callback and it's work.
Why the filp_open function (& co) don't work on timer callback function ?
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */
MODULE_LICENSE("GPL");
static struct timer_list my_timer;
int cptNbClic ;
int result;
struct file_operations timer_fops;
int write_file_system(struct file *fp, char * buf){
int nb;
mm_segment_t old_fs=get_fs();
set_fs(get_ds());
nb = fp->f_op->write(fp,buf ,10, &fp->f_pos);
set_fs(old_fs);
return nb;
}
void writeInMemory(void){
// printk(KERN_DEBUG "busy %d\n", busy);
int nbwrite;
char buf[3];
int fmemory;
fmemory=filp_open ("/dev/memory", O_WRONLY | O_APPEND | O_CREAT,0); //don't work on this function
if (fmemory==NULL){//verification de l'ouverture
printk(KERN_ALERT "filp_open error input memory!!.\n");
return -1;
}
sprintf(buf, "%d", cptNbClic);
printk(KERN_DEBUG "%d\n", cptNbClic);
nbwrite = write_file_system(fmemory, buf);
filp_close(fmemory, 0);
}
void my_timer_callback( unsigned long data )
{
cptNbClic++;
printk(KERN_DEBUG "cptNbClic %d\n", cptNbClic);
writeInMemory();
setup_timer(&my_timer, my_timer_callback, 0);
mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));
}
static int timer_open(struct inode *inode, struct file *filp) {
/* setup your timer to call my_timer_callback */
cptNbClic = 0;
setup_timer(&my_timer, my_timer_callback, 0);
/* setup timer interval to 200 msecs */
mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));
return 0;
}
static int timer_release(struct inode *inode, struct file *filp) {
/* Success */
printk(KERN_DEBUG "Releasing of cpt module\n");
del_timer(&my_timer);
return 0;
}
static int __init timer_init(void) {
/* Registering device */
result = register_chrdev(0, "timer", &timer_fops);
if (result < 0) {
printk(KERN_DEBUG "timer: cannot obtain major number \n");
return result;
}
printk(KERN_ALERT "Inserting timer module : %d\n", result);
return 0;
}
static void __exit timer_exit(void) {
unregister_chrdev(result, "timer");
printk(KERN_DEBUG "Removing timer module\n");
}
struct file_operations timer_fops = {
owner: THIS_MODULE,
open: timer_open,
release: timer_release
};
/* Declaration of the init and exit functions */
module_init(timer_init);
module_exit(timer_exit);
Sorry for my bad english
No need to call setup_timer function in your my_timer_callback().Already timer is setup. If you want a recurring timer then just again call mod_timer() in your handler which will updates your timer expire value and your timer happily runs again and again till del_timer() call.