diff options
author | Bob Peterson <rpeterso@redhat.com> | 2016-02-05 14:39:02 -0500 |
---|---|---|
committer | David Teigland <teigland@redhat.com> | 2016-02-22 15:02:17 -0500 |
commit | b81171cb6869111dbaf9fb642f4434514c21d696 (patch) | |
tree | ee4a60628eae1e09006f50df50076b519f82306b | |
parent | 1a31833d085a339cf42573de0717209e8b2172e8 (diff) |
DLM: Save and restore socket callbacks properly
This patch fixes the problems with patch b3a5bbfd7.
1. It removes a return statement from lowcomms_error_report
because it needs to call the original error report in all paths
through the function.
2. All socket callbacks are saved and restored, not just the
sk_error_report, and that's done so with proper locking like
sunrpc does.
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: David Teigland <teigland@redhat.com>
-rw-r--r-- | fs/dlm/lowcomms.c | 70 |
1 files changed, 59 insertions, 11 deletions
diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c index dc9ae6d670dc..00640e70ed7a 100644 --- a/fs/dlm/lowcomms.c +++ b/fs/dlm/lowcomms.c | |||
@@ -124,7 +124,10 @@ struct connection { | |||
124 | struct connection *othercon; | 124 | struct connection *othercon; |
125 | struct work_struct rwork; /* Receive workqueue */ | 125 | struct work_struct rwork; /* Receive workqueue */ |
126 | struct work_struct swork; /* Send workqueue */ | 126 | struct work_struct swork; /* Send workqueue */ |
127 | void (*orig_error_report)(struct sock *sk); | 127 | void (*orig_error_report)(struct sock *); |
128 | void (*orig_data_ready)(struct sock *); | ||
129 | void (*orig_state_change)(struct sock *); | ||
130 | void (*orig_write_space)(struct sock *); | ||
128 | }; | 131 | }; |
129 | #define sock2con(x) ((struct connection *)(x)->sk_user_data) | 132 | #define sock2con(x) ((struct connection *)(x)->sk_user_data) |
130 | 133 | ||
@@ -467,10 +470,17 @@ int dlm_lowcomms_connect_node(int nodeid) | |||
467 | 470 | ||
468 | static void lowcomms_error_report(struct sock *sk) | 471 | static void lowcomms_error_report(struct sock *sk) |
469 | { | 472 | { |
470 | struct connection *con = sock2con(sk); | 473 | struct connection *con; |
471 | struct sockaddr_storage saddr; | 474 | struct sockaddr_storage saddr; |
472 | int buflen; | 475 | int buflen; |
476 | void (*orig_report)(struct sock *) = NULL; | ||
473 | 477 | ||
478 | read_lock_bh(&sk->sk_callback_lock); | ||
479 | con = sock2con(sk); | ||
480 | if (con == NULL) | ||
481 | goto out; | ||
482 | |||
483 | orig_report = con->orig_error_report; | ||
474 | if (con->sock == NULL || | 484 | if (con->sock == NULL || |
475 | kernel_getpeername(con->sock, (struct sockaddr *)&saddr, &buflen)) { | 485 | kernel_getpeername(con->sock, (struct sockaddr *)&saddr, &buflen)) { |
476 | printk_ratelimited(KERN_ERR "dlm: node %d: socket error " | 486 | printk_ratelimited(KERN_ERR "dlm: node %d: socket error " |
@@ -478,7 +488,6 @@ static void lowcomms_error_report(struct sock *sk) | |||
478 | "sk_err=%d/%d\n", dlm_our_nodeid(), | 488 | "sk_err=%d/%d\n", dlm_our_nodeid(), |
479 | con->nodeid, dlm_config.ci_tcp_port, | 489 | con->nodeid, dlm_config.ci_tcp_port, |
480 | sk->sk_err, sk->sk_err_soft); | 490 | sk->sk_err, sk->sk_err_soft); |
481 | return; | ||
482 | } else if (saddr.ss_family == AF_INET) { | 491 | } else if (saddr.ss_family == AF_INET) { |
483 | struct sockaddr_in *sin4 = (struct sockaddr_in *)&saddr; | 492 | struct sockaddr_in *sin4 = (struct sockaddr_in *)&saddr; |
484 | 493 | ||
@@ -501,22 +510,54 @@ static void lowcomms_error_report(struct sock *sk) | |||
501 | dlm_config.ci_tcp_port, sk->sk_err, | 510 | dlm_config.ci_tcp_port, sk->sk_err, |
502 | sk->sk_err_soft); | 511 | sk->sk_err_soft); |
503 | } | 512 | } |
504 | con->orig_error_report(sk); | 513 | out: |
514 | read_unlock_bh(&sk->sk_callback_lock); | ||
515 | if (orig_report) | ||
516 | orig_report(sk); | ||
517 | } | ||
518 | |||
519 | /* Note: sk_callback_lock must be locked before calling this function. */ | ||
520 | static void save_callbacks(struct connection *con, struct sock *sk) | ||
521 | { | ||
522 | lock_sock(sk); | ||
523 | con->orig_data_ready = sk->sk_data_ready; | ||
524 | con->orig_state_change = sk->sk_state_change; | ||
525 | con->orig_write_space = sk->sk_write_space; | ||
526 | con->orig_error_report = sk->sk_error_report; | ||
527 | release_sock(sk); | ||
528 | } | ||
529 | |||
530 | static void restore_callbacks(struct connection *con, struct sock *sk) | ||
531 | { | ||
532 | write_lock_bh(&sk->sk_callback_lock); | ||
533 | lock_sock(sk); | ||
534 | sk->sk_user_data = NULL; | ||
535 | sk->sk_data_ready = con->orig_data_ready; | ||
536 | sk->sk_state_change = con->orig_state_change; | ||
537 | sk->sk_write_space = con->orig_write_space; | ||
538 | sk->sk_error_report = con->orig_error_report; | ||
539 | release_sock(sk); | ||
540 | write_unlock_bh(&sk->sk_callback_lock); | ||
505 | } | 541 | } |
506 | 542 | ||
507 | /* Make a socket active */ | 543 | /* Make a socket active */ |
508 | static void add_sock(struct socket *sock, struct connection *con) | 544 | static void add_sock(struct socket *sock, struct connection *con) |
509 | { | 545 | { |
546 | struct sock *sk = sock->sk; | ||
547 | |||
548 | write_lock_bh(&sk->sk_callback_lock); | ||
510 | con->sock = sock; | 549 | con->sock = sock; |
511 | 550 | ||
551 | sk->sk_user_data = con; | ||
552 | if (!test_bit(CF_IS_OTHERCON, &con->flags)) | ||
553 | save_callbacks(con, sk); | ||
512 | /* Install a data_ready callback */ | 554 | /* Install a data_ready callback */ |
513 | con->sock->sk->sk_data_ready = lowcomms_data_ready; | 555 | sk->sk_data_ready = lowcomms_data_ready; |
514 | con->sock->sk->sk_write_space = lowcomms_write_space; | 556 | sk->sk_write_space = lowcomms_write_space; |
515 | con->sock->sk->sk_state_change = lowcomms_state_change; | 557 | sk->sk_state_change = lowcomms_state_change; |
516 | con->sock->sk->sk_user_data = con; | 558 | sk->sk_allocation = GFP_NOFS; |
517 | con->sock->sk->sk_allocation = GFP_NOFS; | 559 | sk->sk_error_report = lowcomms_error_report; |
518 | con->orig_error_report = con->sock->sk->sk_error_report; | 560 | write_unlock_bh(&sk->sk_callback_lock); |
519 | con->sock->sk->sk_error_report = lowcomms_error_report; | ||
520 | } | 561 | } |
521 | 562 | ||
522 | /* Add the port number to an IPv6 or 4 sockaddr and return the address | 563 | /* Add the port number to an IPv6 or 4 sockaddr and return the address |
@@ -551,6 +592,8 @@ static void close_connection(struct connection *con, bool and_other, | |||
551 | 592 | ||
552 | mutex_lock(&con->sock_mutex); | 593 | mutex_lock(&con->sock_mutex); |
553 | if (con->sock) { | 594 | if (con->sock) { |
595 | if (!test_bit(CF_IS_OTHERCON, &con->flags)) | ||
596 | restore_callbacks(con, con->sock->sk); | ||
554 | sock_release(con->sock); | 597 | sock_release(con->sock); |
555 | con->sock = NULL; | 598 | con->sock = NULL; |
556 | } | 599 | } |
@@ -1192,6 +1235,8 @@ static struct socket *tcp_create_listen_sock(struct connection *con, | |||
1192 | if (result < 0) { | 1235 | if (result < 0) { |
1193 | log_print("Failed to set SO_REUSEADDR on socket: %d", result); | 1236 | log_print("Failed to set SO_REUSEADDR on socket: %d", result); |
1194 | } | 1237 | } |
1238 | sock->sk->sk_user_data = con; | ||
1239 | |||
1195 | con->rx_action = tcp_accept_from_sock; | 1240 | con->rx_action = tcp_accept_from_sock; |
1196 | con->connect_action = tcp_connect_to_sock; | 1241 | con->connect_action = tcp_connect_to_sock; |
1197 | 1242 | ||
@@ -1273,6 +1318,7 @@ static int sctp_listen_for_all(void) | |||
1273 | if (result < 0) | 1318 | if (result < 0) |
1274 | log_print("Could not set SCTP NODELAY error %d\n", result); | 1319 | log_print("Could not set SCTP NODELAY error %d\n", result); |
1275 | 1320 | ||
1321 | write_lock_bh(&sock->sk->sk_callback_lock); | ||
1276 | /* Init con struct */ | 1322 | /* Init con struct */ |
1277 | sock->sk->sk_user_data = con; | 1323 | sock->sk->sk_user_data = con; |
1278 | con->sock = sock; | 1324 | con->sock = sock; |
@@ -1280,6 +1326,8 @@ static int sctp_listen_for_all(void) | |||
1280 | con->rx_action = sctp_accept_from_sock; | 1326 | con->rx_action = sctp_accept_from_sock; |
1281 | con->connect_action = sctp_connect_to_sock; | 1327 | con->connect_action = sctp_connect_to_sock; |
1282 | 1328 | ||
1329 | write_unlock_bh(&sock->sk->sk_callback_lock); | ||
1330 | |||
1283 | /* Bind to all addresses. */ | 1331 | /* Bind to all addresses. */ |
1284 | if (sctp_bind_addrs(con, dlm_config.ci_tcp_port)) | 1332 | if (sctp_bind_addrs(con, dlm_config.ci_tcp_port)) |
1285 | goto create_delsock; | 1333 | goto create_delsock; |