123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- /* 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() */
|