diff options
Diffstat (limited to 'net/rxrpc/krxiod.c')
-rw-r--r-- | net/rxrpc/krxiod.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/net/rxrpc/krxiod.c b/net/rxrpc/krxiod.c new file mode 100644 index 000000000000..2b537f425a17 --- /dev/null +++ b/net/rxrpc/krxiod.c @@ -0,0 +1,261 @@ +/* krxiod.c: Rx I/O 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/sched.h> +#include <linux/completion.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <rxrpc/krxiod.h> +#include <rxrpc/transport.h> +#include <rxrpc/peer.h> +#include <rxrpc/call.h> +#include "internal.h" + +static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxiod_sleepq); +static DECLARE_COMPLETION(rxrpc_krxiod_dead); + +static atomic_t rxrpc_krxiod_qcount = ATOMIC_INIT(0); + +static LIST_HEAD(rxrpc_krxiod_transportq); +static DEFINE_SPINLOCK(rxrpc_krxiod_transportq_lock); + +static LIST_HEAD(rxrpc_krxiod_callq); +static DEFINE_SPINLOCK(rxrpc_krxiod_callq_lock); + +static volatile int rxrpc_krxiod_die; + +/*****************************************************************************/ +/* + * Rx I/O daemon + */ +static int rxrpc_krxiod(void *arg) +{ + DECLARE_WAITQUEUE(krxiod,current); + + printk("Started krxiod %d\n",current->pid); + + daemonize("krxiod"); + + /* loop around waiting for work to do */ + do { + /* wait for work or to be told to exit */ + _debug("### Begin Wait"); + if (!atomic_read(&rxrpc_krxiod_qcount)) { + set_current_state(TASK_INTERRUPTIBLE); + + add_wait_queue(&rxrpc_krxiod_sleepq, &krxiod); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (atomic_read(&rxrpc_krxiod_qcount) || + rxrpc_krxiod_die || + signal_pending(current)) + break; + + schedule(); + } + + remove_wait_queue(&rxrpc_krxiod_sleepq, &krxiod); + set_current_state(TASK_RUNNING); + } + _debug("### End Wait"); + + /* do work if been given some to do */ + _debug("### Begin Work"); + + /* see if there's a transport in need of attention */ + if (!list_empty(&rxrpc_krxiod_transportq)) { + struct rxrpc_transport *trans = NULL; + + spin_lock_irq(&rxrpc_krxiod_transportq_lock); + + if (!list_empty(&rxrpc_krxiod_transportq)) { + trans = list_entry( + rxrpc_krxiod_transportq.next, + struct rxrpc_transport, + krxiodq_link); + + list_del_init(&trans->krxiodq_link); + atomic_dec(&rxrpc_krxiod_qcount); + + /* make sure it hasn't gone away and doesn't go + * away */ + if (atomic_read(&trans->usage)>0) + rxrpc_get_transport(trans); + else + trans = NULL; + } + + spin_unlock_irq(&rxrpc_krxiod_transportq_lock); + + if (trans) { + rxrpc_trans_receive_packet(trans); + rxrpc_put_transport(trans); + } + } + + /* see if there's a call in need of attention */ + if (!list_empty(&rxrpc_krxiod_callq)) { + struct rxrpc_call *call = NULL; + + spin_lock_irq(&rxrpc_krxiod_callq_lock); + + if (!list_empty(&rxrpc_krxiod_callq)) { + call = list_entry(rxrpc_krxiod_callq.next, + struct rxrpc_call, + rcv_krxiodq_lk); + list_del_init(&call->rcv_krxiodq_lk); + atomic_dec(&rxrpc_krxiod_qcount); + + /* make sure it hasn't gone away and doesn't go + * away */ + if (atomic_read(&call->usage) > 0) { + _debug("@@@ KRXIOD" + " Begin Attend Call %p", call); + rxrpc_get_call(call); + } + else { + call = NULL; + } + } + + spin_unlock_irq(&rxrpc_krxiod_callq_lock); + + if (call) { + rxrpc_call_do_stuff(call); + rxrpc_put_call(call); + _debug("@@@ KRXIOD End Attend Call %p", call); + } + } + + _debug("### End Work"); + + try_to_freeze(PF_FREEZE); + + /* discard pending signals */ + rxrpc_discard_my_signals(); + + } while (!rxrpc_krxiod_die); + + /* and that's all */ + complete_and_exit(&rxrpc_krxiod_dead, 0); + +} /* end rxrpc_krxiod() */ + +/*****************************************************************************/ +/* + * start up a krxiod daemon + */ +int __init rxrpc_krxiod_init(void) +{ + return kernel_thread(rxrpc_krxiod, NULL, 0); + +} /* end rxrpc_krxiod_init() */ + +/*****************************************************************************/ +/* + * kill the krxiod daemon and wait for it to complete + */ +void rxrpc_krxiod_kill(void) +{ + rxrpc_krxiod_die = 1; + wake_up_all(&rxrpc_krxiod_sleepq); + wait_for_completion(&rxrpc_krxiod_dead); + +} /* end rxrpc_krxiod_kill() */ + +/*****************************************************************************/ +/* + * queue a transport for attention by krxiod + */ +void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans) +{ + unsigned long flags; + + _enter(""); + + if (list_empty(&trans->krxiodq_link)) { + spin_lock_irqsave(&rxrpc_krxiod_transportq_lock, flags); + + if (list_empty(&trans->krxiodq_link)) { + if (atomic_read(&trans->usage) > 0) { + list_add_tail(&trans->krxiodq_link, + &rxrpc_krxiod_transportq); + atomic_inc(&rxrpc_krxiod_qcount); + } + } + + spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock, flags); + wake_up_all(&rxrpc_krxiod_sleepq); + } + + _leave(""); + +} /* end rxrpc_krxiod_queue_transport() */ + +/*****************************************************************************/ +/* + * dequeue a transport from krxiod's attention queue + */ +void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans) +{ + unsigned long flags; + + _enter(""); + + spin_lock_irqsave(&rxrpc_krxiod_transportq_lock, flags); + if (!list_empty(&trans->krxiodq_link)) { + list_del_init(&trans->krxiodq_link); + atomic_dec(&rxrpc_krxiod_qcount); + } + spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock, flags); + + _leave(""); + +} /* end rxrpc_krxiod_dequeue_transport() */ + +/*****************************************************************************/ +/* + * queue a call for attention by krxiod + */ +void rxrpc_krxiod_queue_call(struct rxrpc_call *call) +{ + unsigned long flags; + + if (list_empty(&call->rcv_krxiodq_lk)) { + spin_lock_irqsave(&rxrpc_krxiod_callq_lock, flags); + if (atomic_read(&call->usage) > 0) { + list_add_tail(&call->rcv_krxiodq_lk, + &rxrpc_krxiod_callq); + atomic_inc(&rxrpc_krxiod_qcount); + } + spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock, flags); + } + wake_up_all(&rxrpc_krxiod_sleepq); + +} /* end rxrpc_krxiod_queue_call() */ + +/*****************************************************************************/ +/* + * dequeue a call from krxiod's attention queue + */ +void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call) +{ + unsigned long flags; + + spin_lock_irqsave(&rxrpc_krxiod_callq_lock, flags); + if (!list_empty(&call->rcv_krxiodq_lk)) { + list_del_init(&call->rcv_krxiodq_lk); + atomic_dec(&rxrpc_krxiod_qcount); + } + spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock, flags); + +} /* end rxrpc_krxiod_dequeue_call() */ |