2005-04-17 02:20:36 +04:00
/* kafsasyncd.c: AFS asynchronous operation 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 .
*
*
* The AFS async daemon is used to the following :
* - probe " dead " servers to see whether they ' ve come back to life yet .
* - probe " live " servers that we haven ' t talked to for a while to see if they are better
* candidates for serving than what we ' re currently using
* - poll volume location servers to keep up to date volume location lists
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/completion.h>
# include "cell.h"
# include "server.h"
# include "volume.h"
# include "kafsasyncd.h"
# include "kafstimod.h"
# include <rxrpc/call.h>
# include <asm/errno.h>
# include "internal.h"
static DECLARE_COMPLETION ( kafsasyncd_alive ) ;
static DECLARE_COMPLETION ( kafsasyncd_dead ) ;
static DECLARE_WAIT_QUEUE_HEAD ( kafsasyncd_sleepq ) ;
static struct task_struct * kafsasyncd_task ;
static int kafsasyncd_die ;
static int kafsasyncd ( void * arg ) ;
static LIST_HEAD ( kafsasyncd_async_attnq ) ;
static LIST_HEAD ( kafsasyncd_async_busyq ) ;
static DEFINE_SPINLOCK ( kafsasyncd_async_lock ) ;
static void kafsasyncd_null_call_attn_func ( struct rxrpc_call * call )
{
}
static void kafsasyncd_null_call_error_func ( struct rxrpc_call * call )
{
}
/*****************************************************************************/
/*
* start the async daemon
*/
int afs_kafsasyncd_start ( void )
{
int ret ;
ret = kernel_thread ( kafsasyncd , NULL , 0 ) ;
if ( ret < 0 )
return ret ;
wait_for_completion ( & kafsasyncd_alive ) ;
return ret ;
} /* end afs_kafsasyncd_start() */
/*****************************************************************************/
/*
* stop the async daemon
*/
void afs_kafsasyncd_stop ( void )
{
/* get rid of my daemon */
kafsasyncd_die = 1 ;
wake_up ( & kafsasyncd_sleepq ) ;
wait_for_completion ( & kafsasyncd_dead ) ;
} /* end afs_kafsasyncd_stop() */
/*****************************************************************************/
/*
* probing daemon
*/
static int kafsasyncd ( void * arg )
{
struct afs_async_op * op ;
int die ;
DECLARE_WAITQUEUE ( myself , current ) ;
kafsasyncd_task = current ;
printk ( " kAFS: Started kafsasyncd %d \n " , current - > pid ) ;
daemonize ( " kafsasyncd " ) ;
complete ( & kafsasyncd_alive ) ;
/* loop around looking for things to attend to */
do {
set_current_state ( TASK_INTERRUPTIBLE ) ;
add_wait_queue ( & kafsasyncd_sleepq , & myself ) ;
for ( ; ; ) {
if ( ! list_empty ( & kafsasyncd_async_attnq ) | |
signal_pending ( current ) | |
kafsasyncd_die )
break ;
schedule ( ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
}
remove_wait_queue ( & kafsasyncd_sleepq , & myself ) ;
set_current_state ( TASK_RUNNING ) ;
2005-06-25 10:13:50 +04:00
try_to_freeze ( ) ;
2005-04-17 02:20:36 +04:00
/* discard pending signals */
afs_discard_my_signals ( ) ;
die = kafsasyncd_die ;
/* deal with the next asynchronous operation requiring
* attention */
if ( ! list_empty ( & kafsasyncd_async_attnq ) ) {
struct afs_async_op * op ;
_debug ( " @@@ Begin Asynchronous Operation " ) ;
op = NULL ;
spin_lock ( & kafsasyncd_async_lock ) ;
if ( ! list_empty ( & kafsasyncd_async_attnq ) ) {
op = list_entry ( kafsasyncd_async_attnq . next ,
struct afs_async_op , link ) ;
list_del ( & op - > link ) ;
list_add_tail ( & op - > link ,
& kafsasyncd_async_busyq ) ;
}
spin_unlock ( & kafsasyncd_async_lock ) ;
_debug ( " @@@ Operation %p {%p} \n " ,
op , op ? op - > ops : NULL ) ;
if ( op )
op - > ops - > attend ( op ) ;
_debug ( " @@@ End Asynchronous Operation " ) ;
}
} while ( ! die ) ;
/* need to kill all outstanding asynchronous operations before
* exiting */
kafsasyncd_task = NULL ;
spin_lock ( & kafsasyncd_async_lock ) ;
/* fold the busy and attention queues together */
list_splice_init ( & kafsasyncd_async_busyq ,
& kafsasyncd_async_attnq ) ;
/* dequeue kafsasyncd from all their wait queues */
list_for_each_entry ( op , & kafsasyncd_async_attnq , link ) {
op - > call - > app_attn_func = kafsasyncd_null_call_attn_func ;
op - > call - > app_error_func = kafsasyncd_null_call_error_func ;
remove_wait_queue ( & op - > call - > waitq , & op - > waiter ) ;
}
spin_unlock ( & kafsasyncd_async_lock ) ;
/* abort all the operations */
while ( ! list_empty ( & kafsasyncd_async_attnq ) ) {
op = list_entry ( kafsasyncd_async_attnq . next , struct afs_async_op , link ) ;
list_del_init ( & op - > link ) ;
rxrpc_call_abort ( op - > call , - EIO ) ;
rxrpc_put_call ( op - > call ) ;
op - > call = NULL ;
op - > ops - > discard ( op ) ;
}
/* and that's all */
_leave ( " " ) ;
complete_and_exit ( & kafsasyncd_dead , 0 ) ;
} /* end kafsasyncd() */
/*****************************************************************************/
/*
* begin an operation
* - place operation on busy queue
*/
void afs_kafsasyncd_begin_op ( struct afs_async_op * op )
{
_enter ( " " ) ;
spin_lock ( & kafsasyncd_async_lock ) ;
init_waitqueue_entry ( & op - > waiter , kafsasyncd_task ) ;
add_wait_queue ( & op - > call - > waitq , & op - > waiter ) ;
list_del ( & op - > link ) ;
list_add_tail ( & op - > link , & kafsasyncd_async_busyq ) ;
spin_unlock ( & kafsasyncd_async_lock ) ;
_leave ( " " ) ;
} /* end afs_kafsasyncd_begin_op() */
/*****************************************************************************/
/*
* request attention for an operation
* - move to attention queue
*/
void afs_kafsasyncd_attend_op ( struct afs_async_op * op )
{
_enter ( " " ) ;
spin_lock ( & kafsasyncd_async_lock ) ;
list_del ( & op - > link ) ;
list_add_tail ( & op - > link , & kafsasyncd_async_attnq ) ;
spin_unlock ( & kafsasyncd_async_lock ) ;
wake_up ( & kafsasyncd_sleepq ) ;
_leave ( " " ) ;
} /* end afs_kafsasyncd_attend_op() */
/*****************************************************************************/
/*
* terminate an operation
* - remove from either queue
*/
void afs_kafsasyncd_terminate_op ( struct afs_async_op * op )
{
_enter ( " " ) ;
spin_lock ( & kafsasyncd_async_lock ) ;
if ( ! list_empty ( & op - > link ) ) {
list_del_init ( & op - > link ) ;
remove_wait_queue ( & op - > call - > waitq , & op - > waiter ) ;
}
spin_unlock ( & kafsasyncd_async_lock ) ;
wake_up ( & kafsasyncd_sleepq ) ;
_leave ( " " ) ;
} /* end afs_kafsasyncd_terminate_op() */