aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2016-04-04 09:00:38 -0400
committerDavid Howells <dhowells@redhat.com>2016-07-06 05:43:51 -0400
commite653cfe49cec540529217933e07caf6c0f25ac93 (patch)
tree455d3e42c41388b7ebed523ed9b701db1ee0aeb9
parentd1e858c5a392a50c16ce36624203032bdeb3595b (diff)
rxrpc: Release a call's connection ref on call disconnection
When a call is disconnected, clear the call's pointer to the connection and release the associated ref on that connection. This means that the call no longer pins the connection and the connection can be discarded even before the call is. As the code currently stands, the call struct is effectively pinned by userspace until userspace has enacted a recvmsg() to retrieve the final call state as sk_buffs on the receive queue pin the call to which they're related because: (1) The rxrpc_call struct contains the userspace ID that recvmsg() has to include in the control message buffer to indicate which call is being referred to. This ID must remain valid until the terminal packet is completely read and must be invalidated immediately at that point as userspace is entitled to immediately reuse it. (2) The final ACK to the reply to a client call isn't sent until the last data packet is entirely read (it's probably worth altering this in future to be send the ACK as soon as all the data has been received). This change requires a bit of rearrangement to make sure that the call isn't going to try and access the connection again after protocol completion: (1) Delete the error link earlier when we're releasing the call. Possibly network errors should be distributed via connections at the cost of adding in an access to the rxrpc_connection struct. (2) Remove the call from the connection's call tree before disconnecting the call. The call tree needs to be removed anyway and incoming packets delivered by channel pointer instead. (3) The release call event should be considered last after all other events have been processed so that we don't need access to the connection again. (4) Move the channel_lock taking from rxrpc_release_call() to rxrpc_disconnect_call() where it will be required in future. Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r--net/rxrpc/call_event.c10
-rw-r--r--net/rxrpc/call_object.c26
-rw-r--r--net/rxrpc/conn_object.c8
3 files changed, 22 insertions, 22 deletions
diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
index 0ba84295f913..638d66df284a 100644
--- a/net/rxrpc/call_event.c
+++ b/net/rxrpc/call_event.c
@@ -858,11 +858,6 @@ void rxrpc_process_call(struct work_struct *work)
858 iov[0].iov_len = sizeof(whdr); 858 iov[0].iov_len = sizeof(whdr);
859 859
860 /* deal with events of a final nature */ 860 /* deal with events of a final nature */
861 if (test_bit(RXRPC_CALL_EV_RELEASE, &call->events)) {
862 rxrpc_release_call(call);
863 clear_bit(RXRPC_CALL_EV_RELEASE, &call->events);
864 }
865
866 if (test_bit(RXRPC_CALL_EV_RCVD_ERROR, &call->events)) { 861 if (test_bit(RXRPC_CALL_EV_RCVD_ERROR, &call->events)) {
867 enum rxrpc_skb_mark mark; 862 enum rxrpc_skb_mark mark;
868 int error; 863 int error;
@@ -1144,6 +1139,11 @@ void rxrpc_process_call(struct work_struct *work)
1144 goto maybe_reschedule; 1139 goto maybe_reschedule;
1145 } 1140 }
1146 1141
1142 if (test_bit(RXRPC_CALL_EV_RELEASE, &call->events)) {
1143 rxrpc_release_call(call);
1144 clear_bit(RXRPC_CALL_EV_RELEASE, &call->events);
1145 }
1146
1147 /* other events may have been raised since we started checking */ 1147 /* other events may have been raised since we started checking */
1148 goto maybe_reschedule; 1148 goto maybe_reschedule;
1149 1149
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c
index 6223a7ed831f..b43d89c89744 100644
--- a/net/rxrpc/call_object.c
+++ b/net/rxrpc/call_object.c
@@ -628,6 +628,10 @@ void rxrpc_release_call(struct rxrpc_call *call)
628 */ 628 */
629 _debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn); 629 _debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn);
630 630
631 spin_lock(&conn->params.peer->lock);
632 hlist_del_init(&call->error_link);
633 spin_unlock(&conn->params.peer->lock);
634
631 write_lock_bh(&rx->call_lock); 635 write_lock_bh(&rx->call_lock);
632 if (!list_empty(&call->accept_link)) { 636 if (!list_empty(&call->accept_link)) {
633 _debug("unlinking once-pending call %p { e=%lx f=%lx }", 637 _debug("unlinking once-pending call %p { e=%lx f=%lx }",
@@ -643,25 +647,22 @@ void rxrpc_release_call(struct rxrpc_call *call)
643 write_unlock_bh(&rx->call_lock); 647 write_unlock_bh(&rx->call_lock);
644 648
645 /* free up the channel for reuse */ 649 /* free up the channel for reuse */
646 spin_lock(&conn->channel_lock);
647 write_lock_bh(&conn->lock); 650 write_lock_bh(&conn->lock);
648 write_lock(&call->state_lock); 651 write_lock(&call->state_lock);
649 652
650 rxrpc_disconnect_call(call);
651
652 spin_unlock(&conn->channel_lock);
653
654 if (call->state < RXRPC_CALL_COMPLETE && 653 if (call->state < RXRPC_CALL_COMPLETE &&
655 call->state != RXRPC_CALL_CLIENT_FINAL_ACK) { 654 call->state != RXRPC_CALL_CLIENT_FINAL_ACK) {
656 _debug("+++ ABORTING STATE %d +++\n", call->state); 655 _debug("+++ ABORTING STATE %d +++\n", call->state);
657 call->state = RXRPC_CALL_LOCALLY_ABORTED; 656 call->state = RXRPC_CALL_LOCALLY_ABORTED;
658 call->local_abort = RX_CALL_DEAD; 657 call->local_abort = RX_CALL_DEAD;
659 set_bit(RXRPC_CALL_EV_ABORT, &call->events);
660 rxrpc_queue_call(call);
661 } 658 }
662 write_unlock(&call->state_lock); 659 write_unlock(&call->state_lock);
660
661 rb_erase(&call->conn_node, &conn->calls);
663 write_unlock_bh(&conn->lock); 662 write_unlock_bh(&conn->lock);
664 663
664 rxrpc_disconnect_call(call);
665
665 /* clean up the Rx queue */ 666 /* clean up the Rx queue */
666 if (!skb_queue_empty(&call->rx_queue) || 667 if (!skb_queue_empty(&call->rx_queue) ||
667 !skb_queue_empty(&call->rx_oos_queue)) { 668 !skb_queue_empty(&call->rx_oos_queue)) {
@@ -817,16 +818,7 @@ static void rxrpc_cleanup_call(struct rxrpc_call *call)
817 return; 818 return;
818 } 819 }
819 820
820 if (call->conn) { 821 ASSERTCMP(call->conn, ==, NULL);
821 spin_lock(&call->conn->params.peer->lock);
822 hlist_del_init(&call->error_link);
823 spin_unlock(&call->conn->params.peer->lock);
824
825 write_lock_bh(&call->conn->lock);
826 rb_erase(&call->conn_node, &call->conn->calls);
827 write_unlock_bh(&call->conn->lock);
828 rxrpc_put_connection(call->conn);
829 }
830 822
831 /* Remove the call from the hash */ 823 /* Remove the call from the hash */
832 rxrpc_call_hash_del(call); 824 rxrpc_call_hash_del(call);
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index 0e022dfab034..99d18107421f 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -540,11 +540,19 @@ void rxrpc_disconnect_call(struct rxrpc_call *call)
540 540
541 _enter("%d,%d", conn->debug_id, call->channel); 541 _enter("%d,%d", conn->debug_id, call->channel);
542 542
543 spin_lock(&conn->channel_lock);
544
543 if (conn->channels[chan] == call) { 545 if (conn->channels[chan] == call) {
544 rcu_assign_pointer(conn->channels[chan], NULL); 546 rcu_assign_pointer(conn->channels[chan], NULL);
545 atomic_inc(&conn->avail_chans); 547 atomic_inc(&conn->avail_chans);
546 wake_up(&conn->channel_wq); 548 wake_up(&conn->channel_wq);
547 } 549 }
550
551 spin_unlock(&conn->channel_lock);
552
553 call->conn = NULL;
554 rxrpc_put_connection(conn);
555 _leave("");
548} 556}
549 557
550/* 558/*