1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
/*
* Copyright (C) 2003 Sistina Software
*
* This file is released under the LGPL.
*/
#include "dm.h"
#include "dm-daemon.h"
#include <linux/module.h>
#include <linux/sched.h>
static int daemon(void *arg)
{
struct dm_daemon *dd = (struct dm_daemon *) arg;
DECLARE_WAITQUEUE(wq, current);
daemonize();
reparent_to_init();
/* block all signals */
spin_lock_irq(¤t->sigmask_lock);
sigfillset(¤t->blocked);
flush_signals(current);
spin_unlock_irq(¤t->sigmask_lock);
strcpy(current->comm, dd->name);
atomic_set(&dd->please_die, 0);
add_wait_queue(&dd->job_queue, &wq);
down(&dd->run_lock);
up(&dd->start_lock);
/*
* dd->fn() could do anything, very likely it will
* suspend. So we can't set the state to
* TASK_INTERRUPTIBLE before calling it. In order to
* prevent a race with a waking thread we do this little
* dance with the dd->woken variable.
*/
while (1) {
do {
set_current_state(TASK_RUNNING);
if (atomic_read(&dd->please_die))
goto out;
atomic_set(&dd->woken, 0);
dd->fn();
yield();
set_current_state(TASK_INTERRUPTIBLE);
} while (atomic_read(&dd->woken));
schedule();
}
out:
remove_wait_queue(&dd->job_queue, &wq);
up(&dd->run_lock);
return 0;
}
int dm_daemon_start(struct dm_daemon *dd, const char *name, void (*fn)(void))
{
pid_t pid = 0;
/*
* Initialise the dm_daemon.
*/
dd->fn = fn;
strncpy(dd->name, name, sizeof(dd->name) - 1);
sema_init(&dd->start_lock, 1);
sema_init(&dd->run_lock, 1);
init_waitqueue_head(&dd->job_queue);
/*
* Start the new thread.
*/
down(&dd->start_lock);
pid = kernel_thread(daemon, dd, 0);
if (pid <= 0) {
DMERR("Failed to start %s thread", name);
return -EAGAIN;
}
/*
* wait for the daemon to up this mutex.
*/
down(&dd->start_lock);
up(&dd->start_lock);
return 0;
}
void dm_daemon_stop(struct dm_daemon *dd)
{
atomic_set(&dd->please_die, 1);
dm_daemon_wake(dd);
down(&dd->run_lock);
up(&dd->run_lock);
}
void dm_daemon_wake(struct dm_daemon *dd)
{
atomic_set(&dd->woken, 1);
wake_up_interruptible(&dd->job_queue);
}
EXPORT_SYMBOL(dm_daemon_start);
EXPORT_SYMBOL(dm_daemon_stop);
EXPORT_SYMBOL(dm_daemon_wake);
|