2005-04-16 15:20:36 -07:00
/* 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 " ) ;
2005-06-24 23:13:50 -07:00
try_to_freeze ( ) ;
2005-04-16 15:20:36 -07:00
/* 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() */