/* cmservice.c: AFS Cache Manager Service
*
* 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 "server.h"
#include "cell.h"
#include "transport.h"
#include <rxrpc/rxrpc.h>
#include <rxrpc/transport.h>
#include <rxrpc/connection.h>
#include <rxrpc/call.h>
#include "cmservice.h"
#include "internal.h"
static unsigned afscm_usage; /* AFS cache manager usage count */
static struct rw_semaphore afscm_sem; /* AFS cache manager start/stop semaphore */
static int afscm_new_call(struct rxrpc_call *call);
static void afscm_attention(struct rxrpc_call *call);
static void afscm_error(struct rxrpc_call *call);
static void afscm_aemap(struct rxrpc_call *call);
static void _SRXAFSCM_CallBack(struct rxrpc_call *call);
static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call);
static void _SRXAFSCM_Probe(struct rxrpc_call *call);
typedef void (*_SRXAFSCM_xxxx_t)(struct rxrpc_call *call);
static const struct rxrpc_operation AFSCM_ops[] = {
{
.id = 204,
.asize = RXRPC_APP_MARK_EOF,
.name = "CallBack",
.user = _SRXAFSCM_CallBack,
},
{
.id = 205,
.asize = RXRPC_APP_MARK_EOF,
.name = "InitCallBackState",
.user = _SRXAFSCM_InitCallBackState,
},
{
.id = 206,
.asize = RXRPC_APP_MARK_EOF,
.name = "Probe",
.user = _SRXAFSCM_Probe,
},
#if 0
{
.id = 207,
.asize = RXRPC_APP_MARK_EOF,
.name = "GetLock",
.user = _SRXAFSCM_GetLock,
},
{
.id = 208,
.asize = RXRPC_APP_MARK_EOF,
.name = "GetCE",
.user = _SRXAFSCM_GetCE,
},
{
.id = 209,
.asize = RXRPC_APP_MARK_EOF,
.name = "GetXStatsVersion",
.user = _SRXAFSCM_GetXStatsVersion,
},
{
.id = 210,
.asize = RXRPC_APP_MARK_EOF,
.name = "GetXStats",
.user = _SRXAFSCM_GetXStats,
}
#endif
};
static struct rxrpc_service AFSCM_service = {
.name = "AFS/CM",
.owner = THIS_MODULE,
.link = LIST_HEAD_INIT(AFSCM_service.link),
.new_call = afscm_new_call,
.service_id = 1,
.attn_func = afscm_attention,
.error_func = afscm_error,
.aemap_func = afscm_aemap,
.ops_begin = &AFSCM_ops[0],
.ops_end = &AFSCM_ops[ARRAY_SIZE(AFSCM_ops)],
};
static DECLARE_COMPLETION(kafscmd_alive);
static DECLARE_COMPLETION(kafscmd_dead);
static DECLARE_WAIT_QUEUE_HEAD(kafscmd_sleepq);
static LIST_HEAD(kafscmd_attention_list);
static LIST_HEAD(afscm_calls);
static DEFINE_SPINLOCK(afscm_calls_lock);
static DEFINE_SPINLOCK(kafscmd_attention_lock);
static int kafscmd_die;
/*****************************************************************************/
/*
* AFS Cache Manager kernel thread
*/
static int kafscmd(void *arg)
{
DECLARE_WAITQUEUE(myself, current);
struct rxrpc_call *call;
_SRXAFSCM_xxxx_t func;
int die;
printk(KERN_INFO "kAFS: Started kafscmd %d\n", current->pid);
daemonize("kafscmd");
complete(&kafscmd_alive);
/* loop around looking for things to attend to */
do {
if (list_empty(&kafscmd_attention_list)) {
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&kafscmd_sleepq, &myself);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (!list_empty(&kafscmd_attention_list) ||
signal_pending(current) ||
kafscmd_die)
break;
schedule();
}
remove_wait_queue(&kafscmd_sleepq, &myself);
set_current_state(TASK_RUNNING);
}
die = kafscmd_die;
/* dequeue the next call requiring attention */
call = NULL;
spin_lock(&kafscmd_attention_lock);
if (!list_empty(&kafscmd_attention_list)) {
call = list_entry(kafscmd_attention_list.next,
struct rxrpc_call,
app_attn_link);
list_del_init(&call->app_attn_link);
die = 0;
}
spin_unlock(&kafscmd_attention_lock);
if (call) {
/* act upon it */
_debug("@@@ Begin Attend Call %p", call);
func = call->app_user;
if (func)
func(call);
rxrpc_put_call(call);
_debug("@@@ End Attend Call %p", call);
}
} while(!die);
/* and that's all */
complete_and_exit(&kafscmd_dead, 0);
} /* end kafscmd() */
/*****************************************************************************/
/*
* handle a call coming in to the cache manager
* - if I want to keep the call, I must increment its usage count
* - the return value will be negated and passed back in an abort packet if
* non-zero
* - serialised by virtue of there only being one krxiod
*/
static int afscm_new_call(struct rxrpc_call *call)
{
_enter("%p{cid=%u u=%d}",
call, ntohl(call->call_id), atomic_read(&call->usage));
rxrpc_get_call(call);
/* add to my current call list */
spin_lock(&afscm_calls_lock);
list_add(&call->app_link,&afscm_calls);
spin_unlock(&afscm_calls_lock);
_leave(" = 0");
return 0;
} /* end afscm_new_call() */
/*****************************************************************************/
/*
* queue on the kafscmd queue for attention
*/
static void afscm_attention(struct rxrpc_call *call)
{
_enter("%p{cid=%u u=%d}",
call, ntohl(call->call_id), atomic_read(&call->usage));
spin_lock(&kafscmd_attention_lock);
if (list_empty(&call->app_attn_link)) {
list_add_tail(&call->app_attn_link, &kafscmd_attention_list);
rxrpc_get_call(call);
}
spin_unlock(&kafscmd_attention_lock);
wake_up(&kafscmd_sleepq);
_leave(" {u=%d}", atomic_read(&call->usage));
} /* end afscm_attention() */
/*****************************************************************************/
/*
* handle my call being aborted
* - clean up, dequeue and put my ref to the call
*/
static void afscm_error(struct rxrpc_call *call)
{
int removed;
_enter("%p{est=%s ac=%u er=%d}",
call,
rxrpc_call_error_states[call->app_err_state],
call->app_abort_code,
call->app_errno);
spin_lock(&kafscmd_attention_lock);
if (list_empty(&call->app_attn_link)) {
list_add_tail(&call->app_attn_link, &kafscmd_attention_list);
rxrpc_get_call(call);
}
spin_unlock(&kafscmd_attention_lock);
removed = 0;
spin_lock(&afscm_calls_lock);
if (!list_empty(&call->app_link)) {
list_del_init(&call->app_link);
removed = 1;
}
spin_unlock(&afscm_calls_lock);
if (removed)
rxrpc_put_call(call);
wake_up(&kafscmd_sleepq);
_leave("");
} /* end afscm_error() */
/*****************************************************************************/
/*
* map afs abort codes to/from Linux error codes
* - called with call->lock held
*/
static void afscm_aemap(struct rxrpc_call *call)
{
switch (call->app_err_state) {
case RXRPC_ESTATE_LOCAL_ABORT:
call->app_abort_code = -call->app_errno;
break;
case RXRPC_ESTATE_PEER_ABORT:
call->app_errno = -ECONNABORTED;
break;
default:
break;
}
} /* end afscm_aemap() */
/*****************************************************************************/
/*
* start the cache manager service if not already started
*/
int afscm_start(void)
{
int ret;
down_write(&afscm_sem);
if (!afscm_usage) {
ret = kernel_thread(kafscmd, NULL, 0);
if (ret < 0)
goto out;
wait_for_completion(&kafscmd_alive);
ret = rxrpc_add_service(afs_transport, &AFSCM_service);
if (ret < 0)
goto kill;
afs_kafstimod_add_timer(&afs_mntpt_expiry_timer,
afs_mntpt_expiry_timeout * HZ);
}
afscm_usage++;
up_write(&afscm_sem);
return 0;
kill:
kafscmd_die = 1;
wake_up(&kafscmd_sleepq);
wait_for_completion(&kafscmd_dead);
out:
up_write(&afscm_sem);
return ret;
} /* end afscm_start() */
/*****************************************************************************/
/*
* stop the cache manager service
*/
void afscm_stop(void)
{
struct rxrpc_call *call;
down_write(&afscm_sem);
BUG_ON(afscm_usage == 0);
afscm_usage--;
if (afscm_usage == 0) {
/* don't want more incoming calls */
rxrpc_del_service(afs_transport, &AFSCM_service);
/* abort any calls I've still got open (the afscm_error() will
* dequeue them) */
spin_lock(&afscm_calls_lock);
while (!list_empty(&afscm_calls)) {
call = list_entry(afscm_calls.next,
struct rxrpc_call,
app_link);
list_del_init(&call->app_link);
rxrpc_get_call(call);
spin_unlock(&afscm_calls_lock);
rxrpc_call_abort(call, -ESRCH); /* abort, dequeue and
* put */
_debug("nuking active call %08x.%d",
ntohl(call->conn->conn_id),
ntohl(call->call_id));
rxrpc_put_call(call);
rxrpc_put_call(call);
spin_lock(&afscm_calls_lock);
}
spin_unlock(&afscm_calls_lock);
/* get rid of my daemon */
kafscmd_die = 1;
wake_up(&kafscmd_sleepq);
wait_for_completion(&kafscmd_dead);
/* dispose of any calls waiting for attention */
spin_lock(&kafscmd_attention_lock);
while (!list_empty(&kafscmd_attention_list)) {
call = list_entry(kafscmd_attention_list.next,
struct rxrpc_call,
app_attn_link);
list_del_init(&call->app_attn_link);
spin_unlock(&kafscmd_attention_lock);
rxrpc_put_call(call);
spin_lock(&kafscmd_attention_lock);
}
spin_unlock(&kafscmd_attention_lock);
afs_kafstimod_del_timer(&afs_mntpt_expiry_timer);
}
up_write(&afscm_sem);
} /* end afscm_stop() */
/*****************************************************************************/
/*
* handle the fileserver breaking a set of callbacks
*/
static void _SRXAFSCM_CallBack(struct rxrpc_call *call)
{
struct afs_server *server;
size_t count, qty, tmp;
int ret = 0, removed;
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
server = afs_server_get_from_peer(call->conn->peer);
switch (call->app_call_state) {
/* we've received the last packet
* - drain all the data from the call and send the reply
*/
case RXRPC_CSTATE_SRVR_GOT_ARGS:
ret = -EBADMSG;
qty = call->app_ready_qty;
if (qty < 8 || qty > 50 * (6 * 4) + 8)
break;
{
struct afs_callback *cb, *pcb;
int loop;
__be32 *fp, *bp;
fp = rxrpc_call_alloc_scratch(call, qty);
/* drag the entire argument block out to the scratch
* space */
ret = rxrpc_call_read_data(call, fp, qty, 0);
if (ret < 0)
break;
/* and unmarshall the parameter block */
ret = -EBADMSG;
count = ntohl(*fp++);
if (count>AFSCBMAX ||
(count * (3 * 4) + 8 != qty &&
count * (6 * 4) + 8 != qty))
break;
bp = fp + count*3;
tmp = ntohl(*bp++);
if (tmp > 0 && tmp != count)
break;
if (tmp == 0)
bp = NULL;
pcb = cb = rxrpc_call_alloc_scratch_s(
call, struct afs_callback);
for (loop = count - 1; loop >= 0; loop--) {
pcb->fid.vid = ntohl(*fp++);
pcb->fid.vnode = ntohl(*fp++);
pcb->fid.unique = ntohl(*fp++);
if (bp) {
pcb->version = ntohl(*bp++);
pcb->expiry = ntohl(*bp++);
pcb->type = ntohl(*bp++);
}
else {
pcb->version = 0;
pcb->expiry = 0;
pcb->type = AFSCM_CB_UNTYPED;
}
pcb++;
}
/* invoke the actual service routine */
ret = SRXAFSCM_CallBack(server, count, cb);
if (ret < 0)
break;
}
/* send the reply */
ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
GFP_KERNEL, 0, &count);
if (ret < 0)
break;
break;
/* operation complete */
case RXRPC_CSTATE_COMPLETE:
call->app_user = NULL;
removed = 0;
spin_lock(&afscm_calls_lock);
if (!list_empty(&call->app_link)) {
list_del_init(&call->app_link);
removed = 1;
}
spin_unlock(&afscm_calls_lock);
if (removed)
rxrpc_put_call(call);
break;
/* operation terminated on error */
case RXRPC_CSTATE_ERROR:
call->app_user = NULL;
break;
default:
break;
}
if (ret < 0)
rxrpc_call_abort(call, ret);
afs_put_server(server);
_leave(" = %d", ret);
} /* end _SRXAFSCM_CallBack() */
/*****************************************************************************/
/*
* handle the fileserver asking us to initialise our callback state
*/
static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call)
{
struct afs_server *server;
size_t count;
int ret = 0, removed;
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
server = afs_server_get_from_peer(call->conn->peer);
switch (call->app_call_state) {
/* we've received the last packet - drain all the data from the
* call */
case RXRPC_CSTATE_SRVR_GOT_ARGS:
/* shouldn't be any args */
ret = -EBADMSG;
break;
/* send the reply when asked for it */
case RXRPC_CSTATE_SRVR_SND_REPLY:
/* invoke the actual service routine */
ret = SRXAFSCM_InitCallBackState(server);
if (ret < 0)
break;
ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
GFP_KERNEL, 0, &count);
if (ret < 0)
break;
break;
/* operation complete */
case RXRPC_CSTATE_COMPLETE:
call->app_user = NULL;
removed = 0;
spin_lock(&afscm_calls_lock);
if (!list_empty(&call->app_link)) {
list_del_init(&call->app_link);
removed = 1;
}
spin_unlock(&afscm_calls_lock);
if (removed)
rxrpc_put_call(call);
break;
/* operation terminated on error */
case RXRPC_CSTATE_ERROR:
call->app_user = NULL;
break;
default:
break;
}
if (ret < 0)
rxrpc_call_abort(call, ret);
afs_put_server(server);
_leave(" = %d", ret);
} /* end _SRXAFSCM_InitCallBackState() */
/*****************************************************************************/
/*
* handle a probe from a fileserver
*/
static void _SRXAFSCM_Probe(struct rxrpc_call *call)
{
struct afs_server *server;
size_t count;
int ret = 0, removed;
_enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]);
server = afs_server_get_from_peer(call->conn->peer);
switch (call->app_call_state) {
/* we've received the last packet - drain all the data from the
* call */
case RXRPC_CSTATE_SRVR_GOT_ARGS:
/* shouldn't be any args */
ret = -EBADMSG;
break;
/* send the reply when asked for it */
case RXRPC_CSTATE_SRVR_SND_REPLY:
/* invoke the actual service routine */
ret = SRXAFSCM_Probe(server);
if (ret < 0)
break;
ret = rxrpc_call_write_data(call, 0, NULL, RXRPC_LAST_PACKET,
GFP_KERNEL, 0, &count);
if (ret < 0)
break;
break;
/* operation complete */
case RXRPC_CSTATE_COMPLETE:
call->app_user = NULL;
removed = 0;
spin_lock(&afscm_calls_lock);
if (!list_empty(&call->app_link)) {
list_del_init(&call->app_link);
removed = 1;
}
spin_unlock(&afscm_calls_lock);
if (removed)
rxrpc_put_call(call);
break;
/* operation terminated on error */
case RXRPC_CSTATE_ERROR:
call->app_user = NULL;
break;
default:
break;
}
if (ret < 0)
rxrpc_call_abort(call, ret);
afs_put_server(server);
_leave(" = %d", ret);
} /* end _SRXAFSCM_Probe() */