I was trying some experiments with the kernel daemon. The experiment works
as follows:
A daemon sleeps in the background. User can enter a string through the proc
interface. Whenever a string is entered, the daemon is woke. The daemon
keeps a copy of the last entered string in a variable. Initially the
variable is initialized to NULL. When the daemon wakes, it checks if the
string entered is same as the previous one or a new string is entered. When
the string is entered, in case the new or the old strings are NULL, or in
case the entered string is same as the old string the daemon goes back to
sleep (with the help of the function interruptible_sleep_on().
The problem I am facing is that when I enter the string the second time, the
system stalls. I added some sleeps in the code and figured out that when the
proc function wakes up the daemon, the system stalls.
I have attached the module code with the mail. Any suggestion for the error
would be a great help.
Shubham
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
typedef struct {
char *old_file_name;
int old_file_name_length;
char *new_file_name;
int new_file_name_length;
pid_t thread_pid;
int thread_present;
wait_queue_head_t thread_sv; /*to make the thread sleep on certain condition */
// wait_queue_head_t multi_thread_sv[1]; /* in case of multi threading, second thread will sleep until first is awake */
/* but will this be possible? a module can be loaded only once!!! */
} thread_struct;
#define prc_file_name "test_proc_file_name"
thread_struct *ts = NULL;
char *file_name = NULL;
int file_name_size = 0;
int my_thread_fun (void *arg) {
int ret;
thread_struct *my_thread = (thread_struct *) arg;
if (!try_module_get(THIS_MODULE)) {
printk(KERN_INFO "%s:\tunable to get the module\n",__FUNCTION__);
ret = -EINVAL;
goto out;
}
daemonize("test_thread");
allow_signal(SIGKILL);
my_thread->thread_present = 1;
printk(KERN_INFO "%s:\tthread id = %d name of the old file= %s, name of the new file = %s\n",
__FUNCTION__, my_thread->thread_pid, my_thread->new_file_name, my_thread->old_file_name);
for (;;) {
if (signal_pending(current)) {
my_thread->thread_pid = 0;
printk(KERN_INFO "%s:\tgot a signal to commit suicide \n",__FUNCTION__);
ret = -EINTR;
goto out;
}
if (my_thread->new_file_name == NULL || my_thread->old_file_name == NULL) {
printk(KERN_INFO "%s:\ttime to sleep as nothing to compare\n",__FUNCTION__);
interruptible_sleep_on(&my_thread->thread_sv);
}
printk(KERN_INFO "%s:\tthread id = %d name of the old file= %s, name of the new file = %s\n",
__FUNCTION__, my_thread->thread_pid, my_thread->new_file_name, my_thread->old_file_name);
/* adding this continue because the thread may
* sleep on the above condition and when it wakes
* it needs to check again if the old and new file
* name are NULL or not
*/
continue;
if (my_thread->new_file_name != NULL) {
printk(KERN_INFO "%s:\tname of the new file is %s\n",__FUNCTION__, my_thread->new_file_name);
}
if (my_thread->old_file_name != NULL) {
printk(KERN_INFO "%s:\tname of the old file is %s\n",__FUNCTION__, my_thread->old_file_name);
}
printk(KERN_INFO "%s:\told file(%s) new file(%s) going for comparision\n",
__FUNCTION__, my_thread->old_file_name, my_thread->new_file_name);
ssleep(2);
// schedule_timeout(HZ);
if (!memcmp(my_thread->old_file_name, my_thread->new_file_name,my_thread->new_file_name_length)) {
// (my_thread->old_file_name_length > my_thread->new_file_name_length ?
// my_thread->old_file_name_length : my_thread->new_file_name_length))) {
ssleep(2);
printk(KERN_INFO "%s:\tname of the new file (%s) is same as old file (%s) sleeping\n",
__FUNCTION__, my_thread->new_file_name, my_thread->old_file_name);
interruptible_sleep_on(&my_thread->thread_sv);
}
ssleep(2);
printk(KERN_INFO "%s:\tnew file(%s)\n",__FUNCTION__, my_thread->new_file_name);
// schedule_timeout(HZ);
my_thread->old_file_name = my_thread->new_file_name;
my_thread->old_file_name_length = my_thread->new_file_name_length;
ssleep(2);
printk(KERN_INFO "%s:\trounding off\n",__FUNCTION__);
}
out:
my_thread->thread_present = 0;
module_put(THIS_MODULE);
return ret;
}
int read_proc_file(
char *buf,
char **buf_loc,
off_t offset,
int buf_len,
int *eof,
void *data)
{
int ret;
printk(KERN_INFO "%s reading from the proc file %s\n", __FUNCTION__,prc_file_name);
if (offset > 0) {
return 0;
} else {
memcpy(buf, file_name, file_name_size);
ret = file_name_size;
}
return ret;
}
int write_proc_file (
struct file *filp,
const char __user *buf,
unsigned long len,
void *data) {
printk(KERN_INFO "%s:\tlength of file name = %ld\n",__FUNCTION__, len);
printk(KERN_INFO "%s:\twriting into the proc file\n", __FUNCTION__);
file_name_size = len;
file_name = (char *) kmalloc(len, GFP_KERNEL);
if (copy_from_user(file_name, buf, file_name_size)) {
return -1;
}
/* calling the function to print the page information for this file */
printk(KERN_INFO "%s:\tname of the file is: %s\n", __FUNCTION__, file_name);
if (ts && ts->thread_present) {
// schedule_timeout(HZ);
ssleep(2);
printk(KERN_INFO "%s:\ta thread exists and is sleeping. time to wake it and make him do some work\n",
__FUNCTION__);
if (ts->old_file_name != NULL) {
printk(KERN_INFO "old entry exists free the memory it occupies\n");
kfree(ts->old_file_name);
}
printk(KERN_INFO "%s:\tadding the new entries to the daemon structure\n",__FUNCTION__);
// schedule_timeout(HZ);
ssleep(2);
ts->old_file_name = ts->new_file_name;
ts->old_file_name_length = ts->new_file_name_length;
ts->new_file_name = file_name;
ts->new_file_name_length = file_name_size;
printk(KERN_INFO "%s:\twakkkkkkeeee\n",__FUNCTION__);
// schedule_timeout(HZ);
ssleep(2);
wake_up(&ts->thread_sv);
}
return file_name_size;
}
int __init
init_thread_module(void) {
struct proc_dir_entry *my_entry;
ts = (thread_struct *) kmalloc(sizeof(thread_struct), GFP_KERNEL);
if (ts == NULL) {
printk(KERN_INFO "%s:\tunable to allocate memory for the thread structure\n",__FUNCTION__);
return 0;
}
ts->new_file_name = NULL;
ts->old_file_name = NULL;
init_waitqueue_head(&ts->thread_sv);
printk(KERN_INFO "%s:\tmodule loaded\n",__FUNCTION__);
printk(KERN_INFO "%s:\ttrying to initiate a thread\n",__FUNCTION__);
my_entry = create_proc_entry(prc_file_name, 0644, NULL);
if (my_entry == NULL) {
printk(KERN_INFO "%s:\tunable to allocate a proc entry\n",__FUNCTION__);
return 0;
}
my_entry->read_proc = read_proc_file;
my_entry->write_proc = write_proc_file;
my_entry->owner = THIS_MODULE;
ts->thread_pid = kernel_thread(my_thread_fun, (void *) ts,
(CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD));
if (ts->thread_pid < 0) {
printk(KERN_INFO "%s:\tunable to create a thread\n", __FUNCTION__);
}
return 0;
}
void __exit
exit_thread_module(void) {
while (ts->thread_present) {
/* kill the deamon */
if (kill_proc(ts->thread_pid, SIGKILL, 1) != 0) {
printk(KERN_INFO "%s:\tunable to kill the thread. dont know what to do now \n", __FUNCTION__);
}
}
if (ts->new_file_name != NULL)
kfree(ts->new_file_name);
if (ts->old_file_name != NULL)
kfree(ts->old_file_name);
kfree(ts);
remove_proc_entry(prc_file_name, NULL);
printk(KERN_INFO "%s:\ttime to unload the module\n", __FUNCTION__);
return;
}
module_init(init_thread_module)
module_exit(exit_thread_module)
_______________________________________________
Kernelnewbies mailing list
[email protected]
http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies