X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Fafs%2Fcmservice.c;h=6685f4cbccb334c7188a229e055158e364b73bf1;hb=6b06d2cc6d52830e3e0c001006b26255f47184dd;hp=3d097fddcb7a7d1943420bc143dc62eb34f9d377;hpb=63e8d9114dab88c101be2372cb1f83a4fe74d9c1;p=linux-2.6 diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index 3d097fddcb..6685f4cbcc 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -1,4 +1,4 @@ -/* cmservice.c: AFS Cache Manager Service +/* AFS Cache Manager Service * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -12,641 +12,463 @@ #include #include #include -#include -#include "server.h" -#include "cell.h" -#include "transport.h" -#include -#include -#include -#include -#include "cmservice.h" +#include #include "internal.h" +#include "afs_cm.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 -}; +struct workqueue_struct *afs_cm_workqueue; -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 int afs_deliver_cb_init_call_back_state(struct afs_call *, + struct sk_buff *, bool); +static int afs_deliver_cb_init_call_back_state3(struct afs_call *, + struct sk_buff *, bool); +static int afs_deliver_cb_probe(struct afs_call *, struct sk_buff *, bool); +static int afs_deliver_cb_callback(struct afs_call *, struct sk_buff *, bool); +static int afs_deliver_cb_get_capabilities(struct afs_call *, struct sk_buff *, + bool); +static void afs_cm_destructor(struct afs_call *); -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 + * CB.CallBack operation type */ -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() */ +static const struct afs_call_type afs_SRXCBCallBack = { + .name = "CB.CallBack", + .deliver = afs_deliver_cb_callback, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; -/*****************************************************************************/ /* - * 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 + * CB.InitCallBackState operation type */ -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() */ +static const struct afs_call_type afs_SRXCBInitCallBackState = { + .name = "CB.InitCallBackState", + .deliver = afs_deliver_cb_init_call_back_state, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; -/*****************************************************************************/ /* - * queue on the kafscmd queue for attention + * CB.InitCallBackState3 operation type */ -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() */ +static const struct afs_call_type afs_SRXCBInitCallBackState3 = { + .name = "CB.InitCallBackState3", + .deliver = afs_deliver_cb_init_call_back_state3, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; -/*****************************************************************************/ /* - * handle my call being aborted - * - clean up, dequeue and put my ref to the call + * CB.Probe operation type */ -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); +static const struct afs_call_type afs_SRXCBProbe = { + .name = "CB.Probe", + .deliver = afs_deliver_cb_probe, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; - _leave(""); -} /* end afscm_error() */ +/* + * CB.GetCapabilities operation type + */ +static const struct afs_call_type afs_SRXCBGetCapabilites = { + .name = "CB.GetCapabilities", + .deliver = afs_deliver_cb_get_capabilities, + .abort_to_error = afs_abort_to_error, + .destructor = afs_cm_destructor, +}; -/*****************************************************************************/ /* - * map afs abort codes to/from Linux error codes - * - called with call->lock held + * route an incoming cache manager call + * - return T if supported, F if not */ -static void afscm_aemap(struct rxrpc_call *call) +bool afs_cm_incoming_call(struct afs_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; + u32 operation_id = ntohl(call->operation_ID); + + _enter("{CB.OP %u}", operation_id); + + switch (operation_id) { + case CBCallBack: + call->type = &afs_SRXCBCallBack; + return true; + case CBInitCallBackState: + call->type = &afs_SRXCBInitCallBackState; + return true; + case CBInitCallBackState3: + call->type = &afs_SRXCBInitCallBackState3; + return true; + case CBProbe: + call->type = &afs_SRXCBProbe; + return true; + case CBGetCapabilities: + call->type = &afs_SRXCBGetCapabilites; + return true; default: - break; + return false; } -} /* end afscm_aemap() */ +} -/*****************************************************************************/ /* - * start the cache manager service if not already started + * clean up a cache manager call */ -int afscm_start(void) +static void afs_cm_destructor(struct afs_call *call) { - 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; + _enter(""); -} /* end afscm_start() */ + afs_put_server(call->server); + call->server = NULL; + kfree(call->buffer); + call->buffer = NULL; +} -/*****************************************************************************/ /* - * stop the cache manager service + * allow the fileserver to see if the cache manager is still alive */ -void afscm_stop(void) +static void SRXAFSCB_CallBack(struct work_struct *work) { - struct rxrpc_call *call; + struct afs_call *call = container_of(work, struct afs_call, work); - down_write(&afscm_sem); + _enter(""); - BUG_ON(afscm_usage == 0); - afscm_usage--; + /* be sure to send the reply *before* attempting to spam the AFS server + * with FSFetchStatus requests on the vnodes with broken callbacks lest + * the AFS server get into a vicious cycle of trying to break further + * callbacks because it hadn't received completion of the CBCallBack op + * yet */ + afs_send_empty_reply(call); - 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); + afs_break_callbacks(call->server, call->count, call->request); + _leave(""); +} - list_del_init(&call->app_link); - rxrpc_get_call(call); - spin_unlock(&afscm_calls_lock); +/* + * deliver request data to a CB.CallBack call + */ +static int afs_deliver_cb_callback(struct afs_call *call, struct sk_buff *skb, + bool last) +{ + struct afs_callback *cb; + struct afs_server *server; + struct in_addr addr; + __be32 *bp; + u32 tmp; + int ret, loop; + + _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); + + switch (call->unmarshall) { + case 0: + call->offset = 0; + call->unmarshall++; + + /* extract the FID array and its count in two steps */ + case 1: + _debug("extract FID count"); + ret = afs_extract_data(call, skb, last, &call->tmp, 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } - rxrpc_call_abort(call, -ESRCH); /* abort, dequeue and - * put */ + call->count = ntohl(call->tmp); + _debug("FID count: %u", call->count); + if (call->count > AFSCBMAX) + return -EBADMSG; + + call->buffer = kmalloc(call->count * 3 * 4, GFP_KERNEL); + if (!call->buffer) + return -ENOMEM; + call->offset = 0; + call->unmarshall++; + + case 2: + _debug("extract FID array"); + ret = afs_extract_data(call, skb, last, call->buffer, + call->count * 3 * 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } - _debug("nuking active call %08x.%d", - ntohl(call->conn->conn_id), - ntohl(call->call_id)); - rxrpc_put_call(call); - rxrpc_put_call(call); + _debug("unmarshall FID array"); + call->request = kcalloc(call->count, + sizeof(struct afs_callback), + GFP_KERNEL); + if (!call->request) + return -ENOMEM; + + cb = call->request; + bp = call->buffer; + for (loop = call->count; loop > 0; loop--, cb++) { + cb->fid.vid = ntohl(*bp++); + cb->fid.vnode = ntohl(*bp++); + cb->fid.unique = ntohl(*bp++); + cb->type = AFSCM_CB_UNTYPED; + } - spin_lock(&afscm_calls_lock); + call->offset = 0; + call->unmarshall++; + + /* extract the callback array and its count in two steps */ + case 3: + _debug("extract CB count"); + ret = afs_extract_data(call, skb, last, &call->tmp, 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; } - spin_unlock(&afscm_calls_lock); - /* get rid of my daemon */ - kafscmd_die = 1; - wake_up(&kafscmd_sleepq); - wait_for_completion(&kafscmd_dead); + tmp = ntohl(call->tmp); + _debug("CB count: %u", tmp); + if (tmp != call->count && tmp != 0) + return -EBADMSG; + call->offset = 0; + call->unmarshall++; + if (tmp == 0) + goto empty_cb_array; + + case 4: + _debug("extract CB array"); + ret = afs_extract_data(call, skb, last, call->request, + call->count * 3 * 4); + switch (ret) { + case 0: break; + case -EAGAIN: return 0; + default: return ret; + } - /* 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); + _debug("unmarshall CB array"); + cb = call->request; + bp = call->buffer; + for (loop = call->count; loop > 0; loop--, cb++) { + cb->version = ntohl(*bp++); + cb->expiry = ntohl(*bp++); + cb->type = ntohl(*bp++); + } - list_del_init(&call->app_attn_link); - spin_unlock(&kafscmd_attention_lock); + empty_cb_array: + call->offset = 0; + call->unmarshall++; - rxrpc_put_call(call); + case 5: + _debug("trailer"); + if (skb->len != 0) + return -EBADMSG; + break; + } - spin_lock(&kafscmd_attention_lock); - } - spin_unlock(&kafscmd_attention_lock); + if (!last) + return 0; - afs_kafstimod_del_timer(&afs_mntpt_expiry_timer); - } + call->state = AFS_CALL_REPLYING; - up_write(&afscm_sem); + /* we'll need the file server record as that tells us which set of + * vnodes to operate upon */ + memcpy(&addr, &ip_hdr(skb)->saddr, 4); + server = afs_find_server(&addr); + if (!server) + return -ENOTCONN; + call->server = server; -} /* end afscm_stop() */ + INIT_WORK(&call->work, SRXAFSCB_CallBack); + schedule_work(&call->work); + return 0; +} -/*****************************************************************************/ /* - * handle the fileserver breaking a set of callbacks + * allow the fileserver to request callback state (re-)initialisation */ -static void _SRXAFSCM_CallBack(struct rxrpc_call *call) +static void SRXAFSCB_InitCallBackState(struct work_struct *work) { - 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; - } + struct afs_call *call = container_of(work, struct afs_call, work); - /* 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); + _enter("{%p}", call->server); - if (removed) - rxrpc_put_call(call); - break; + afs_init_callback_state(call->server); + afs_send_empty_reply(call); + _leave(""); +} - /* operation terminated on error */ - case RXRPC_CSTATE_ERROR: - call->app_user = NULL; - break; +/* + * deliver request data to a CB.InitCallBackState call + */ +static int afs_deliver_cb_init_call_back_state(struct afs_call *call, + struct sk_buff *skb, + bool last) +{ + struct afs_server *server; + struct in_addr addr; - default: - break; - } + _enter(",{%u},%d", skb->len, last); - if (ret < 0) - rxrpc_call_abort(call, ret); + if (skb->len > 0) + return -EBADMSG; + if (!last) + return 0; - afs_put_server(server); + /* no unmarshalling required */ + call->state = AFS_CALL_REPLYING; - _leave(" = %d", ret); + /* we'll need the file server record as that tells us which set of + * vnodes to operate upon */ + memcpy(&addr, &ip_hdr(skb)->saddr, 4); + server = afs_find_server(&addr); + if (!server) + return -ENOTCONN; + call->server = server; -} /* end _SRXAFSCM_CallBack() */ + INIT_WORK(&call->work, SRXAFSCB_InitCallBackState); + schedule_work(&call->work); + return 0; +} -/*****************************************************************************/ /* - * handle the fileserver asking us to initialise our callback state + * deliver request data to a CB.InitCallBackState3 call */ -static void _SRXAFSCM_InitCallBackState(struct rxrpc_call *call) +static int afs_deliver_cb_init_call_back_state3(struct afs_call *call, + struct sk_buff *skb, + bool last) { struct afs_server *server; - size_t count; - int ret = 0, removed; + struct in_addr addr; - _enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); + _enter(",{%u},%d", skb->len, last); - server = afs_server_get_from_peer(call->conn->peer); + if (!last) + return 0; - 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; + /* no unmarshalling required */ + call->state = AFS_CALL_REPLYING; - /* 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); + /* we'll need the file server record as that tells us which set of + * vnodes to operate upon */ + memcpy(&addr, &ip_hdr(skb)->saddr, 4); + server = afs_find_server(&addr); + if (!server) + return -ENOTCONN; + call->server = server; - 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); + INIT_WORK(&call->work, SRXAFSCB_InitCallBackState); + schedule_work(&call->work); + return 0; +} - _leave(" = %d", ret); +/* + * allow the fileserver to see if the cache manager is still alive + */ +static void SRXAFSCB_Probe(struct work_struct *work) +{ + struct afs_call *call = container_of(work, struct afs_call, work); -} /* end _SRXAFSCM_InitCallBackState() */ + _enter(""); + afs_send_empty_reply(call); + _leave(""); +} -/*****************************************************************************/ /* - * handle a probe from a fileserver + * deliver request data to a CB.Probe call */ -static void _SRXAFSCM_Probe(struct rxrpc_call *call) +static int afs_deliver_cb_probe(struct afs_call *call, struct sk_buff *skb, + bool last) { - struct afs_server *server; - size_t count; - int ret = 0, removed; - - _enter("%p{acs=%s}", call, rxrpc_call_states[call->app_call_state]); + _enter(",{%u},%d", skb->len, last); - server = afs_server_get_from_peer(call->conn->peer); + if (skb->len > 0) + return -EBADMSG; + if (!last) + return 0; - 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; + /* no unmarshalling required */ + call->state = AFS_CALL_REPLYING; - /* 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; + INIT_WORK(&call->work, SRXAFSCB_Probe); + schedule_work(&call->work); + return 0; +} - /* 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; +/* + * allow the fileserver to ask about the cache manager's capabilities + */ +static void SRXAFSCB_GetCapabilities(struct work_struct *work) +{ + struct afs_interface *ifs; + struct afs_call *call = container_of(work, struct afs_call, work); + int loop, nifs; + + struct { + struct /* InterfaceAddr */ { + __be32 nifs; + __be32 uuid[11]; + __be32 ifaddr[32]; + __be32 netmask[32]; + __be32 mtu[32]; + } ia; + struct /* Capabilities */ { + __be32 capcount; + __be32 caps[1]; + } cap; + } reply; + + _enter(""); + + nifs = 0; + ifs = kcalloc(32, sizeof(*ifs), GFP_KERNEL); + if (ifs) { + nifs = afs_get_ipv4_interfaces(ifs, 32, false); + if (nifs < 0) { + kfree(ifs); + ifs = NULL; + nifs = 0; } - spin_unlock(&afscm_calls_lock); + } - if (removed) - rxrpc_put_call(call); - break; + memset(&reply, 0, sizeof(reply)); + reply.ia.nifs = htonl(nifs); + + reply.ia.uuid[0] = htonl(afs_uuid.time_low); + reply.ia.uuid[1] = htonl(afs_uuid.time_mid); + reply.ia.uuid[2] = htonl(afs_uuid.time_hi_and_version); + reply.ia.uuid[3] = htonl((s8) afs_uuid.clock_seq_hi_and_reserved); + reply.ia.uuid[4] = htonl((s8) afs_uuid.clock_seq_low); + for (loop = 0; loop < 6; loop++) + reply.ia.uuid[loop + 5] = htonl((s8) afs_uuid.node[loop]); + + if (ifs) { + for (loop = 0; loop < nifs; loop++) { + reply.ia.ifaddr[loop] = ifs[loop].address.s_addr; + reply.ia.netmask[loop] = ifs[loop].netmask.s_addr; + reply.ia.mtu[loop] = htonl(ifs[loop].mtu); + } + } - /* operation terminated on error */ - case RXRPC_CSTATE_ERROR: - call->app_user = NULL; - break; + reply.cap.capcount = htonl(1); + reply.cap.caps[0] = htonl(AFS_CAP_ERROR_TRANSLATION); + afs_send_simple_reply(call, &reply, sizeof(reply)); - default: - break; - } + _leave(""); +} - if (ret < 0) - rxrpc_call_abort(call, ret); +/* + * deliver request data to a CB.GetCapabilities call + */ +static int afs_deliver_cb_get_capabilities(struct afs_call *call, + struct sk_buff *skb, bool last) +{ + _enter(",{%u},%d", skb->len, last); - afs_put_server(server); + if (skb->len > 0) + return -EBADMSG; + if (!last) + return 0; - _leave(" = %d", ret); + /* no unmarshalling required */ + call->state = AFS_CALL_REPLYING; -} /* end _SRXAFSCM_Probe() */ + INIT_WORK(&call->work, SRXAFSCB_GetCapabilities); + schedule_work(&call->work); + return 0; +}