diff options
Diffstat (limited to 'net/rxrpc/krxtimod.c')
-rw-r--r-- | net/rxrpc/krxtimod.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/net/rxrpc/krxtimod.c b/net/rxrpc/krxtimod.c new file mode 100644 index 000000000000..249c2b0290bb --- /dev/null +++ b/net/rxrpc/krxtimod.c @@ -0,0 +1,203 @@ +/* krxtimod.c: RXRPC timeout daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include <rxrpc/rxrpc.h> +#include <rxrpc/krxtimod.h> +#include <asm/errno.h> +#include "internal.h" + +static DECLARE_COMPLETION(krxtimod_alive); +static DECLARE_COMPLETION(krxtimod_dead); +static DECLARE_WAIT_QUEUE_HEAD(krxtimod_sleepq); +static int krxtimod_die; + +static LIST_HEAD(krxtimod_list); +static DEFINE_SPINLOCK(krxtimod_lock); + +static int krxtimod(void *arg); + +/*****************************************************************************/ +/* + * start the timeout daemon + */ +int rxrpc_krxtimod_start(void) +{ + int ret; + + ret = kernel_thread(krxtimod, NULL, 0); + if (ret < 0) + return ret; + + wait_for_completion(&krxtimod_alive); + + return ret; +} /* end rxrpc_krxtimod_start() */ + +/*****************************************************************************/ +/* + * stop the timeout daemon + */ +void rxrpc_krxtimod_kill(void) +{ + /* get rid of my daemon */ + krxtimod_die = 1; + wake_up(&krxtimod_sleepq); + wait_for_completion(&krxtimod_dead); + +} /* end rxrpc_krxtimod_kill() */ + +/*****************************************************************************/ +/* + * timeout processing daemon + */ +static int krxtimod(void *arg) +{ + DECLARE_WAITQUEUE(myself, current); + + rxrpc_timer_t *timer; + + printk("Started krxtimod %d\n", current->pid); + + daemonize("krxtimod"); + + complete(&krxtimod_alive); + + /* loop around looking for things to attend to */ + loop: + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&krxtimod_sleepq, &myself); + + for (;;) { + unsigned long jif; + signed long timeout; + + /* deal with the server being asked to die */ + if (krxtimod_die) { + remove_wait_queue(&krxtimod_sleepq, &myself); + _leave(""); + complete_and_exit(&krxtimod_dead, 0); + } + + try_to_freeze(PF_FREEZE); + + /* discard pending signals */ + rxrpc_discard_my_signals(); + + /* work out the time to elapse before the next event */ + spin_lock(&krxtimod_lock); + if (list_empty(&krxtimod_list)) { + timeout = MAX_SCHEDULE_TIMEOUT; + } + else { + timer = list_entry(krxtimod_list.next, + rxrpc_timer_t, link); + timeout = timer->timo_jif; + jif = jiffies; + + if (time_before_eq((unsigned long) timeout, jif)) + goto immediate; + + else { + timeout = (long) timeout - (long) jiffies; + } + } + spin_unlock(&krxtimod_lock); + + schedule_timeout(timeout); + + set_current_state(TASK_INTERRUPTIBLE); + } + + /* the thing on the front of the queue needs processing + * - we come here with the lock held and timer pointing to the expired + * entry + */ + immediate: + remove_wait_queue(&krxtimod_sleepq, &myself); + set_current_state(TASK_RUNNING); + + _debug("@@@ Begin Timeout of %p", timer); + + /* dequeue the timer */ + list_del_init(&timer->link); + spin_unlock(&krxtimod_lock); + + /* call the timeout function */ + timer->ops->timed_out(timer); + + _debug("@@@ End Timeout"); + goto loop; + +} /* end krxtimod() */ + +/*****************************************************************************/ +/* + * (re-)queue a timer + */ +void rxrpc_krxtimod_add_timer(rxrpc_timer_t *timer, unsigned long timeout) +{ + struct list_head *_p; + rxrpc_timer_t *ptimer; + + _enter("%p,%lu", timer, timeout); + + spin_lock(&krxtimod_lock); + + list_del(&timer->link); + + /* the timer was deferred or reset - put it back in the queue at the + * right place */ + timer->timo_jif = jiffies + timeout; + + list_for_each(_p, &krxtimod_list) { + ptimer = list_entry(_p, rxrpc_timer_t, link); + if (time_before(timer->timo_jif, ptimer->timo_jif)) + break; + } + + list_add_tail(&timer->link, _p); /* insert before stopping point */ + + spin_unlock(&krxtimod_lock); + + wake_up(&krxtimod_sleepq); + + _leave(""); +} /* end rxrpc_krxtimod_add_timer() */ + +/*****************************************************************************/ +/* + * dequeue a timer + * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued + */ +int rxrpc_krxtimod_del_timer(rxrpc_timer_t *timer) +{ + int ret = 0; + + _enter("%p", timer); + + spin_lock(&krxtimod_lock); + + if (list_empty(&timer->link)) + ret = -ENOENT; + else + list_del_init(&timer->link); + + spin_unlock(&krxtimod_lock); + + wake_up(&krxtimod_sleepq); + + _leave(" = %d", ret); + return ret; +} /* end rxrpc_krxtimod_del_timer() */ |