diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/rxrpc/ar-internal.h | 5 | ||||
| -rw-r--r-- | net/rxrpc/conn_client.c | 42 | ||||
| -rw-r--r-- | net/rxrpc/conn_object.c | 74 | ||||
| -rw-r--r-- | net/rxrpc/conn_service.c | 34 |
4 files changed, 97 insertions, 58 deletions
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index ad48f851b40c..d8e4d6e6a030 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h | |||
| @@ -258,6 +258,8 @@ struct rxrpc_conn_parameters { | |||
| 258 | */ | 258 | */ |
| 259 | enum rxrpc_conn_flag { | 259 | enum rxrpc_conn_flag { |
| 260 | RXRPC_CONN_HAS_IDR, /* Has a client conn ID assigned */ | 260 | RXRPC_CONN_HAS_IDR, /* Has a client conn ID assigned */ |
| 261 | RXRPC_CONN_IN_SERVICE_CONNS, /* Conn is in peer->service_conns */ | ||
| 262 | RXRPC_CONN_IN_CLIENT_CONNS, /* Conn is in local->client_conns */ | ||
| 261 | }; | 263 | }; |
| 262 | 264 | ||
| 263 | /* | 265 | /* |
| @@ -544,10 +546,10 @@ void __exit rxrpc_destroy_all_calls(void); | |||
| 544 | */ | 546 | */ |
| 545 | extern struct idr rxrpc_client_conn_ids; | 547 | extern struct idr rxrpc_client_conn_ids; |
| 546 | 548 | ||
| 547 | void rxrpc_put_client_connection_id(struct rxrpc_connection *); | ||
| 548 | void rxrpc_destroy_client_conn_ids(void); | 549 | void rxrpc_destroy_client_conn_ids(void); |
| 549 | int rxrpc_connect_call(struct rxrpc_call *, struct rxrpc_conn_parameters *, | 550 | int rxrpc_connect_call(struct rxrpc_call *, struct rxrpc_conn_parameters *, |
| 550 | struct sockaddr_rxrpc *, gfp_t); | 551 | struct sockaddr_rxrpc *, gfp_t); |
| 552 | void rxrpc_unpublish_client_conn(struct rxrpc_connection *); | ||
| 551 | 553 | ||
| 552 | /* | 554 | /* |
| 553 | * conn_event.c | 555 | * conn_event.c |
| @@ -609,6 +611,7 @@ static inline void rxrpc_queue_conn(struct rxrpc_connection *conn) | |||
| 609 | struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *, | 611 | struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *, |
| 610 | struct sockaddr_rxrpc *, | 612 | struct sockaddr_rxrpc *, |
| 611 | struct sk_buff *); | 613 | struct sk_buff *); |
| 614 | void rxrpc_unpublish_service_conn(struct rxrpc_connection *); | ||
| 612 | 615 | ||
| 613 | /* | 616 | /* |
| 614 | * input.c | 617 | * input.c |
diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 9180164a51aa..aa21462f3236 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c | |||
| @@ -84,7 +84,7 @@ error: | |||
| 84 | /* | 84 | /* |
| 85 | * Release a connection ID for a client connection from the global pool. | 85 | * Release a connection ID for a client connection from the global pool. |
| 86 | */ | 86 | */ |
| 87 | void rxrpc_put_client_connection_id(struct rxrpc_connection *conn) | 87 | static void rxrpc_put_client_connection_id(struct rxrpc_connection *conn) |
| 88 | { | 88 | { |
| 89 | if (test_bit(RXRPC_CONN_HAS_IDR, &conn->flags)) { | 89 | if (test_bit(RXRPC_CONN_HAS_IDR, &conn->flags)) { |
| 90 | spin_lock(&rxrpc_conn_id_lock); | 90 | spin_lock(&rxrpc_conn_id_lock); |
| @@ -278,12 +278,13 @@ int rxrpc_connect_call(struct rxrpc_call *call, | |||
| 278 | * lock before dropping the client conn lock. | 278 | * lock before dropping the client conn lock. |
| 279 | */ | 279 | */ |
| 280 | _debug("new conn"); | 280 | _debug("new conn"); |
| 281 | set_bit(RXRPC_CONN_IN_CLIENT_CONNS, &candidate->flags); | ||
| 282 | rb_link_node(&candidate->client_node, parent, pp); | ||
| 283 | rb_insert_color(&candidate->client_node, &local->client_conns); | ||
| 284 | attached: | ||
| 281 | conn = candidate; | 285 | conn = candidate; |
| 282 | candidate = NULL; | 286 | candidate = NULL; |
| 283 | 287 | ||
| 284 | rb_link_node(&conn->client_node, parent, pp); | ||
| 285 | rb_insert_color(&conn->client_node, &local->client_conns); | ||
| 286 | |||
| 287 | atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1); | 288 | atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1); |
| 288 | spin_lock(&conn->channel_lock); | 289 | spin_lock(&conn->channel_lock); |
| 289 | spin_unlock(&local->client_conns_lock); | 290 | spin_unlock(&local->client_conns_lock); |
| @@ -307,13 +308,22 @@ found_channel: | |||
| 307 | _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); | 308 | _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); |
| 308 | return 0; | 309 | return 0; |
| 309 | 310 | ||
| 310 | /* We found a suitable connection already in existence. Discard any | 311 | /* We found a potentially suitable connection already in existence. If |
| 311 | * candidate we may have allocated, and try to get a channel on this | 312 | * we can reuse it (ie. its usage count hasn't been reduced to 0 by the |
| 312 | * one. | 313 | * reaper), discard any candidate we may have allocated, and try to get |
| 314 | * a channel on this one, otherwise we have to replace it. | ||
| 313 | */ | 315 | */ |
| 314 | found_extant_conn: | 316 | found_extant_conn: |
| 315 | _debug("found conn"); | 317 | _debug("found conn"); |
| 316 | rxrpc_get_connection(conn); | 318 | if (!rxrpc_get_connection_maybe(conn)) { |
| 319 | set_bit(RXRPC_CONN_IN_CLIENT_CONNS, &candidate->flags); | ||
| 320 | rb_replace_node(&conn->client_node, | ||
| 321 | &candidate->client_node, | ||
| 322 | &local->client_conns); | ||
| 323 | clear_bit(RXRPC_CONN_IN_CLIENT_CONNS, &conn->flags); | ||
| 324 | goto attached; | ||
| 325 | } | ||
| 326 | |||
| 317 | spin_unlock(&local->client_conns_lock); | 327 | spin_unlock(&local->client_conns_lock); |
| 318 | 328 | ||
| 319 | rxrpc_put_connection(candidate); | 329 | rxrpc_put_connection(candidate); |
| @@ -357,3 +367,19 @@ interrupted: | |||
| 357 | _leave(" = -ERESTARTSYS"); | 367 | _leave(" = -ERESTARTSYS"); |
| 358 | return -ERESTARTSYS; | 368 | return -ERESTARTSYS; |
| 359 | } | 369 | } |
| 370 | |||
| 371 | /* | ||
| 372 | * Remove a client connection from the local endpoint's tree, thereby removing | ||
| 373 | * it as a target for reuse for new client calls. | ||
| 374 | */ | ||
| 375 | void rxrpc_unpublish_client_conn(struct rxrpc_connection *conn) | ||
| 376 | { | ||
| 377 | struct rxrpc_local *local = conn->params.local; | ||
| 378 | |||
| 379 | spin_lock(&local->client_conns_lock); | ||
| 380 | if (test_and_clear_bit(RXRPC_CONN_IN_CLIENT_CONNS, &conn->flags)) | ||
| 381 | rb_erase(&conn->client_node, &local->client_conns); | ||
| 382 | spin_unlock(&local->client_conns_lock); | ||
| 383 | |||
| 384 | rxrpc_put_client_connection_id(conn); | ||
| 385 | } | ||
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 8379e3748d13..89bc6480b4e2 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c | |||
| @@ -49,7 +49,10 @@ struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) | |||
| 49 | skb_queue_head_init(&conn->rx_queue); | 49 | skb_queue_head_init(&conn->rx_queue); |
| 50 | conn->security = &rxrpc_no_security; | 50 | conn->security = &rxrpc_no_security; |
| 51 | spin_lock_init(&conn->state_lock); | 51 | spin_lock_init(&conn->state_lock); |
| 52 | atomic_set(&conn->usage, 1); | 52 | /* We maintain an extra ref on the connection whilst it is |
| 53 | * on the rxrpc_connections list. | ||
| 54 | */ | ||
| 55 | atomic_set(&conn->usage, 2); | ||
| 53 | conn->debug_id = atomic_inc_return(&rxrpc_debug_id); | 56 | conn->debug_id = atomic_inc_return(&rxrpc_debug_id); |
| 54 | atomic_set(&conn->avail_chans, RXRPC_MAXCALLS); | 57 | atomic_set(&conn->avail_chans, RXRPC_MAXCALLS); |
| 55 | conn->size_align = 4; | 58 | conn->size_align = 4; |
| @@ -111,7 +114,7 @@ struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local, | |||
| 111 | return NULL; | 114 | return NULL; |
| 112 | 115 | ||
| 113 | found: | 116 | found: |
| 114 | rxrpc_get_connection(conn); | 117 | conn = rxrpc_get_connection_maybe(conn); |
| 115 | read_unlock_bh(&peer->conn_lock); | 118 | read_unlock_bh(&peer->conn_lock); |
| 116 | _leave(" = %p", conn); | 119 | _leave(" = %p", conn); |
| 117 | return conn; | 120 | return conn; |
| @@ -173,10 +176,10 @@ void rxrpc_put_connection(struct rxrpc_connection *conn) | |||
| 173 | _enter("%p{u=%d,d=%d}", | 176 | _enter("%p{u=%d,d=%d}", |
| 174 | conn, atomic_read(&conn->usage), conn->debug_id); | 177 | conn, atomic_read(&conn->usage), conn->debug_id); |
| 175 | 178 | ||
| 176 | ASSERTCMP(atomic_read(&conn->usage), >, 0); | 179 | ASSERTCMP(atomic_read(&conn->usage), >, 1); |
| 177 | 180 | ||
| 178 | conn->put_time = ktime_get_seconds(); | 181 | conn->put_time = ktime_get_seconds(); |
| 179 | if (atomic_dec_and_test(&conn->usage)) { | 182 | if (atomic_dec_return(&conn->usage) == 1) { |
| 180 | _debug("zombie"); | 183 | _debug("zombie"); |
| 181 | rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); | 184 | rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); |
| 182 | } | 185 | } |
| @@ -216,59 +219,41 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu) | |||
| 216 | static void rxrpc_connection_reaper(struct work_struct *work) | 219 | static void rxrpc_connection_reaper(struct work_struct *work) |
| 217 | { | 220 | { |
| 218 | struct rxrpc_connection *conn, *_p; | 221 | struct rxrpc_connection *conn, *_p; |
| 219 | struct rxrpc_peer *peer; | 222 | unsigned long reap_older_than, earliest, put_time, now; |
| 220 | unsigned long now, earliest, reap_time; | ||
| 221 | 223 | ||
| 222 | LIST_HEAD(graveyard); | 224 | LIST_HEAD(graveyard); |
| 223 | 225 | ||
| 224 | _enter(""); | 226 | _enter(""); |
| 225 | 227 | ||
| 226 | now = ktime_get_seconds(); | 228 | now = ktime_get_seconds(); |
| 229 | reap_older_than = now - rxrpc_connection_expiry; | ||
| 227 | earliest = ULONG_MAX; | 230 | earliest = ULONG_MAX; |
| 228 | 231 | ||
| 229 | write_lock(&rxrpc_connection_lock); | 232 | write_lock(&rxrpc_connection_lock); |
| 230 | list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { | 233 | list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { |
| 231 | _debug("reap CONN %d { u=%d,t=%ld }", | 234 | ASSERTCMP(atomic_read(&conn->usage), >, 0); |
| 232 | conn->debug_id, atomic_read(&conn->usage), | 235 | if (likely(atomic_read(&conn->usage) > 1)) |
| 233 | (long) now - (long) conn->put_time); | ||
| 234 | |||
| 235 | if (likely(atomic_read(&conn->usage) > 0)) | ||
| 236 | continue; | 236 | continue; |
| 237 | 237 | ||
| 238 | if (rxrpc_conn_is_client(conn)) { | 238 | put_time = READ_ONCE(conn->put_time); |
| 239 | struct rxrpc_local *local = conn->params.local; | 239 | if (time_after(put_time, reap_older_than)) { |
| 240 | spin_lock(&local->client_conns_lock); | 240 | if (time_before(put_time, earliest)) |
| 241 | reap_time = conn->put_time + rxrpc_connection_expiry; | 241 | earliest = put_time; |
| 242 | 242 | continue; | |
| 243 | if (atomic_read(&conn->usage) > 0) { | ||
| 244 | ; | ||
| 245 | } else if (reap_time <= now) { | ||
| 246 | list_move_tail(&conn->link, &graveyard); | ||
| 247 | rxrpc_put_client_connection_id(conn); | ||
| 248 | rb_erase(&conn->client_node, | ||
| 249 | &local->client_conns); | ||
| 250 | } else if (reap_time < earliest) { | ||
| 251 | earliest = reap_time; | ||
| 252 | } | ||
| 253 | |||
| 254 | spin_unlock(&local->client_conns_lock); | ||
| 255 | } else { | ||
| 256 | peer = conn->params.peer; | ||
| 257 | write_lock_bh(&peer->conn_lock); | ||
| 258 | reap_time = conn->put_time + rxrpc_connection_expiry; | ||
| 259 | |||
| 260 | if (atomic_read(&conn->usage) > 0) { | ||
| 261 | ; | ||
| 262 | } else if (reap_time <= now) { | ||
| 263 | list_move_tail(&conn->link, &graveyard); | ||
| 264 | rb_erase(&conn->service_node, | ||
| 265 | &peer->service_conns); | ||
| 266 | } else if (reap_time < earliest) { | ||
| 267 | earliest = reap_time; | ||
| 268 | } | ||
| 269 | |||
| 270 | write_unlock_bh(&peer->conn_lock); | ||
| 271 | } | 243 | } |
| 244 | |||
| 245 | /* The usage count sits at 1 whilst the object is unused on the | ||
| 246 | * list; we reduce that to 0 to make the object unavailable. | ||
| 247 | */ | ||
| 248 | if (atomic_cmpxchg(&conn->usage, 1, 0) != 1) | ||
| 249 | continue; | ||
| 250 | |||
| 251 | if (rxrpc_conn_is_client(conn)) | ||
| 252 | rxrpc_unpublish_client_conn(conn); | ||
| 253 | else | ||
| 254 | rxrpc_unpublish_service_conn(conn); | ||
| 255 | |||
| 256 | list_move_tail(&conn->link, &graveyard); | ||
| 272 | } | 257 | } |
| 273 | write_unlock(&rxrpc_connection_lock); | 258 | write_unlock(&rxrpc_connection_lock); |
| 274 | 259 | ||
| @@ -279,7 +264,6 @@ static void rxrpc_connection_reaper(struct work_struct *work) | |||
| 279 | (earliest - now) * HZ); | 264 | (earliest - now) * HZ); |
| 280 | } | 265 | } |
| 281 | 266 | ||
| 282 | /* then destroy all those pulled out */ | ||
| 283 | while (!list_empty(&graveyard)) { | 267 | while (!list_empty(&graveyard)) { |
| 284 | conn = list_entry(graveyard.next, struct rxrpc_connection, | 268 | conn = list_entry(graveyard.next, struct rxrpc_connection, |
| 285 | link); | 269 | link); |
diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index a42b210c40a5..77a509e6003a 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c | |||
| @@ -104,10 +104,12 @@ struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *local, | |||
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | /* we can now add the new candidate to the list */ | 106 | /* we can now add the new candidate to the list */ |
| 107 | set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &candidate->flags); | ||
| 108 | rb_link_node(&candidate->service_node, p, pp); | ||
| 109 | rb_insert_color(&candidate->service_node, &peer->service_conns); | ||
| 110 | attached: | ||
| 107 | conn = candidate; | 111 | conn = candidate; |
| 108 | candidate = NULL; | 112 | candidate = NULL; |
| 109 | rb_link_node(&conn->service_node, p, pp); | ||
| 110 | rb_insert_color(&conn->service_node, &peer->service_conns); | ||
| 111 | rxrpc_get_peer(peer); | 113 | rxrpc_get_peer(peer); |
| 112 | rxrpc_get_local(local); | 114 | rxrpc_get_local(local); |
| 113 | 115 | ||
| @@ -128,11 +130,19 @@ success: | |||
| 128 | 130 | ||
| 129 | /* we found the connection in the list immediately */ | 131 | /* we found the connection in the list immediately */ |
| 130 | found_extant_connection: | 132 | found_extant_connection: |
| 133 | if (!rxrpc_get_connection_maybe(conn)) { | ||
| 134 | set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &candidate->flags); | ||
| 135 | rb_replace_node(&conn->service_node, | ||
| 136 | &candidate->service_node, | ||
| 137 | &peer->service_conns); | ||
| 138 | clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags); | ||
| 139 | goto attached; | ||
| 140 | } | ||
| 141 | |||
| 131 | if (sp->hdr.securityIndex != conn->security_ix) { | 142 | if (sp->hdr.securityIndex != conn->security_ix) { |
| 132 | read_unlock_bh(&peer->conn_lock); | 143 | read_unlock_bh(&peer->conn_lock); |
| 133 | goto security_mismatch; | 144 | goto security_mismatch_put; |
| 134 | } | 145 | } |
| 135 | rxrpc_get_connection(conn); | ||
| 136 | read_unlock_bh(&peer->conn_lock); | 146 | read_unlock_bh(&peer->conn_lock); |
| 137 | goto success; | 147 | goto success; |
| 138 | 148 | ||
| @@ -147,8 +157,24 @@ found_extant_second: | |||
| 147 | kfree(candidate); | 157 | kfree(candidate); |
| 148 | goto success; | 158 | goto success; |
| 149 | 159 | ||
| 160 | security_mismatch_put: | ||
| 161 | rxrpc_put_connection(conn); | ||
| 150 | security_mismatch: | 162 | security_mismatch: |
| 151 | kfree(candidate); | 163 | kfree(candidate); |
| 152 | _leave(" = -EKEYREJECTED"); | 164 | _leave(" = -EKEYREJECTED"); |
| 153 | return ERR_PTR(-EKEYREJECTED); | 165 | return ERR_PTR(-EKEYREJECTED); |
| 154 | } | 166 | } |
| 167 | |||
| 168 | /* | ||
| 169 | * Remove the service connection from the peer's tree, thereby removing it as a | ||
| 170 | * target for incoming packets. | ||
| 171 | */ | ||
| 172 | void rxrpc_unpublish_service_conn(struct rxrpc_connection *conn) | ||
| 173 | { | ||
| 174 | struct rxrpc_peer *peer = conn->params.peer; | ||
| 175 | |||
| 176 | write_lock_bh(&peer->conn_lock); | ||
| 177 | if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags)) | ||
| 178 | rb_erase(&conn->service_node, &peer->service_conns); | ||
| 179 | write_unlock_bh(&peer->conn_lock); | ||
| 180 | } | ||
