aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2016-09-13 04:05:14 -0400
committerDavid Howells <dhowells@redhat.com>2016-09-13 17:38:37 -0400
commit3432a757b1f889f8c0d33cd9fcabdae172ed812b (patch)
treedd17c676654c1d8f37f39619bd2ab89acfae8643
parentcbd00891de9bb4756bac6f6edfa945d5a6468977 (diff)
rxrpc: Fix prealloc refcounting
The preallocated call buffer holds a ref on the calls within that buffer. The ref was being released in the wrong place - it worked okay for incoming calls to the AFS cache manager service, but doesn't work right for incoming calls to a userspace service. Instead of releasing an extra ref service calls in rxrpc_release_call(), the ref needs to be released during the acceptance/rejectance process. To this end: (1) The prealloc ref is now normally released during rxrpc_new_incoming_call(). (2) For preallocated kernel API calls, the kernel API's ref needs to be released when the call is discarded on socket close. (3) We shouldn't take a second ref in rxrpc_accept_call(). (4) rxrpc_recvmsg_new_call() needs to get a ref of its own when it adds the call to the to_be_accepted socket queue. In doing (4) above, we would prefer not to put the call's refcount down to 0 as that entails doing cleanup in softirq context, but it's unlikely as there are several refs held elsewhere, at least one of which must be put by someone in process context calling rxrpc_release_call(). However, it's not a problem if we do have to do that. Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r--net/rxrpc/call_accept.c9
-rw-r--r--net/rxrpc/call_object.c3
-rw-r--r--net/rxrpc/recvmsg.c1
3 files changed, 9 insertions, 4 deletions
diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c
index 5fd9d2c89b7f..26c293ef98eb 100644
--- a/net/rxrpc/call_accept.c
+++ b/net/rxrpc/call_accept.c
@@ -221,6 +221,7 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx)
221 if (rx->discard_new_call) { 221 if (rx->discard_new_call) {
222 _debug("discard %lx", call->user_call_ID); 222 _debug("discard %lx", call->user_call_ID);
223 rx->discard_new_call(call, call->user_call_ID); 223 rx->discard_new_call(call, call->user_call_ID);
224 rxrpc_put_call(call, rxrpc_call_put_kernel);
224 } 225 }
225 rxrpc_call_completed(call); 226 rxrpc_call_completed(call);
226 rxrpc_release_call(rx, call); 227 rxrpc_release_call(rx, call);
@@ -402,6 +403,13 @@ found_service:
402 if (call->state == RXRPC_CALL_SERVER_ACCEPTING) 403 if (call->state == RXRPC_CALL_SERVER_ACCEPTING)
403 rxrpc_notify_socket(call); 404 rxrpc_notify_socket(call);
404 405
406 /* We have to discard the prealloc queue's ref here and rely on a
407 * combination of the RCU read lock and refs held either by the socket
408 * (recvmsg queue, to-be-accepted queue or user ID tree) or the kernel
409 * service to prevent the call from being deallocated too early.
410 */
411 rxrpc_put_call(call, rxrpc_call_put);
412
405 _leave(" = %p{%d}", call, call->debug_id); 413 _leave(" = %p{%d}", call, call->debug_id);
406out: 414out:
407 spin_unlock(&rx->incoming_lock); 415 spin_unlock(&rx->incoming_lock);
@@ -469,7 +477,6 @@ struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *rx,
469 } 477 }
470 478
471 /* formalise the acceptance */ 479 /* formalise the acceptance */
472 rxrpc_get_call(call, rxrpc_call_got);
473 call->notify_rx = notify_rx; 480 call->notify_rx = notify_rx;
474 call->user_call_ID = user_call_ID; 481 call->user_call_ID = user_call_ID;
475 rxrpc_get_call(call, rxrpc_call_got_userid); 482 rxrpc_get_call(call, rxrpc_call_got_userid);
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c
index 3f9476508204..9aa1c4b53563 100644
--- a/net/rxrpc/call_object.c
+++ b/net/rxrpc/call_object.c
@@ -464,9 +464,6 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
464 call->rxtx_buffer[i] = NULL; 464 call->rxtx_buffer[i] = NULL;
465 } 465 }
466 466
467 /* We have to release the prealloc backlog ref */
468 if (rxrpc_is_service_call(call))
469 rxrpc_put_call(call, rxrpc_call_put);
470 _leave(""); 467 _leave("");
471} 468}
472 469
diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
index 16ff56f69256..a284205b8ecf 100644
--- a/net/rxrpc/recvmsg.c
+++ b/net/rxrpc/recvmsg.c
@@ -118,6 +118,7 @@ static int rxrpc_recvmsg_new_call(struct rxrpc_sock *rx,
118 list_del_init(&call->recvmsg_link); 118 list_del_init(&call->recvmsg_link);
119 write_unlock_bh(&rx->recvmsg_lock); 119 write_unlock_bh(&rx->recvmsg_lock);
120 120
121 rxrpc_get_call(call, rxrpc_call_got);
121 write_lock(&rx->call_lock); 122 write_lock(&rx->call_lock);
122 list_add_tail(&call->accept_link, &rx->to_be_accepted); 123 list_add_tail(&call->accept_link, &rx->to_be_accepted);
123 write_unlock(&rx->call_lock); 124 write_unlock(&rx->call_lock);