/* 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 <linux/freezer.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; 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(); /* 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() */