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