diff options
author | Jon Paul Maloy <jon.maloy@ericsson.com> | 2014-02-13 17:29:18 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-02-13 17:57:07 -0500 |
commit | e099e86c9e24fe9aff36773600543eb31d8954d1 (patch) | |
tree | 723936589e12e2bdcbf6aa8b702122b7488cec5e /net/tipc | |
parent | a83045292daf9f07d0b103e5715ef527123d2fcc (diff) |
tipc: add node_lock protection to link lookup function
In an earlier commit, ("tipc: remove links list from bearer struct")
we described three issues that need to be pre-emptively resolved before
we can remove tipc_net_lock. Here we resolve issue a) described in that
commit:
"a) In access method #2, we access the link before taking the
protecting node_lock. This will not work once net_lock is gone,
so we will have to change the access order. We will deal with
this in a later commit in this series."
Here, we change that access order, by ensuring that the function
link_find_link() returns only a safe reference for finding
the link, i.e., a node pointer and an index into its 'links' array,
not the link pointer itself. We also change all callers of this
function to first take the node lock before they can check if there
still is a valid link pointer at the returned index. Since the
function now returns a node pointer rather than a link pointer,
we rename it to the more appropriate 'tipc_link_find_owner().
Signed-off-by: Jon Maloy <jon.maloy@ericsson.com>
Reviewed-by: Ying Xue <ying.xue@windriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc')
-rw-r--r-- | net/tipc/link.c | 110 |
1 files changed, 65 insertions, 45 deletions
diff --git a/net/tipc/link.c b/net/tipc/link.c index 03075165665e..4fb4ae0a75ed 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c | |||
@@ -2390,35 +2390,40 @@ void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window) | |||
2390 | l_ptr->queue_limit[MSG_FRAGMENTER] = 4000; | 2390 | l_ptr->queue_limit[MSG_FRAGMENTER] = 4000; |
2391 | } | 2391 | } |
2392 | 2392 | ||
2393 | /** | 2393 | /* tipc_link_find_owner - locate owner node of link by link's name |
2394 | * link_find_link - locate link by name | 2394 | * @name: pointer to link name string |
2395 | * @name: ptr to link name string | 2395 | * @bearer_id: pointer to index in 'node->links' array where the link was found. |
2396 | * @node: ptr to area to be filled with ptr to associated node | ||
2397 | * | ||
2398 | * Caller must hold 'tipc_net_lock' to ensure node and bearer are not deleted; | 2396 | * Caller must hold 'tipc_net_lock' to ensure node and bearer are not deleted; |
2399 | * this also prevents link deletion. | 2397 | * this also prevents link deletion. |
2400 | * | 2398 | * |
2401 | * Returns pointer to link (or 0 if invalid link name). | 2399 | * Returns pointer to node owning the link, or 0 if no matching link is found. |
2402 | */ | 2400 | */ |
2403 | static struct tipc_link *link_find_link(const char *name, | 2401 | static struct tipc_node *tipc_link_find_owner(const char *link_name, |
2404 | struct tipc_node **node) | 2402 | unsigned int *bearer_id) |
2405 | { | 2403 | { |
2406 | struct tipc_link *l_ptr; | 2404 | struct tipc_link *l_ptr; |
2407 | struct tipc_node *n_ptr; | 2405 | struct tipc_node *n_ptr; |
2406 | struct tipc_node *tmp_n_ptr; | ||
2407 | struct tipc_node *found_node = 0; | ||
2408 | |||
2408 | int i; | 2409 | int i; |
2409 | 2410 | ||
2410 | list_for_each_entry(n_ptr, &tipc_node_list, list) { | 2411 | *bearer_id = 0; |
2412 | list_for_each_entry_safe(n_ptr, tmp_n_ptr, &tipc_node_list, list) { | ||
2413 | spin_lock(&n_ptr->lock); | ||
2411 | for (i = 0; i < MAX_BEARERS; i++) { | 2414 | for (i = 0; i < MAX_BEARERS; i++) { |
2412 | l_ptr = n_ptr->links[i]; | 2415 | l_ptr = n_ptr->links[i]; |
2413 | if (l_ptr && !strcmp(l_ptr->name, name)) | 2416 | if (l_ptr && !strcmp(l_ptr->name, link_name)) { |
2414 | goto found; | 2417 | *bearer_id = i; |
2418 | found_node = n_ptr; | ||
2419 | break; | ||
2420 | } | ||
2415 | } | 2421 | } |
2422 | spin_unlock(&n_ptr->lock); | ||
2423 | if (found_node) | ||
2424 | break; | ||
2416 | } | 2425 | } |
2417 | l_ptr = NULL; | 2426 | return found_node; |
2418 | n_ptr = NULL; | ||
2419 | found: | ||
2420 | *node = n_ptr; | ||
2421 | return l_ptr; | ||
2422 | } | 2427 | } |
2423 | 2428 | ||
2424 | /** | 2429 | /** |
@@ -2460,32 +2465,33 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd) | |||
2460 | struct tipc_link *l_ptr; | 2465 | struct tipc_link *l_ptr; |
2461 | struct tipc_bearer *b_ptr; | 2466 | struct tipc_bearer *b_ptr; |
2462 | struct tipc_media *m_ptr; | 2467 | struct tipc_media *m_ptr; |
2468 | int bearer_id; | ||
2463 | int res = 0; | 2469 | int res = 0; |
2464 | 2470 | ||
2465 | l_ptr = link_find_link(name, &node); | 2471 | node = tipc_link_find_owner(name, &bearer_id); |
2466 | if (l_ptr) { | 2472 | if (node) { |
2467 | /* | ||
2468 | * acquire node lock for tipc_link_send_proto_msg(). | ||
2469 | * see "TIPC locking policy" in net.c. | ||
2470 | */ | ||
2471 | tipc_node_lock(node); | 2473 | tipc_node_lock(node); |
2472 | switch (cmd) { | 2474 | l_ptr = node->links[bearer_id]; |
2473 | case TIPC_CMD_SET_LINK_TOL: | 2475 | |
2474 | link_set_supervision_props(l_ptr, new_value); | 2476 | if (l_ptr) { |
2475 | tipc_link_send_proto_msg(l_ptr, | 2477 | switch (cmd) { |
2476 | STATE_MSG, 0, 0, new_value, 0, 0); | 2478 | case TIPC_CMD_SET_LINK_TOL: |
2477 | break; | 2479 | link_set_supervision_props(l_ptr, new_value); |
2478 | case TIPC_CMD_SET_LINK_PRI: | 2480 | tipc_link_send_proto_msg(l_ptr, STATE_MSG, 0, |
2479 | l_ptr->priority = new_value; | 2481 | 0, new_value, 0, 0); |
2480 | tipc_link_send_proto_msg(l_ptr, | 2482 | break; |
2481 | STATE_MSG, 0, 0, 0, new_value, 0); | 2483 | case TIPC_CMD_SET_LINK_PRI: |
2482 | break; | 2484 | l_ptr->priority = new_value; |
2483 | case TIPC_CMD_SET_LINK_WINDOW: | 2485 | tipc_link_send_proto_msg(l_ptr, STATE_MSG, 0, |
2484 | tipc_link_set_queue_limits(l_ptr, new_value); | 2486 | 0, 0, new_value, 0); |
2485 | break; | 2487 | break; |
2486 | default: | 2488 | case TIPC_CMD_SET_LINK_WINDOW: |
2487 | res = -EINVAL; | 2489 | tipc_link_set_queue_limits(l_ptr, new_value); |
2488 | break; | 2490 | break; |
2491 | default: | ||
2492 | res = -EINVAL; | ||
2493 | break; | ||
2494 | } | ||
2489 | } | 2495 | } |
2490 | tipc_node_unlock(node); | 2496 | tipc_node_unlock(node); |
2491 | return res; | 2497 | return res; |
@@ -2580,6 +2586,7 @@ struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_ | |||
2580 | char *link_name; | 2586 | char *link_name; |
2581 | struct tipc_link *l_ptr; | 2587 | struct tipc_link *l_ptr; |
2582 | struct tipc_node *node; | 2588 | struct tipc_node *node; |
2589 | unsigned int bearer_id; | ||
2583 | 2590 | ||
2584 | if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_LINK_NAME)) | 2591 | if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_LINK_NAME)) |
2585 | return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); | 2592 | return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); |
@@ -2590,15 +2597,19 @@ struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_ | |||
2590 | return tipc_cfg_reply_error_string("link not found"); | 2597 | return tipc_cfg_reply_error_string("link not found"); |
2591 | return tipc_cfg_reply_none(); | 2598 | return tipc_cfg_reply_none(); |
2592 | } | 2599 | } |
2593 | |||
2594 | read_lock_bh(&tipc_net_lock); | 2600 | read_lock_bh(&tipc_net_lock); |
2595 | l_ptr = link_find_link(link_name, &node); | 2601 | node = tipc_link_find_owner(link_name, &bearer_id); |
2602 | if (!node) { | ||
2603 | read_unlock_bh(&tipc_net_lock); | ||
2604 | return tipc_cfg_reply_error_string("link not found"); | ||
2605 | } | ||
2606 | spin_lock(&node->lock); | ||
2607 | l_ptr = node->links[bearer_id]; | ||
2596 | if (!l_ptr) { | 2608 | if (!l_ptr) { |
2609 | tipc_node_unlock(node); | ||
2597 | read_unlock_bh(&tipc_net_lock); | 2610 | read_unlock_bh(&tipc_net_lock); |
2598 | return tipc_cfg_reply_error_string("link not found"); | 2611 | return tipc_cfg_reply_error_string("link not found"); |
2599 | } | 2612 | } |
2600 | |||
2601 | tipc_node_lock(node); | ||
2602 | link_reset_statistics(l_ptr); | 2613 | link_reset_statistics(l_ptr); |
2603 | tipc_node_unlock(node); | 2614 | tipc_node_unlock(node); |
2604 | read_unlock_bh(&tipc_net_lock); | 2615 | read_unlock_bh(&tipc_net_lock); |
@@ -2628,18 +2639,27 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size) | |||
2628 | struct tipc_node *node; | 2639 | struct tipc_node *node; |
2629 | char *status; | 2640 | char *status; |
2630 | u32 profile_total = 0; | 2641 | u32 profile_total = 0; |
2642 | unsigned int bearer_id; | ||
2631 | int ret; | 2643 | int ret; |
2632 | 2644 | ||
2633 | if (!strcmp(name, tipc_bclink_name)) | 2645 | if (!strcmp(name, tipc_bclink_name)) |
2634 | return tipc_bclink_stats(buf, buf_size); | 2646 | return tipc_bclink_stats(buf, buf_size); |
2635 | 2647 | ||
2636 | read_lock_bh(&tipc_net_lock); | 2648 | read_lock_bh(&tipc_net_lock); |
2637 | l = link_find_link(name, &node); | 2649 | node = tipc_link_find_owner(name, &bearer_id); |
2638 | if (!l) { | 2650 | if (!node) { |
2639 | read_unlock_bh(&tipc_net_lock); | 2651 | read_unlock_bh(&tipc_net_lock); |
2640 | return 0; | 2652 | return 0; |
2641 | } | 2653 | } |
2642 | tipc_node_lock(node); | 2654 | tipc_node_lock(node); |
2655 | |||
2656 | l = node->links[bearer_id]; | ||
2657 | if (!l) { | ||
2658 | tipc_node_unlock(node); | ||
2659 | read_unlock_bh(&tipc_net_lock); | ||
2660 | return 0; | ||
2661 | } | ||
2662 | |||
2643 | s = &l->stats; | 2663 | s = &l->stats; |
2644 | 2664 | ||
2645 | if (tipc_link_is_active(l)) | 2665 | if (tipc_link_is_active(l)) |