653 строки
14 KiB
C
653 строки
14 KiB
C
/* 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() */
|