Commit d39bbd44 authored by Jon Paul Maloy's avatar Jon Paul Maloy Committed by David S. Miller
Browse files

tipc: move link input queue to tipc_node

At present, the link input queue and the name distributor receive
queues are fields aggregated in struct tipc_link. This is a hazard,
because a link might be deleted while a receiving socket still keeps
reference to one of the queues.

This commit fixes this bug. However, rather than adding yet another
reference counter to the critical data path, we move the two queues
to safe ground inside struct tipc_node, which is already protected, and
let the link code only handle references to the queues. This is also
in line with planned later changes in this area.

Reviewed-by: default avatarYing Xue <>
Signed-off-by: default avatarJon Maloy <>
Signed-off-by: default avatarDavid S. Miller <>
parent d3a43b90
......@@ -227,7 +227,9 @@ static void link_set_timer(struct tipc_link *link, unsigned long time)
struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
struct tipc_bearer *b_ptr,
const struct tipc_media_addr *media_addr)
const struct tipc_media_addr *media_addr,
struct sk_buff_head *inputq,
struct sk_buff_head *namedq)
struct tipc_net *tn = net_generic(n_ptr->net, tipc_net_id);
struct tipc_link *l_ptr;
......@@ -289,8 +291,9 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
l_ptr->inputq = inputq;
l_ptr->namedq = namedq;
tipc_node_attach_link(n_ptr, l_ptr);
setup_timer(&l_ptr->timer, link_timeout, (unsigned long)l_ptr);
......@@ -391,8 +394,8 @@ void link_prepare_wakeup(struct tipc_link *l)
if ((pnd[imp] + l->backlog[imp].len) >= lim)
skb_unlink(skb, &l->wakeupq);
skb_queue_tail(&l->inputq, skb);
l->owner->inputq = &l->inputq;
skb_queue_tail(l->inputq, skb);
l->owner->inputq = l->inputq;
l->owner->action_flags |= TIPC_MSG_EVT;
......@@ -465,7 +468,7 @@ void tipc_link_reset(struct tipc_link *l_ptr)
if (!owner->inputq)
owner->inputq = &l_ptr->inputq;
owner->inputq = l_ptr->inputq;
skb_queue_splice_init(&l_ptr->wakeupq, owner->inputq);
if (!skb_queue_empty(owner->inputq))
owner->action_flags |= TIPC_MSG_EVT;
......@@ -962,7 +965,7 @@ static bool link_synch(struct tipc_link *l)
/* Is it still in the input queue ? */
post_synch = mod(pl->rcv_nxt - l->synch_point) - 1;
if (skb_queue_len(&pl->inputq) > post_synch)
if (skb_queue_len(pl->inputq) > post_synch)
return false;
l->flags &= ~LINK_SYNCHING;
......@@ -1141,16 +1144,16 @@ static bool tipc_data_input(struct tipc_link *link, struct sk_buff *skb)
if (tipc_skb_queue_tail(&link->inputq, skb, dport)) {
node->inputq = &link->inputq;
if (tipc_skb_queue_tail(link->inputq, skb, dport)) {
node->inputq = link->inputq;
node->action_flags |= TIPC_MSG_EVT;
return true;
node->bclink.recv_permitted = true;
node->namedq = &link->namedq;
skb_queue_tail(&link->namedq, skb);
if (skb_queue_len(&link->namedq) == 1)
node->namedq = link->namedq;
skb_queue_tail(link->namedq, skb);
if (skb_queue_len(link->namedq) == 1)
node->action_flags |= TIPC_NAMED_MSG_EVT;
return true;
......@@ -192,8 +192,8 @@ struct tipc_link {
u16 rcv_nxt;
u32 rcv_unacked;
struct sk_buff_head deferdq;
struct sk_buff_head inputq;
struct sk_buff_head namedq;
struct sk_buff_head *inputq;
struct sk_buff_head *namedq;
/* Congestion handling */
struct sk_buff_head wakeupq;
......@@ -207,9 +207,11 @@ struct tipc_link {
struct tipc_port;
struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
struct tipc_bearer *b_ptr,
const struct tipc_media_addr *media_addr);
struct tipc_link *tipc_link_create(struct tipc_node *n,
struct tipc_bearer *b,
const struct tipc_media_addr *maddr,
struct sk_buff_head *inputq,
struct sk_buff_head *namedq);
void tipc_link_delete(struct tipc_link *link);
void tipc_link_delete_list(struct net *net, unsigned int bearer_id);
void tipc_link_failover_send_queue(struct tipc_link *l_ptr);
......@@ -132,6 +132,7 @@ struct tipc_node *tipc_node_create(struct net *net, u32 addr)
hlist_add_head_rcu(&n_ptr->hash, &tn->node_htable[tipc_hashfn(addr)]);
list_for_each_entry_rcu(temp_node, &tn->node_list, list) {
......@@ -350,9 +351,10 @@ bool tipc_node_update_dest(struct tipc_node *n, struct tipc_bearer *b,
struct tipc_link *l = n->links[b->identity].link;
struct tipc_media_addr *curr = &n->links[b->identity].maddr;
struct sk_buff_head *inputq = &n->links[b->identity].inputq;
if (!l)
l = tipc_link_create(n, b, maddr);
l = tipc_link_create(n, b, maddr, inputq, &n->bclink.namedq);
if (!l)
return false;
memcpy(&l->media_addr, maddr, sizeof(*maddr));
......@@ -85,13 +85,14 @@ struct tipc_node_bclink {
u32 deferred_size;
struct sk_buff_head deferdq;
struct sk_buff *reasm_buf;
int inputq_map;
struct sk_buff_head namedq;
bool recv_permitted;
struct tipc_link_entry {
struct tipc_link *link;
u32 mtu;
struct sk_buff_head inputq;
struct tipc_media_addr maddr;
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment