/* * perform processing on an asynchronous call * - on a multiple-thread workqueue this work item may try to run on several * CPUs at the same time */ static void afs_process_async_call(struct work_struct *work) { struct afs_call *call = container_of(work, struct afs_call, async_work); _enter(""); if (!skb_queue_empty(&call->rx_queue)) afs_deliver_to_call(call); if (call->state >= AFS_CALL_COMPLETE && call->wait_mode) { if (call->wait_mode->async_complete) call->wait_mode->async_complete(call->reply, call->error); call->reply = NULL; /* kill the call */ rxrpc_kernel_end_call(call->rxcall); call->rxcall = NULL; if (call->type->destructor) call->type->destructor(call); /* we can't just delete the call because the work item may be * queued */ call->async_workfn = afs_delete_async_call; queue_work(afs_async_calls, &call->async_work); } _leave(""); }
/* * Perform I/O processing on an asynchronous call. The work item carries a ref * to the call struct that we either need to release or to pass on. */ static void afs_process_async_call(struct work_struct *work) { struct afs_call *call = container_of(work, struct afs_call, async_work); _enter(""); if (call->state < AFS_CALL_COMPLETE && call->need_attention) { call->need_attention = false; afs_deliver_to_call(call); } if (call->state == AFS_CALL_COMPLETE) { call->reply[0] = NULL; /* We have two refs to release - one from the alloc and one * queued with the work item - and we can't just deallocate the * call because the work item may be queued again. */ call->async_work.func = afs_delete_async_call; if (!queue_work(afs_async_calls, &call->async_work)) afs_put_call(call); } afs_put_call(call); _leave(""); }
/* * wait synchronously for a call to complete */ static int afs_wait_for_call_to_complete(struct afs_call *call) { struct sk_buff *skb; int ret; DECLARE_WAITQUEUE(myself, current); _enter(""); add_wait_queue(&call->waitq, &myself); for (;;) { set_current_state(TASK_INTERRUPTIBLE); /* deliver any messages that are in the queue */ if (!skb_queue_empty(&call->rx_queue)) { __set_current_state(TASK_RUNNING); afs_deliver_to_call(call); continue; } ret = call->error; if (call->state >= AFS_CALL_COMPLETE) break; ret = -EINTR; if (signal_pending(current)) break; schedule(); } remove_wait_queue(&call->waitq, &myself); __set_current_state(TASK_RUNNING); /* kill the call */ if (call->state < AFS_CALL_COMPLETE) { _debug("call incomplete"); rxrpc_kernel_abort_call(call->rxcall, RX_CALL_DEAD); while ((skb = skb_dequeue(&call->rx_queue))) afs_free_skb(skb); } _debug("call complete"); rxrpc_kernel_end_call(call->rxcall); call->rxcall = NULL; call->type->destructor(call); afs_free_call(call); _leave(" = %d", ret); return ret; }
/* * perform processing on an asynchronous call * - on a multiple-thread workqueue this work item may try to run on several * CPUs at the same time */ static void afs_process_async_call(struct afs_call *call) { _enter(""); if (!skb_queue_empty(&call->rx_queue)) afs_deliver_to_call(call); if (call->state >= AFS_CALL_COMPLETE && call->wait_mode) { if (call->wait_mode->async_complete) call->wait_mode->async_complete(call->reply, call->error); call->reply = NULL; /* kill the call */ afs_end_call_nofree(call); /* we can't just delete the call because the work item may be * queued */ call->async_workfn = afs_delete_async_call; queue_work(afs_async_calls, &call->async_work); } _leave(""); }
/* * wait synchronously for a call to complete */ static long afs_wait_for_call_to_complete(struct afs_call *call, struct afs_addr_cursor *ac) { signed long rtt2, timeout; long ret; u64 rtt; u32 life, last_life; DECLARE_WAITQUEUE(myself, current); _enter(""); rtt = rxrpc_kernel_get_rtt(call->net->socket, call->rxcall); rtt2 = nsecs_to_jiffies64(rtt) * 2; if (rtt2 < 2) rtt2 = 2; timeout = rtt2; last_life = rxrpc_kernel_check_life(call->net->socket, call->rxcall); add_wait_queue(&call->waitq, &myself); for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); /* deliver any messages that are in the queue */ if (!afs_check_call_state(call, AFS_CALL_COMPLETE) && call->need_attention) { call->need_attention = false; __set_current_state(TASK_RUNNING); afs_deliver_to_call(call); continue; } if (afs_check_call_state(call, AFS_CALL_COMPLETE)) break; life = rxrpc_kernel_check_life(call->net->socket, call->rxcall); if (timeout == 0 && life == last_life && signal_pending(current)) break; if (life != last_life) { timeout = rtt2; last_life = life; } timeout = schedule_timeout(timeout); } remove_wait_queue(&call->waitq, &myself); __set_current_state(TASK_RUNNING); /* Kill off the call if it's still live. */ if (!afs_check_call_state(call, AFS_CALL_COMPLETE)) { _debug("call interrupted"); if (rxrpc_kernel_abort_call(call->net->socket, call->rxcall, RX_USER_ABORT, -EINTR, "KWI")) afs_set_call_complete(call, -EINTR, 0); } spin_lock_bh(&call->state_lock); ac->abort_code = call->abort_code; ac->error = call->error; spin_unlock_bh(&call->state_lock); ret = ac->error; switch (ret) { case 0: if (call->ret_reply0) { ret = (long)call->reply[0]; call->reply[0] = NULL; } /* Fall through */ case -ECONNABORTED: ac->responded = true; break; } _debug("call complete"); afs_put_call(call); _leave(" = %p", (void *)ret); return ret; }