From 50100a5e39461b2a61d6040e73c384766c29975d Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 22 Aug 2014 18:09:07 -0400 Subject: tipc: use pseudo message to wake up sockets after link congestion The current link implementation keeps a linked list of blocked ports/ sockets that is populated when there is link congestion. The purpose of this is to let the link know which users to wake up when the congestion abates. This adds unnecessary complexity to the data structure and the code, since it forces us to involve the link each time we want to delete a socket. It also forces us to grab the spinlock port_lock within the scope of node_lock. We want to get rid of this direct dependence, as well as the deadlock hazard resulting from the usage of port_lock. In this commit, we instead let the link keep list of a "wakeup" pseudo messages for use in such situations. Those messages are sent to the pending sockets via the ordinary message reception path, and wake up the socket's owner when they are received. This enables us to get rid of the 'waiting_ports' linked lists in struct tipc_port that manifest this direct reference. As a consequence, we can eliminate another BH entry into the socket, and hence the need to grab port_lock. This is a further step in our effort to remove port_lock altogether. Signed-off-by: Jon Maloy Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- net/tipc/port.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net/tipc/port.c') diff --git a/net/tipc/port.c b/net/tipc/port.c index 7e096a5e770..b58a777a439 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -92,7 +92,6 @@ u32 tipc_port_init(struct tipc_port *p_ptr, p_ptr->max_pkt = MAX_PKT_DEFAULT; p_ptr->ref = ref; - INIT_LIST_HEAD(&p_ptr->wait_list); INIT_LIST_HEAD(&p_ptr->subscription.nodesub_list); k_init_timer(&p_ptr->timer, (Handler)port_timeout, ref); INIT_LIST_HEAD(&p_ptr->publications); @@ -134,7 +133,6 @@ void tipc_port_destroy(struct tipc_port *p_ptr) } spin_lock_bh(&tipc_port_list_lock); list_del(&p_ptr->port_list); - list_del(&p_ptr->wait_list); spin_unlock_bh(&tipc_port_list_lock); k_term_timer(&p_ptr->timer); } -- cgit v1.2.3-70-g09d2 From 02be61a981fb5ca5f1526323336198ee92cadf95 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 22 Aug 2014 18:09:08 -0400 Subject: tipc: use message to abort connections when losing contact to node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the current implementation, each 'struct tipc_node' instance keeps a linked list of those ports/sockets that are connected to the node represented by that struct. The purpose of this is to let the node object know which sockets to alert when it loses contact with its peer node, i.e., which sockets need to have their connections aborted. This entails an unwanted direct reference from the node structure back to the port/socket structure, and a need to grab port_lock when we have to make an upcall to the port. We want to get rid of this unecessary BH entry point into the socket, and also eliminate its use of port_lock. In this commit, we instead let the node struct keep list of "connected socket" structs, which each represents a connected socket, but is allocated independently by the node at the moment of connection. If the node loses contact with its peer node, the list is traversed, and a "connection abort" message is created for each entry in the list. The message is sent to it respective connected socket using the ordinary data path, and the receiving socket aborts its connections upon reception of the message. This enables us to get rid of the direct reference from 'struct node' to ´struct port', and another unwanted BH access point to the latter. Signed-off-by: Jon Maloy Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- net/tipc/node.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/tipc/node.h | 3 +++ net/tipc/port.c | 29 +++------------------ 3 files changed, 85 insertions(+), 25 deletions(-) (limited to 'net/tipc/port.c') diff --git a/net/tipc/node.c b/net/tipc/node.c index 6ea2c15cfc8..17e6378c4df 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -51,6 +51,13 @@ static u32 tipc_num_nodes; static u32 tipc_num_links; static DEFINE_SPINLOCK(node_list_lock); +struct tipc_sock_conn { + u32 port; + u32 peer_port; + u32 peer_node; + struct list_head list; +}; + /* * A trivial power-of-two bitmask technique is used for speed, since this * operation is done for every incoming TIPC packet. The number of hash table @@ -101,6 +108,7 @@ struct tipc_node *tipc_node_create(u32 addr) INIT_HLIST_NODE(&n_ptr->hash); INIT_LIST_HEAD(&n_ptr->list); INIT_LIST_HEAD(&n_ptr->nsub); + INIT_LIST_HEAD(&n_ptr->conn_sks); __skb_queue_head_init(&n_ptr->waiting_sks); hlist_add_head_rcu(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]); @@ -138,6 +146,71 @@ void tipc_node_stop(void) spin_unlock_bh(&node_list_lock); } +int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port) +{ + struct tipc_node *node; + struct tipc_sock_conn *conn; + + if (in_own_node(dnode)) + return 0; + + node = tipc_node_find(dnode); + if (!node) { + pr_warn("Connecting sock to node 0x%x failed\n", dnode); + return -EHOSTUNREACH; + } + conn = kmalloc(sizeof(*conn), GFP_ATOMIC); + if (!conn) + return -EHOSTUNREACH; + conn->peer_node = dnode; + conn->port = port; + conn->peer_port = peer_port; + + tipc_node_lock(node); + list_add_tail(&conn->list, &node->conn_sks); + tipc_node_unlock(node); + return 0; +} + +void tipc_node_remove_conn(u32 dnode, u32 port) +{ + struct tipc_node *node; + struct tipc_sock_conn *conn, *safe; + + if (in_own_node(dnode)) + return; + + node = tipc_node_find(dnode); + if (!node) + return; + + tipc_node_lock(node); + list_for_each_entry_safe(conn, safe, &node->conn_sks, list) { + if (port != conn->port) + continue; + list_del(&conn->list); + kfree(conn); + } + tipc_node_unlock(node); +} + +void tipc_node_abort_sock_conns(struct list_head *conns) +{ + struct tipc_sock_conn *conn, *safe; + struct sk_buff *buf; + + list_for_each_entry_safe(conn, safe, conns, list) { + buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, + SHORT_H_SIZE, 0, tipc_own_addr, + conn->peer_node, conn->port, + conn->peer_port, TIPC_ERR_NO_NODE); + if (likely(buf)) + tipc_sk_rcv(buf); + list_del(&conn->list); + kfree(conn); + } +} + /** * tipc_node_link_up - handle addition of link * @@ -476,6 +549,7 @@ int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len) void tipc_node_unlock(struct tipc_node *node) { LIST_HEAD(nsub_list); + LIST_HEAD(conn_sks); struct sk_buff_head waiting_sks; u32 addr = 0; @@ -491,6 +565,7 @@ void tipc_node_unlock(struct tipc_node *node) } if (node->action_flags & TIPC_NOTIFY_NODE_DOWN) { list_replace_init(&node->nsub, &nsub_list); + list_replace_init(&node->conn_sks, &conn_sks); node->action_flags &= ~TIPC_NOTIFY_NODE_DOWN; } if (node->action_flags & TIPC_NOTIFY_NODE_UP) { @@ -502,6 +577,9 @@ void tipc_node_unlock(struct tipc_node *node) while (!skb_queue_empty(&waiting_sks)) tipc_sk_rcv(__skb_dequeue(&waiting_sks)); + if (!list_empty(&conn_sks)) + tipc_node_abort_sock_conns(&conn_sks); + if (!list_empty(&nsub_list)) tipc_nodesub_notify(&nsub_list); diff --git a/net/tipc/node.h b/net/tipc/node.h index 2ebf9e8b50f..522d6f3157b 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -117,6 +117,7 @@ struct tipc_node { u32 signature; struct list_head nsub; struct sk_buff_head waiting_sks; + struct list_head conn_sks; struct rcu_head rcu; }; @@ -135,6 +136,8 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space) struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space); int tipc_node_get_linkname(u32 bearer_id, u32 node, char *linkname, size_t len); void tipc_node_unlock(struct tipc_node *node); +int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port); +void tipc_node_remove_conn(u32 dnode, u32 port); static inline void tipc_node_lock(struct tipc_node *node) { diff --git a/net/tipc/port.c b/net/tipc/port.c index b58a777a439..edbd83d223c 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -48,7 +48,6 @@ DEFINE_SPINLOCK(tipc_port_list_lock); static LIST_HEAD(ports); -static void port_handle_node_down(unsigned long ref); static struct sk_buff *port_build_self_abort_msg(struct tipc_port *, u32 err); static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *, u32 err); static void port_timeout(unsigned long ref); @@ -126,10 +125,10 @@ void tipc_port_destroy(struct tipc_port *p_ptr) k_cancel_timer(&p_ptr->timer); if (p_ptr->connected) { buf = port_build_peer_abort_msg(p_ptr, TIPC_ERR_NO_PORT); - tipc_nodesub_unsubscribe(&p_ptr->subscription); msg = buf_msg(buf); peer = msg_destnode(msg); tipc_link_xmit(buf, peer, msg_link_selector(msg)); + tipc_node_remove_conn(peer, p_ptr->ref); } spin_lock_bh(&tipc_port_list_lock); list_del(&p_ptr->port_list); @@ -188,22 +187,6 @@ static void port_timeout(unsigned long ref) tipc_link_xmit(buf, msg_destnode(msg), msg_link_selector(msg)); } - -static void port_handle_node_down(unsigned long ref) -{ - struct tipc_port *p_ptr = tipc_port_lock(ref); - struct sk_buff *buf = NULL; - struct tipc_msg *msg = NULL; - - if (!p_ptr) - return; - buf = port_build_self_abort_msg(p_ptr, TIPC_ERR_NO_NODE); - tipc_port_unlock(p_ptr); - msg = buf_msg(buf); - tipc_link_xmit(buf, msg_destnode(msg), msg_link_selector(msg)); -} - - static struct sk_buff *port_build_self_abort_msg(struct tipc_port *p_ptr, u32 err) { struct sk_buff *buf = port_build_peer_abort_msg(p_ptr, err); @@ -217,7 +200,6 @@ static struct sk_buff *port_build_self_abort_msg(struct tipc_port *p_ptr, u32 er return buf; } - static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *p_ptr, u32 err) { struct sk_buff *buf; @@ -447,11 +429,8 @@ int __tipc_port_connect(u32 ref, struct tipc_port *p_ptr, p_ptr->probing_state = TIPC_CONN_OK; p_ptr->connected = 1; k_start_timer(&p_ptr->timer, p_ptr->probing_interval); - - tipc_nodesub_subscribe(&p_ptr->subscription, peer->node, - (void *)(unsigned long)ref, - (net_ev_handler)port_handle_node_down); - res = 0; + res = tipc_node_add_conn(tipc_port_peernode(p_ptr), p_ptr->ref, + tipc_port_peerport(p_ptr)); exit: p_ptr->max_pkt = tipc_node_get_mtu(peer->node, ref); return res; @@ -467,7 +446,7 @@ int __tipc_port_disconnect(struct tipc_port *tp_ptr) if (tp_ptr->connected) { tp_ptr->connected = 0; /* let timer expire on it's own to avoid deadlock! */ - tipc_nodesub_unsubscribe(&tp_ptr->subscription); + tipc_node_remove_conn(tipc_port_peernode(tp_ptr), tp_ptr->ref); return 0; } -- cgit v1.2.3-70-g09d2 From 5728901581139e68e6cf53b36590f64829c37453 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 22 Aug 2014 18:09:09 -0400 Subject: tipc: clean up socket timer function The last remaining BH upcall to the socket, apart for the message reception function tipc_sk_rcv(), is the timer function. We prefer to let this function continue executing in BH, since it only does read-acces to semi-permanent data, but we make three changes to it: 1) We introduce a bh_lock_sock()/bh_unlock_sock() inside the scope of port_lock. This is a preparation for replacing port_lock with bh_lock_sock() at the locations where it is still used. 2) We move the function from port.c to socket.c, as a further step of eliminating the port code level altogether. 3) We let it make use of the newly introduced tipc_msg_create() function. This enables us to get rid of three context specific functions (port_create_self_abort_msg() etc.) in port.c Signed-off-by: Jon Maloy Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- net/tipc/port.c | 126 ++++++++++-------------------------------------------- net/tipc/socket.c | 46 ++++++++++++++++++++ 2 files changed, 69 insertions(+), 103 deletions(-) (limited to 'net/tipc/port.c') diff --git a/net/tipc/port.c b/net/tipc/port.c index edbd83d223c..6de79f26981 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -48,9 +48,6 @@ DEFINE_SPINLOCK(tipc_port_list_lock); static LIST_HEAD(ports); -static struct sk_buff *port_build_self_abort_msg(struct tipc_port *, u32 err); -static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *, u32 err); -static void port_timeout(unsigned long ref); /** * tipc_port_peer_msg - verify message was sent by connected port's peer @@ -92,7 +89,6 @@ u32 tipc_port_init(struct tipc_port *p_ptr, p_ptr->max_pkt = MAX_PKT_DEFAULT; p_ptr->ref = ref; INIT_LIST_HEAD(&p_ptr->subscription.nodesub_list); - k_init_timer(&p_ptr->timer, (Handler)port_timeout, ref); INIT_LIST_HEAD(&p_ptr->publications); INIT_LIST_HEAD(&p_ptr->port_list); @@ -114,7 +110,7 @@ void tipc_port_destroy(struct tipc_port *p_ptr) { struct sk_buff *buf = NULL; struct tipc_msg *msg = NULL; - u32 peer; + u32 peer_node; tipc_withdraw(p_ptr, 0, NULL); @@ -124,11 +120,16 @@ void tipc_port_destroy(struct tipc_port *p_ptr) k_cancel_timer(&p_ptr->timer); if (p_ptr->connected) { - buf = port_build_peer_abort_msg(p_ptr, TIPC_ERR_NO_PORT); - msg = buf_msg(buf); - peer = msg_destnode(msg); - tipc_link_xmit(buf, peer, msg_link_selector(msg)); - tipc_node_remove_conn(peer, p_ptr->ref); + peer_node = tipc_port_peernode(p_ptr); + buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, + SHORT_H_SIZE, 0, peer_node, + tipc_own_addr, tipc_port_peerport(p_ptr), + p_ptr->ref, TIPC_ERR_NO_PORT); + if (buf) { + msg = buf_msg(buf); + tipc_link_xmit(buf, peer_node, msg_link_selector(msg)); + } + tipc_node_remove_conn(peer_node, p_ptr->ref); } spin_lock_bh(&tipc_port_list_lock); list_del(&p_ptr->port_list); @@ -136,94 +137,6 @@ void tipc_port_destroy(struct tipc_port *p_ptr) k_term_timer(&p_ptr->timer); } -/* - * port_build_proto_msg(): create connection protocol message for port - * - * On entry the port must be locked and connected. - */ -static struct sk_buff *port_build_proto_msg(struct tipc_port *p_ptr, - u32 type, u32 ack) -{ - struct sk_buff *buf; - struct tipc_msg *msg; - - buf = tipc_buf_acquire(INT_H_SIZE); - if (buf) { - msg = buf_msg(buf); - tipc_msg_init(msg, CONN_MANAGER, type, INT_H_SIZE, - tipc_port_peernode(p_ptr)); - msg_set_destport(msg, tipc_port_peerport(p_ptr)); - msg_set_origport(msg, p_ptr->ref); - msg_set_msgcnt(msg, ack); - buf->next = NULL; - } - return buf; -} - -static void port_timeout(unsigned long ref) -{ - struct tipc_port *p_ptr = tipc_port_lock(ref); - struct sk_buff *buf = NULL; - struct tipc_msg *msg = NULL; - - if (!p_ptr) - return; - - if (!p_ptr->connected) { - tipc_port_unlock(p_ptr); - return; - } - - /* Last probe answered ? */ - if (p_ptr->probing_state == TIPC_CONN_PROBING) { - buf = port_build_self_abort_msg(p_ptr, TIPC_ERR_NO_PORT); - } else { - buf = port_build_proto_msg(p_ptr, CONN_PROBE, 0); - p_ptr->probing_state = TIPC_CONN_PROBING; - k_start_timer(&p_ptr->timer, p_ptr->probing_interval); - } - tipc_port_unlock(p_ptr); - msg = buf_msg(buf); - tipc_link_xmit(buf, msg_destnode(msg), msg_link_selector(msg)); -} - -static struct sk_buff *port_build_self_abort_msg(struct tipc_port *p_ptr, u32 err) -{ - struct sk_buff *buf = port_build_peer_abort_msg(p_ptr, err); - - if (buf) { - struct tipc_msg *msg = buf_msg(buf); - msg_swap_words(msg, 4, 5); - msg_swap_words(msg, 6, 7); - buf->next = NULL; - } - return buf; -} - -static struct sk_buff *port_build_peer_abort_msg(struct tipc_port *p_ptr, u32 err) -{ - struct sk_buff *buf; - struct tipc_msg *msg; - u32 imp; - - if (!p_ptr->connected) - return NULL; - - buf = tipc_buf_acquire(BASIC_H_SIZE); - if (buf) { - msg = buf_msg(buf); - memcpy(msg, &p_ptr->phdr, BASIC_H_SIZE); - msg_set_hdr_sz(msg, BASIC_H_SIZE); - msg_set_size(msg, BASIC_H_SIZE); - imp = msg_importance(msg); - if (imp < TIPC_CRITICAL_IMPORTANCE) - msg_set_importance(msg, ++imp); - msg_set_errcode(msg, err); - buf->next = NULL; - } - return buf; -} - static int port_print(struct tipc_port *p_ptr, char *buf, int len, int full_id) { struct publication *publ; @@ -321,12 +234,15 @@ void tipc_acknowledge(u32 ref, u32 ack) if (!p_ptr) return; if (p_ptr->connected) - buf = port_build_proto_msg(p_ptr, CONN_ACK, ack); - + buf = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, + 0, tipc_port_peernode(p_ptr), + tipc_own_addr, tipc_port_peerport(p_ptr), + p_ptr->ref, TIPC_OK); tipc_port_unlock(p_ptr); if (!buf) return; msg = buf_msg(buf); + msg_set_msgcnt(msg, ack); tipc_link_xmit(buf, msg_destnode(msg), msg_link_selector(msg)); } @@ -478,14 +394,18 @@ int tipc_port_shutdown(u32 ref) struct tipc_msg *msg; struct tipc_port *p_ptr; struct sk_buff *buf = NULL; + u32 peer_node; p_ptr = tipc_port_lock(ref); if (!p_ptr) return -EINVAL; - - buf = port_build_peer_abort_msg(p_ptr, TIPC_CONN_SHUTDOWN); + peer_node = tipc_port_peernode(p_ptr); + buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, + SHORT_H_SIZE, 0, peer_node, + tipc_own_addr, tipc_port_peerport(p_ptr), + p_ptr->ref, TIPC_CONN_SHUTDOWN); tipc_port_unlock(p_ptr); msg = buf_msg(buf); - tipc_link_xmit(buf, msg_destnode(msg), msg_link_selector(msg)); + tipc_link_xmit(buf, peer_node, msg_link_selector(msg)); return tipc_port_disconnect(ref); } diff --git a/net/tipc/socket.c b/net/tipc/socket.c index a8be4d2001f..5f8376e8da2 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -53,6 +53,7 @@ static void tipc_write_space(struct sock *sk); static int tipc_release(struct socket *sock); static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags); static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p); +static void tipc_sk_timeout(unsigned long ref); static const struct proto_ops packet_ops; static const struct proto_ops stream_ops; @@ -202,6 +203,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, sock->state = state; sock_init_data(sock, sk); + k_init_timer(&port->timer, (Handler)tipc_sk_timeout, ref); sk->sk_backlog_rcv = tipc_backlog_rcv; sk->sk_rcvbuf = sysctl_tipc_rmem[1]; sk->sk_data_ready = tipc_data_ready; @@ -1946,6 +1948,50 @@ restart: return res; } +static void tipc_sk_timeout(unsigned long ref) +{ + struct tipc_port *port = tipc_port_lock(ref); + struct tipc_sock *tsk; + struct sock *sk; + struct sk_buff *buf = NULL; + struct tipc_msg *msg = NULL; + u32 peer_port, peer_node; + + if (!port) + return; + + if (!port->connected) { + tipc_port_unlock(port); + return; + } + tsk = tipc_port_to_sock(port); + sk = &tsk->sk; + bh_lock_sock(sk); + peer_port = tipc_port_peerport(port); + peer_node = tipc_port_peernode(port); + + if (port->probing_state == TIPC_CONN_PROBING) { + /* Previous probe not answered -> self abort */ + buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, + SHORT_H_SIZE, 0, tipc_own_addr, + peer_node, ref, peer_port, + TIPC_ERR_NO_PORT); + } else { + buf = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE, + 0, peer_node, tipc_own_addr, + peer_port, ref, TIPC_OK); + port->probing_state = TIPC_CONN_PROBING; + k_start_timer(&port->timer, port->probing_interval); + } + bh_unlock_sock(sk); + tipc_port_unlock(port); + if (!buf) + return; + + msg = buf_msg(buf); + tipc_link_xmit(buf, msg_destnode(msg), msg_link_selector(msg)); +} + /** * tipc_setsockopt - set socket option * @sock: socket structure -- cgit v1.2.3-70-g09d2 From 80e44c22255468337b891da2348cab68cb62766f Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 22 Aug 2014 18:09:10 -0400 Subject: tipc: eliminate function tipc_port_shutdown() tipc_port_shutdown() is a remnant from the now obsolete native interface. As such it grabs port_lock in order to protect itself from concurrent BH processing. However, after the recent changes to the port/socket upcalls, sockets are now basically single-threaded, and all execution, except the read-only tipc_sk_timer(), is executing within the protection of lock_sock(). So the use of port_lock is not needed here. In this commit we eliminate the whole function, and merge it into its only caller, tipc_shutdown(). Signed-off-by: Jon Maloy Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- net/tipc/port.c | 24 ------------------------ net/tipc/port.h | 2 -- net/tipc/socket.c | 15 +++++++++++---- 3 files changed, 11 insertions(+), 30 deletions(-) (limited to 'net/tipc/port.c') diff --git a/net/tipc/port.c b/net/tipc/port.c index 6de79f26981..3ad092b7fb7 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -385,27 +385,3 @@ int tipc_port_disconnect(u32 ref) tipc_port_unlock(p_ptr); return res; } - -/* - * tipc_port_shutdown(): Send a SHUTDOWN msg to peer and disconnect - */ -int tipc_port_shutdown(u32 ref) -{ - struct tipc_msg *msg; - struct tipc_port *p_ptr; - struct sk_buff *buf = NULL; - u32 peer_node; - - p_ptr = tipc_port_lock(ref); - if (!p_ptr) - return -EINVAL; - peer_node = tipc_port_peernode(p_ptr); - buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, - SHORT_H_SIZE, 0, peer_node, - tipc_own_addr, tipc_port_peerport(p_ptr), - p_ptr->ref, TIPC_CONN_SHUTDOWN); - tipc_port_unlock(p_ptr); - msg = buf_msg(buf); - tipc_link_xmit(buf, peer_node, msg_link_selector(msg)); - return tipc_port_disconnect(ref); -} diff --git a/net/tipc/port.h b/net/tipc/port.h index 6cdc7de8c9b..f5762f940e3 100644 --- a/net/tipc/port.h +++ b/net/tipc/port.h @@ -106,8 +106,6 @@ int tipc_port_connect(u32 portref, struct tipc_portid const *port); int tipc_port_disconnect(u32 portref); -int tipc_port_shutdown(u32 ref); - /* * The following routines require that the port be locked on entry */ diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 5f8376e8da2..f202d4790ce 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1899,7 +1899,7 @@ static int tipc_shutdown(struct socket *sock, int how) struct tipc_sock *tsk = tipc_sk(sk); struct tipc_port *port = &tsk->port; struct sk_buff *buf; - u32 peer; + u32 dnode; int res; if (how != SHUT_RDWR) @@ -1920,10 +1920,17 @@ restart: goto restart; } tipc_port_disconnect(port->ref); - if (tipc_msg_reverse(buf, &peer, TIPC_CONN_SHUTDOWN)) - tipc_link_xmit(buf, peer, 0); + if (tipc_msg_reverse(buf, &dnode, TIPC_CONN_SHUTDOWN)) + tipc_link_xmit(buf, dnode, port->ref); } else { - tipc_port_shutdown(port->ref); + dnode = tipc_port_peernode(port); + buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, + TIPC_CONN_MSG, SHORT_H_SIZE, + 0, dnode, tipc_own_addr, + tipc_port_peerport(port), + port->ref, TIPC_CONN_SHUTDOWN); + tipc_link_xmit(buf, dnode, port->ref); + __tipc_port_disconnect(port); } sock->state = SS_DISCONNECTING; -- cgit v1.2.3-70-g09d2 From dadebc00299a19dc4639ba7192db937e31b81eb2 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 22 Aug 2014 18:09:11 -0400 Subject: tipc: eliminate port_connect()/port_disconnect() functions tipc_port_connect()/tipc_port_disconnect() are remnants of the obsolete native API. Their only task is to grab port_lock and call the functions __tipc_port_connect()/__tipc_port_disconnect() respectively, which will perform the actual state change. Since socket/port exection now is single-threaded the use of port_lock is not needed any more, so we can safely replace the two functions with their lock-free counterparts. In this commit, we remove the two functions. Furthermore, the contents of __tipc_port_disconnect() is so trivial that we choose to eliminate that function too, expanding its functionality into tipc_shutdown(). __tipc_port_connect() is simplified, moved to socket.c, and given the more correct name tipc_sk_finish_conn(). Finally, we eliminate the function auto_connect(), and expand its contents into filter_connect(). Signed-off-by: Jon Maloy Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- net/tipc/port.c | 84 ------------------------------------------------------- net/tipc/port.h | 10 ------- net/tipc/socket.c | 75 ++++++++++++++++++++++++------------------------- 3 files changed, 37 insertions(+), 132 deletions(-) (limited to 'net/tipc/port.c') diff --git a/net/tipc/port.c b/net/tipc/port.c index 3ad092b7fb7..2f96719a351 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -40,9 +40,6 @@ #include "name_table.h" #include "socket.h" -/* Connection management: */ -#define PROBING_INTERVAL 3600000 /* [ms] => 1 h */ - #define MAX_REJECT_SIZE 1024 DEFINE_SPINLOCK(tipc_port_list_lock); @@ -304,84 +301,3 @@ int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope, p_ptr->published = 0; return res; } - -int tipc_port_connect(u32 ref, struct tipc_portid const *peer) -{ - struct tipc_port *p_ptr; - int res; - - p_ptr = tipc_port_lock(ref); - if (!p_ptr) - return -EINVAL; - res = __tipc_port_connect(ref, p_ptr, peer); - tipc_port_unlock(p_ptr); - return res; -} - -/* - * __tipc_port_connect - connect to a remote peer - * - * Port must be locked. - */ -int __tipc_port_connect(u32 ref, struct tipc_port *p_ptr, - struct tipc_portid const *peer) -{ - struct tipc_msg *msg; - int res = -EINVAL; - - if (p_ptr->published || p_ptr->connected) - goto exit; - if (!peer->ref) - goto exit; - - msg = &p_ptr->phdr; - msg_set_destnode(msg, peer->node); - msg_set_destport(msg, peer->ref); - msg_set_type(msg, TIPC_CONN_MSG); - msg_set_lookup_scope(msg, 0); - msg_set_hdr_sz(msg, SHORT_H_SIZE); - - p_ptr->probing_interval = PROBING_INTERVAL; - p_ptr->probing_state = TIPC_CONN_OK; - p_ptr->connected = 1; - k_start_timer(&p_ptr->timer, p_ptr->probing_interval); - res = tipc_node_add_conn(tipc_port_peernode(p_ptr), p_ptr->ref, - tipc_port_peerport(p_ptr)); -exit: - p_ptr->max_pkt = tipc_node_get_mtu(peer->node, ref); - return res; -} - -/* - * __tipc_disconnect - disconnect port from peer - * - * Port must be locked. - */ -int __tipc_port_disconnect(struct tipc_port *tp_ptr) -{ - if (tp_ptr->connected) { - tp_ptr->connected = 0; - /* let timer expire on it's own to avoid deadlock! */ - tipc_node_remove_conn(tipc_port_peernode(tp_ptr), tp_ptr->ref); - return 0; - } - - return -ENOTCONN; -} - -/* - * tipc_port_disconnect(): Disconnect port form peer. - * This is a node local operation. - */ -int tipc_port_disconnect(u32 ref) -{ - struct tipc_port *p_ptr; - int res; - - p_ptr = tipc_port_lock(ref); - if (!p_ptr) - return -EINVAL; - res = __tipc_port_disconnect(p_ptr); - tipc_port_unlock(p_ptr); - return res; -} diff --git a/net/tipc/port.h b/net/tipc/port.h index f5762f940e3..b356cb8340d 100644 --- a/net/tipc/port.h +++ b/net/tipc/port.h @@ -102,16 +102,6 @@ int tipc_publish(struct tipc_port *p_ptr, unsigned int scope, int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope, struct tipc_name_seq const *name_seq); -int tipc_port_connect(u32 portref, struct tipc_portid const *port); - -int tipc_port_disconnect(u32 portref); - -/* - * The following routines require that the port be locked on entry - */ -int __tipc_port_disconnect(struct tipc_port *tp_ptr); -int __tipc_port_connect(u32 ref, struct tipc_port *p_ptr, - struct tipc_portid const *peer); int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg); struct sk_buff *tipc_port_get_ports(void); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index f202d4790ce..a65105818fe 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -45,6 +45,7 @@ #define SS_READY -2 /* socket is connectionless */ #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ +#define CONN_PROBING_INTERVAL 3600000 /* [ms] => 1 h */ #define TIPC_FWD_MSG 1 static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb); @@ -339,7 +340,9 @@ static int tipc_release(struct socket *sock) if ((sock->state == SS_CONNECTING) || (sock->state == SS_CONNECTED)) { sock->state = SS_DISCONNECTING; - tipc_port_disconnect(port->ref); + port->connected = 0; + tipc_node_remove_conn(tipc_port_peernode(port), + port->ref); } if (tipc_msg_reverse(buf, &dnode, TIPC_ERR_NO_PORT)) tipc_link_xmit(buf, dnode, 0); @@ -988,29 +991,25 @@ static int tipc_send_packet(struct kiocb *iocb, struct socket *sock, return tipc_send_stream(iocb, sock, m, dsz); } -/** - * auto_connect - complete connection setup to a remote port - * @tsk: tipc socket structure - * @msg: peer's response message - * - * Returns 0 on success, errno otherwise +/* tipc_sk_finish_conn - complete the setup of a connection */ -static int auto_connect(struct tipc_sock *tsk, struct tipc_msg *msg) +static void tipc_sk_finish_conn(struct tipc_port *port, u32 peer_port, + u32 peer_node) { - struct tipc_port *port = &tsk->port; - struct socket *sock = tsk->sk.sk_socket; - struct tipc_portid peer; - - peer.ref = msg_origport(msg); - peer.node = msg_orignode(msg); + struct tipc_msg *msg = &port->phdr; - __tipc_port_connect(port->ref, port, &peer); + msg_set_destnode(msg, peer_node); + msg_set_destport(msg, peer_port); + msg_set_type(msg, TIPC_CONN_MSG); + msg_set_lookup_scope(msg, 0); + msg_set_hdr_sz(msg, SHORT_H_SIZE); - if (msg_importance(msg) > TIPC_CRITICAL_IMPORTANCE) - return -EINVAL; - msg_set_importance(&port->phdr, (u32)msg_importance(msg)); - sock->state = SS_CONNECTED; - return 0; + port->probing_interval = CONN_PROBING_INTERVAL; + port->probing_state = TIPC_CONN_OK; + port->connected = 1; + k_start_timer(&port->timer, port->probing_interval); + tipc_node_add_conn(peer_node, port->ref, peer_port); + port->max_pkt = tipc_node_get_mtu(peer_node, port->ref); } /** @@ -1405,7 +1404,6 @@ static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf) struct tipc_msg *msg = buf_msg(*buf); int retval = -TIPC_ERR_NO_PORT; - int res; if (msg_mcast(msg)) return retval; @@ -1416,13 +1414,20 @@ static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf) if (msg_connected(msg) && tipc_port_peer_msg(port, msg)) { if (unlikely(msg_errcode(msg))) { sock->state = SS_DISCONNECTING; - __tipc_port_disconnect(port); + port->connected = 0; + /* let timer expire on it's own */ + tipc_node_remove_conn(tipc_port_peernode(port), + port->ref); } retval = TIPC_OK; } break; case SS_CONNECTING: /* Accept only ACK or NACK message */ + + if (unlikely(!msg_connected(msg))) + break; + if (unlikely(msg_errcode(msg))) { sock->state = SS_DISCONNECTING; sk->sk_err = ECONNREFUSED; @@ -1430,17 +1435,17 @@ static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf) break; } - if (unlikely(!msg_connected(msg))) - break; - - res = auto_connect(tsk, msg); - if (res) { + if (unlikely(msg_importance(msg) > TIPC_CRITICAL_IMPORTANCE)) { sock->state = SS_DISCONNECTING; - sk->sk_err = -res; + sk->sk_err = EINVAL; retval = TIPC_OK; break; } + tipc_sk_finish_conn(port, msg_origport(msg), msg_orignode(msg)); + msg_set_importance(&port->phdr, msg_importance(msg)); + sock->state = SS_CONNECTED; + /* If an incoming message is an 'ACK-', it should be * discarded here because it doesn't contain useful * data. In addition, we should try to wake up @@ -1816,8 +1821,6 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) struct sk_buff *buf; struct tipc_port *new_port; struct tipc_msg *msg; - struct tipc_portid peer; - u32 new_ref; long timeo; int res; @@ -1840,7 +1843,6 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) new_sk = new_sock->sk; new_port = &tipc_sk(new_sk)->port; - new_ref = new_port->ref; msg = buf_msg(buf); /* we lock on new_sk; but lockdep sees the lock on sk */ @@ -1853,9 +1855,7 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags) reject_rx_queue(new_sk); /* Connect new socket to it's peer */ - peer.ref = msg_origport(msg); - peer.node = msg_orignode(msg); - tipc_port_connect(new_ref, &peer); + tipc_sk_finish_conn(new_port, msg_origport(msg), msg_orignode(msg)); new_sock->state = SS_CONNECTED; tipc_port_set_importance(new_port, msg_importance(msg)); @@ -1919,9 +1919,9 @@ restart: kfree_skb(buf); goto restart; } - tipc_port_disconnect(port->ref); if (tipc_msg_reverse(buf, &dnode, TIPC_CONN_SHUTDOWN)) tipc_link_xmit(buf, dnode, port->ref); + tipc_node_remove_conn(dnode, port->ref); } else { dnode = tipc_port_peernode(port); buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, @@ -1930,11 +1930,10 @@ restart: tipc_port_peerport(port), port->ref, TIPC_CONN_SHUTDOWN); tipc_link_xmit(buf, dnode, port->ref); - __tipc_port_disconnect(port); } - + port->connected = 0; sock->state = SS_DISCONNECTING; - + tipc_node_remove_conn(dnode, port->ref); /* fall through */ case SS_DISCONNECTING: -- cgit v1.2.3-70-g09d2 From 739f5e4efc82c4cb6b5201cbed337b6ff663bf19 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 22 Aug 2014 18:09:12 -0400 Subject: tipc: redefine message acknowledge function The function tipc_acknowledge() is a remnant from the obsolete native API. Currently, it grabs port_lock, before building an acknowledge message and sending it to the peer. Since all access to socket members now is protected by the socket lock, it has become unnecessary to grab port_lock here. In this commit, we remove the usage of port_lock, simplify the function, and move it to socket.c, renaming it to tipc_sk_send_ack(). Signed-off-by: Jon Maloy Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- net/tipc/port.c | 22 ---------------------- net/tipc/port.h | 2 -- net/tipc/socket.c | 22 ++++++++++++++++++++-- 3 files changed, 20 insertions(+), 26 deletions(-) (limited to 'net/tipc/port.c') diff --git a/net/tipc/port.c b/net/tipc/port.c index 2f96719a351..1074ccb0c76 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -221,28 +221,6 @@ void tipc_port_reinit(void) spin_unlock_bh(&tipc_port_list_lock); } -void tipc_acknowledge(u32 ref, u32 ack) -{ - struct tipc_port *p_ptr; - struct sk_buff *buf = NULL; - struct tipc_msg *msg; - - p_ptr = tipc_port_lock(ref); - if (!p_ptr) - return; - if (p_ptr->connected) - buf = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, - 0, tipc_port_peernode(p_ptr), - tipc_own_addr, tipc_port_peerport(p_ptr), - p_ptr->ref, TIPC_OK); - tipc_port_unlock(p_ptr); - if (!buf) - return; - msg = buf_msg(buf); - msg_set_msgcnt(msg, ack); - tipc_link_xmit(buf, msg_destnode(msg), msg_link_selector(msg)); -} - int tipc_publish(struct tipc_port *p_ptr, unsigned int scope, struct tipc_name_seq const *seq) { diff --git a/net/tipc/port.h b/net/tipc/port.h index b356cb8340d..c92e1721f67 100644 --- a/net/tipc/port.h +++ b/net/tipc/port.h @@ -92,8 +92,6 @@ struct tipc_port_list; u32 tipc_port_init(struct tipc_port *p_ptr, const unsigned int importance); -void tipc_acknowledge(u32 port_ref, u32 ack); - void tipc_port_destroy(struct tipc_port *p_ptr); int tipc_publish(struct tipc_port *p_ptr, unsigned int scope, diff --git a/net/tipc/socket.c b/net/tipc/socket.c index a65105818fe..686a11be5c5 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1106,6 +1106,24 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg, return 0; } +static void tipc_sk_send_ack(struct tipc_port *port, uint ack) +{ + struct sk_buff *buf = NULL; + struct tipc_msg *msg; + u32 peer_port = tipc_port_peerport(port); + u32 dnode = tipc_port_peernode(port); + + if (!port->connected) + return; + buf = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, dnode, + tipc_own_addr, peer_port, port->ref, TIPC_OK); + if (!buf) + return; + msg = buf_msg(buf); + msg_set_msgcnt(msg, ack); + tipc_link_xmit(buf, dnode, msg_link_selector(msg)); +} + static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) { struct sock *sk = sock->sk; @@ -1226,7 +1244,7 @@ restart: if (likely(!(flags & MSG_PEEK))) { if ((sock->state != SS_READY) && (++tsk->rcv_unacked >= TIPC_CONNACK_INTV)) { - tipc_acknowledge(port->ref, tsk->rcv_unacked); + tipc_sk_send_ack(port, tsk->rcv_unacked); tsk->rcv_unacked = 0; } advance_rx_queue(sk); @@ -1337,7 +1355,7 @@ restart: /* Consume received message (optional) */ if (likely(!(flags & MSG_PEEK))) { if (unlikely(++tsk->rcv_unacked >= TIPC_CONNACK_INTV)) { - tipc_acknowledge(port->ref, tsk->rcv_unacked); + tipc_sk_send_ack(port, tsk->rcv_unacked); tsk->rcv_unacked = 0; } advance_rx_queue(sk); -- cgit v1.2.3-70-g09d2 From 5b8fa7ce823a59a328e0a7661df2478bfb745de4 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 22 Aug 2014 18:09:13 -0400 Subject: tipc: eliminate functions tipc_port_init and tipc_port_destroy After the latest changes to the socket/port layer the existence of the functions tipc_port_init() and tipc_port_destroy() cannot be justified. They are both called only once, from tipc_sk_create() and tipc_sk_delete() respectively, and their functionality can better be merged into the latter two functions. This also entails that all remaining references to port_lock now are made from inside socket.c, something that will make it easier to remove this lock. Signed-off-by: Jon Maloy Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- net/tipc/port.c | 75 ++----------------------------------------------------- net/tipc/port.h | 4 +-- net/tipc/socket.c | 51 ++++++++++++++++++++++++++++--------- 3 files changed, 42 insertions(+), 88 deletions(-) (limited to 'net/tipc/port.c') diff --git a/net/tipc/port.c b/net/tipc/port.c index 1074ccb0c76..1efa2982d2d 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -42,10 +42,6 @@ #define MAX_REJECT_SIZE 1024 -DEFINE_SPINLOCK(tipc_port_list_lock); - -static LIST_HEAD(ports); - /** * tipc_port_peer_msg - verify message was sent by connected port's peer * @@ -67,73 +63,6 @@ int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg) (!peernode && (orignode == tipc_own_addr)); } -/* tipc_port_init - intiate TIPC port and lock it - * - * Returns obtained reference if initialization is successful, zero otherwise - */ -u32 tipc_port_init(struct tipc_port *p_ptr, - const unsigned int importance) -{ - struct tipc_msg *msg; - u32 ref; - - ref = tipc_ref_acquire(p_ptr, &p_ptr->lock); - if (!ref) { - pr_warn("Port registration failed, ref. table exhausted\n"); - return 0; - } - - p_ptr->max_pkt = MAX_PKT_DEFAULT; - p_ptr->ref = ref; - INIT_LIST_HEAD(&p_ptr->subscription.nodesub_list); - INIT_LIST_HEAD(&p_ptr->publications); - INIT_LIST_HEAD(&p_ptr->port_list); - - /* - * Must hold port list lock while initializing message header template - * to ensure a change to node's own network address doesn't result - * in template containing out-dated network address information - */ - spin_lock_bh(&tipc_port_list_lock); - msg = &p_ptr->phdr; - tipc_msg_init(msg, importance, TIPC_NAMED_MSG, NAMED_H_SIZE, 0); - msg_set_origport(msg, ref); - list_add_tail(&p_ptr->port_list, &ports); - spin_unlock_bh(&tipc_port_list_lock); - return ref; -} - -void tipc_port_destroy(struct tipc_port *p_ptr) -{ - struct sk_buff *buf = NULL; - struct tipc_msg *msg = NULL; - u32 peer_node; - - tipc_withdraw(p_ptr, 0, NULL); - - spin_lock_bh(p_ptr->lock); - tipc_ref_discard(p_ptr->ref); - spin_unlock_bh(p_ptr->lock); - - k_cancel_timer(&p_ptr->timer); - if (p_ptr->connected) { - peer_node = tipc_port_peernode(p_ptr); - buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, - SHORT_H_SIZE, 0, peer_node, - tipc_own_addr, tipc_port_peerport(p_ptr), - p_ptr->ref, TIPC_ERR_NO_PORT); - if (buf) { - msg = buf_msg(buf); - tipc_link_xmit(buf, peer_node, msg_link_selector(msg)); - } - tipc_node_remove_conn(peer_node, p_ptr->ref); - } - spin_lock_bh(&tipc_port_list_lock); - list_del(&p_ptr->port_list); - spin_unlock_bh(&tipc_port_list_lock); - k_term_timer(&p_ptr->timer); -} - static int port_print(struct tipc_port *p_ptr, char *buf, int len, int full_id) { struct publication *publ; @@ -194,7 +123,7 @@ struct sk_buff *tipc_port_get_ports(void) pb_len = ULTRA_STRING_MAX_LEN; spin_lock_bh(&tipc_port_list_lock); - list_for_each_entry(p_ptr, &ports, port_list) { + list_for_each_entry(p_ptr, &tipc_socks, port_list) { spin_lock_bh(p_ptr->lock); str_len += port_print(p_ptr, pb, pb_len, 0); spin_unlock_bh(p_ptr->lock); @@ -213,7 +142,7 @@ void tipc_port_reinit(void) struct tipc_msg *msg; spin_lock_bh(&tipc_port_list_lock); - list_for_each_entry(p_ptr, &ports, port_list) { + list_for_each_entry(p_ptr, &tipc_socks, port_list) { msg = &p_ptr->phdr; msg_set_prevnode(msg, tipc_own_addr); msg_set_orignode(msg, tipc_own_addr); diff --git a/net/tipc/port.h b/net/tipc/port.h index c92e1721f67..4c6cfdd3635 100644 --- a/net/tipc/port.h +++ b/net/tipc/port.h @@ -63,7 +63,6 @@ * @probing_state: * @probing_interval: * @timer_ref: - * @subscription: "node down" subscription used to terminate failed connections */ struct tipc_port { spinlock_t *lock; @@ -80,11 +79,10 @@ struct tipc_port { u32 probing_state; u32 probing_interval; struct timer_list timer; - struct tipc_node_subscr subscription; }; +extern struct list_head tipc_socks; extern spinlock_t tipc_port_list_lock; -struct tipc_port_list; /* * TIPC port manipulation routines diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 686a11be5c5..f2be4c2e20b 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -63,6 +63,9 @@ static const struct proto_ops msg_ops; static struct proto tipc_proto; static struct proto tipc_proto_kern; +DEFINE_SPINLOCK(tipc_port_list_lock); +LIST_HEAD(tipc_socks); + /* * Revised TIPC socket locking policy: * @@ -156,6 +159,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, struct sock *sk; struct tipc_sock *tsk; struct tipc_port *port; + struct tipc_msg *msg; u32 ref; /* Validate arguments */ @@ -191,18 +195,28 @@ static int tipc_sk_create(struct net *net, struct socket *sock, tsk = tipc_sk(sk); port = &tsk->port; - - ref = tipc_port_init(port, TIPC_LOW_IMPORTANCE); + ref = tipc_ref_acquire(port, &port->lock); if (!ref) { - pr_warn("Socket registration failed, ref. table exhausted\n"); - sk_free(sk); + pr_warn("Socket create failed; reference table exhausted\n"); return -ENOMEM; } + port->max_pkt = MAX_PKT_DEFAULT; + port->ref = ref; + INIT_LIST_HEAD(&port->publications); + INIT_LIST_HEAD(&port->port_list); + + /* Guard against race during node address update */ + spin_lock_bh(&tipc_port_list_lock); + msg = &port->phdr; + tipc_msg_init(msg, TIPC_LOW_IMPORTANCE, TIPC_NAMED_MSG, + NAMED_H_SIZE, 0); + msg_set_origport(msg, ref); + list_add_tail(&port->port_list, &tipc_socks); + spin_unlock_bh(&tipc_port_list_lock); /* Finish initializing socket data structures */ sock->ops = ops; sock->state = state; - sock_init_data(sock, sk); k_init_timer(&port->timer, (Handler)tipc_sk_timeout, ref); sk->sk_backlog_rcv = tipc_backlog_rcv; @@ -330,6 +344,7 @@ static int tipc_release(struct socket *sock) * Reject all unreceived messages, except on an active connection * (which disconnects locally & sends a 'FIN+' to peer) */ + dnode = tipc_port_peernode(port); while (sock->state != SS_DISCONNECTING) { buf = __skb_dequeue(&sk->sk_receive_queue); if (buf == NULL) @@ -341,18 +356,31 @@ static int tipc_release(struct socket *sock) (sock->state == SS_CONNECTED)) { sock->state = SS_DISCONNECTING; port->connected = 0; - tipc_node_remove_conn(tipc_port_peernode(port), - port->ref); + tipc_node_remove_conn(dnode, port->ref); } if (tipc_msg_reverse(buf, &dnode, TIPC_ERR_NO_PORT)) tipc_link_xmit(buf, dnode, 0); } } - /* Destroy TIPC port; also disconnects an active connection and - * sends a 'FIN-' to peer. - */ - tipc_port_destroy(port); + tipc_withdraw(port, 0, NULL); + spin_lock_bh(port->lock); + tipc_ref_discard(port->ref); + spin_unlock_bh(port->lock); + k_cancel_timer(&port->timer); + if (port->connected) { + buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, + SHORT_H_SIZE, 0, dnode, tipc_own_addr, + tipc_port_peerport(port), + port->ref, TIPC_ERR_NO_PORT); + if (buf) + tipc_link_xmit(buf, dnode, port->ref); + tipc_node_remove_conn(dnode, port->ref); + } + spin_lock_bh(&tipc_port_list_lock); + list_del(&port->port_list); + spin_unlock_bh(&tipc_port_list_lock); + k_term_timer(&port->timer); /* Discard any remaining (connection-based) messages in receive queue */ __skb_queue_purge(&sk->sk_receive_queue); @@ -360,7 +388,6 @@ static int tipc_release(struct socket *sock) /* Reject any messages that accumulated in backlog queue */ sock->state = SS_DISCONNECTING; release_sock(sk); - sock_put(sk); sock->sk = NULL; -- cgit v1.2.3-70-g09d2 From 5a9ee0be3371eb77d671a77e26261931c5c3fb31 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 22 Aug 2014 18:09:14 -0400 Subject: tipc: use registry when scanning sockets The functions tipc_port_get_ports() and tipc_port_reinit() scan over all sockets/ports to access each of them. This is done by using a dedicated linked list, 'tipc_socks' where all sockets are members. The list is in turn protected by a spinlock, 'port_list_lock', while each socket is locked by using port_lock at the moment of access. In order to reduce complexity and risk of deadlock, we want to get rid of the linked list and the accompanying spinlock. This is what we do in this commit. Instead of the linked list, we use the port registry to scan across the sockets. We also add usage of bh_lock_sock() inside the scope of port_lock in both functions, as a preparation for the complete removal of port_lock. Finally, we move the functions from port.c to socket.c, and rename them to tipc_sk_sock_show() and tipc_sk_reinit() repectively. Signed-off-by: Jon Maloy Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- net/tipc/config.c | 4 +- net/tipc/net.c | 2 +- net/tipc/port.c | 87 ---------------------------------------- net/tipc/port.h | 5 --- net/tipc/ref.c | 20 +++++++++ net/tipc/ref.h | 1 + net/tipc/socket.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++----- net/tipc/socket.h | 3 +- 8 files changed, 133 insertions(+), 107 deletions(-) (limited to 'net/tipc/port.c') diff --git a/net/tipc/config.c b/net/tipc/config.c index 2b42403ad33..876f4c6a263 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -35,7 +35,7 @@ */ #include "core.h" -#include "port.h" +#include "socket.h" #include "name_table.h" #include "config.h" #include "server.h" @@ -266,7 +266,7 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area rep_tlv_buf = tipc_media_get_names(); break; case TIPC_CMD_SHOW_PORTS: - rep_tlv_buf = tipc_port_get_ports(); + rep_tlv_buf = tipc_sk_socks_show(); break; case TIPC_CMD_SHOW_STATS: rep_tlv_buf = tipc_show_stats(); diff --git a/net/tipc/net.c b/net/tipc/net.c index 7fcc94998fe..421dd89152a 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -111,7 +111,7 @@ int tipc_net_start(u32 addr) tipc_own_addr = addr; tipc_named_reinit(); - tipc_port_reinit(); + tipc_sk_reinit(); res = tipc_bclink_init(); if (res) return res; diff --git a/net/tipc/port.c b/net/tipc/port.c index 1efa2982d2d..cea3730fe02 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -63,93 +63,6 @@ int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg) (!peernode && (orignode == tipc_own_addr)); } -static int port_print(struct tipc_port *p_ptr, char *buf, int len, int full_id) -{ - struct publication *publ; - int ret; - - if (full_id) - ret = tipc_snprintf(buf, len, "<%u.%u.%u:%u>:", - tipc_zone(tipc_own_addr), - tipc_cluster(tipc_own_addr), - tipc_node(tipc_own_addr), p_ptr->ref); - else - ret = tipc_snprintf(buf, len, "%-10u:", p_ptr->ref); - - if (p_ptr->connected) { - u32 dport = tipc_port_peerport(p_ptr); - u32 destnode = tipc_port_peernode(p_ptr); - - ret += tipc_snprintf(buf + ret, len - ret, - " connected to <%u.%u.%u:%u>", - tipc_zone(destnode), - tipc_cluster(destnode), - tipc_node(destnode), dport); - if (p_ptr->conn_type != 0) - ret += tipc_snprintf(buf + ret, len - ret, - " via {%u,%u}", p_ptr->conn_type, - p_ptr->conn_instance); - } else if (p_ptr->published) { - ret += tipc_snprintf(buf + ret, len - ret, " bound to"); - list_for_each_entry(publ, &p_ptr->publications, pport_list) { - if (publ->lower == publ->upper) - ret += tipc_snprintf(buf + ret, len - ret, - " {%u,%u}", publ->type, - publ->lower); - else - ret += tipc_snprintf(buf + ret, len - ret, - " {%u,%u,%u}", publ->type, - publ->lower, publ->upper); - } - } - ret += tipc_snprintf(buf + ret, len - ret, "\n"); - return ret; -} - -struct sk_buff *tipc_port_get_ports(void) -{ - struct sk_buff *buf; - struct tlv_desc *rep_tlv; - char *pb; - int pb_len; - struct tipc_port *p_ptr; - int str_len = 0; - - buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN)); - if (!buf) - return NULL; - rep_tlv = (struct tlv_desc *)buf->data; - pb = TLV_DATA(rep_tlv); - pb_len = ULTRA_STRING_MAX_LEN; - - spin_lock_bh(&tipc_port_list_lock); - list_for_each_entry(p_ptr, &tipc_socks, port_list) { - spin_lock_bh(p_ptr->lock); - str_len += port_print(p_ptr, pb, pb_len, 0); - spin_unlock_bh(p_ptr->lock); - } - spin_unlock_bh(&tipc_port_list_lock); - str_len += 1; /* for "\0" */ - skb_put(buf, TLV_SPACE(str_len)); - TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); - - return buf; -} - -void tipc_port_reinit(void) -{ - struct tipc_port *p_ptr; - struct tipc_msg *msg; - - spin_lock_bh(&tipc_port_list_lock); - list_for_each_entry(p_ptr, &tipc_socks, port_list) { - msg = &p_ptr->phdr; - msg_set_prevnode(msg, tipc_own_addr); - msg_set_orignode(msg, tipc_own_addr); - } - spin_unlock_bh(&tipc_port_list_lock); -} - int tipc_publish(struct tipc_port *p_ptr, unsigned int scope, struct tipc_name_seq const *seq) { diff --git a/net/tipc/port.h b/net/tipc/port.h index 4c6cfdd3635..4a3c54e5d69 100644 --- a/net/tipc/port.h +++ b/net/tipc/port.h @@ -73,7 +73,6 @@ struct tipc_port { u32 max_pkt; u32 ref; struct tipc_msg phdr; - struct list_head port_list; struct list_head publications; u32 pub_count; u32 probing_state; @@ -81,9 +80,6 @@ struct tipc_port { struct timer_list timer; }; -extern struct list_head tipc_socks; -extern spinlock_t tipc_port_list_lock; - /* * TIPC port manipulation routines */ @@ -100,7 +96,6 @@ int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope, int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg); -struct sk_buff *tipc_port_get_ports(void); void tipc_port_reinit(void); /** diff --git a/net/tipc/ref.c b/net/tipc/ref.c index 3d4ecd754ee..7fc2740846e 100644 --- a/net/tipc/ref.c +++ b/net/tipc/ref.c @@ -264,3 +264,23 @@ void *tipc_ref_lock(u32 ref) } return NULL; } + +/* tipc_ref_lock_next - lock & return next object after referenced one +*/ +void *tipc_ref_lock_next(u32 *ref) +{ + struct reference *entry; + uint index = *ref & tipc_ref_table.index_mask; + + while (++index < tipc_ref_table.capacity) { + entry = &tipc_ref_table.entries[index]; + if (!entry->object) + continue; + spin_lock_bh(&entry->lock); + *ref = entry->ref; + if (entry->object) + return entry->object; + spin_unlock_bh(&entry->lock); + } + return NULL; +} diff --git a/net/tipc/ref.h b/net/tipc/ref.h index d01aa1df63b..e236fa520a1 100644 --- a/net/tipc/ref.h +++ b/net/tipc/ref.h @@ -44,5 +44,6 @@ u32 tipc_ref_acquire(void *object, spinlock_t **lock); void tipc_ref_discard(u32 ref); void *tipc_ref_lock(u32 ref); +void *tipc_ref_lock_next(u32 *ref); #endif diff --git a/net/tipc/socket.c b/net/tipc/socket.c index f2be4c2e20b..ddc2f8c6fce 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -40,6 +40,7 @@ #include "node.h" #include "link.h" #include +#include "config.h" #define SS_LISTENING -1 /* socket is listening */ #define SS_READY -2 /* socket is connectionless */ @@ -63,9 +64,6 @@ static const struct proto_ops msg_ops; static struct proto tipc_proto; static struct proto tipc_proto_kern; -DEFINE_SPINLOCK(tipc_port_list_lock); -LIST_HEAD(tipc_socks); - /* * Revised TIPC socket locking policy: * @@ -113,6 +111,17 @@ LIST_HEAD(tipc_socks); #include "socket.h" +/* tipc_sk_lock_next: find & lock next socket in registry from given port number +*/ +static struct tipc_sock *tipc_sk_lock_next(u32 *ref) +{ + struct tipc_port *port = (struct tipc_port *)tipc_ref_lock_next(ref); + + if (!port) + return NULL; + return tipc_port_to_sock(port); +} + /** * advance_rx_queue - discard first buffer in socket receive queue * @@ -203,16 +212,11 @@ static int tipc_sk_create(struct net *net, struct socket *sock, port->max_pkt = MAX_PKT_DEFAULT; port->ref = ref; INIT_LIST_HEAD(&port->publications); - INIT_LIST_HEAD(&port->port_list); - /* Guard against race during node address update */ - spin_lock_bh(&tipc_port_list_lock); msg = &port->phdr; tipc_msg_init(msg, TIPC_LOW_IMPORTANCE, TIPC_NAMED_MSG, NAMED_H_SIZE, 0); msg_set_origport(msg, ref); - list_add_tail(&port->port_list, &tipc_socks); - spin_unlock_bh(&tipc_port_list_lock); /* Finish initializing socket data structures */ sock->ops = ops; @@ -377,9 +381,6 @@ static int tipc_release(struct socket *sock) tipc_link_xmit(buf, dnode, port->ref); tipc_node_remove_conn(dnode, port->ref); } - spin_lock_bh(&tipc_port_list_lock); - list_del(&port->port_list); - spin_unlock_bh(&tipc_port_list_lock); k_term_timer(&port->timer); /* Discard any remaining (connection-based) messages in receive queue */ @@ -2043,6 +2044,101 @@ static void tipc_sk_timeout(unsigned long ref) tipc_link_xmit(buf, msg_destnode(msg), msg_link_selector(msg)); } +static int tipc_sk_show(struct tipc_port *port, char *buf, + int len, int full_id) +{ + struct publication *publ; + int ret; + + if (full_id) + ret = tipc_snprintf(buf, len, "<%u.%u.%u:%u>:", + tipc_zone(tipc_own_addr), + tipc_cluster(tipc_own_addr), + tipc_node(tipc_own_addr), port->ref); + else + ret = tipc_snprintf(buf, len, "%-10u:", port->ref); + + if (port->connected) { + u32 dport = tipc_port_peerport(port); + u32 destnode = tipc_port_peernode(port); + + ret += tipc_snprintf(buf + ret, len - ret, + " connected to <%u.%u.%u:%u>", + tipc_zone(destnode), + tipc_cluster(destnode), + tipc_node(destnode), dport); + if (port->conn_type != 0) + ret += tipc_snprintf(buf + ret, len - ret, + " via {%u,%u}", port->conn_type, + port->conn_instance); + } else if (port->published) { + ret += tipc_snprintf(buf + ret, len - ret, " bound to"); + list_for_each_entry(publ, &port->publications, pport_list) { + if (publ->lower == publ->upper) + ret += tipc_snprintf(buf + ret, len - ret, + " {%u,%u}", publ->type, + publ->lower); + else + ret += tipc_snprintf(buf + ret, len - ret, + " {%u,%u,%u}", publ->type, + publ->lower, publ->upper); + } + } + ret += tipc_snprintf(buf + ret, len - ret, "\n"); + return ret; +} + +struct sk_buff *tipc_sk_socks_show(void) +{ + struct sk_buff *buf; + struct tlv_desc *rep_tlv; + char *pb; + int pb_len; + struct tipc_sock *tsk; + int str_len = 0; + u32 ref = 0; + + buf = tipc_cfg_reply_alloc(TLV_SPACE(ULTRA_STRING_MAX_LEN)); + if (!buf) + return NULL; + rep_tlv = (struct tlv_desc *)buf->data; + pb = TLV_DATA(rep_tlv); + pb_len = ULTRA_STRING_MAX_LEN; + + tsk = tipc_sk_lock_next(&ref); + for (; tsk; tsk = tipc_sk_lock_next(&ref)) { + bh_lock_sock(&tsk->sk); + str_len += tipc_sk_show(&tsk->port, pb + str_len, + pb_len - str_len, 0); + bh_unlock_sock(&tsk->sk); + tipc_port_unlock(&tsk->port); + } + str_len += 1; /* for "\0" */ + skb_put(buf, TLV_SPACE(str_len)); + TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); + + return buf; +} + +/* tipc_sk_reinit: set non-zero address in all existing sockets + * when we go from standalone to network mode. + */ +void tipc_sk_reinit(void) +{ + struct tipc_msg *msg; + u32 ref = 0; + struct tipc_sock *tsk = tipc_sk_lock_next(&ref); + + for (; tsk; tsk = tipc_sk_lock_next(&ref)) { + bh_lock_sock(&tsk->sk); + msg = &tsk->port.phdr; + msg_set_prevnode(msg, tipc_own_addr); + msg_set_orignode(msg, tipc_own_addr); + bh_unlock_sock(&tsk->sk); + tipc_port_unlock(&tsk->port); + } +} + /** * tipc_setsockopt - set socket option * @sock: socket structure diff --git a/net/tipc/socket.h b/net/tipc/socket.h index 1405633362f..5d515be604a 100644 --- a/net/tipc/socket.h +++ b/net/tipc/socket.h @@ -79,7 +79,8 @@ static inline int tipc_sk_conn_cong(struct tipc_sock *tsk) } int tipc_sk_rcv(struct sk_buff *buf); - +struct sk_buff *tipc_sk_socks_show(void); void tipc_sk_mcast_rcv(struct sk_buff *buf); +void tipc_sk_reinit(void); #endif -- cgit v1.2.3-70-g09d2 From 0fc87aaebdfbf2c75112ce17aec093652c682acd Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Fri, 22 Aug 2014 18:09:17 -0400 Subject: tipc: remove source file port.c In this commit, we move the remaining functions in port.c to socket.c, and give them new names that correspond to their new location. We then remove the file port.c. There are only cosmetic changes to the moved functions. Signed-off-by: Jon Maloy Reviewed-by: Erik Hugne Reviewed-by: Ying Xue Signed-off-by: David S. Miller --- net/tipc/Makefile | 2 +- net/tipc/port.c | 123 ------------------------------------------------------ net/tipc/socket.c | 102 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 97 insertions(+), 130 deletions(-) delete mode 100644 net/tipc/port.c (limited to 'net/tipc/port.c') diff --git a/net/tipc/Makefile b/net/tipc/Makefile index a080c66d819..5206a444022 100644 --- a/net/tipc/Makefile +++ b/net/tipc/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_TIPC) := tipc.o tipc-y += addr.o bcast.o bearer.o config.o \ core.o link.o discover.o msg.o \ name_distr.o subscr.o name_table.o net.o \ - netlink.o node.o node_subscr.o port.o ref.o \ + netlink.o node.o node_subscr.o ref.o \ socket.o log.o eth_media.o server.o tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o diff --git a/net/tipc/port.c b/net/tipc/port.c deleted file mode 100644 index cea3730fe02..00000000000 --- a/net/tipc/port.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * net/tipc/port.c: TIPC port code - * - * Copyright (c) 1992-2007, 2014, Ericsson AB - * Copyright (c) 2004-2008, 2010-2013, Wind River Systems - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the names of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2 as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "core.h" -#include "config.h" -#include "port.h" -#include "name_table.h" -#include "socket.h" - -#define MAX_REJECT_SIZE 1024 - -/** - * tipc_port_peer_msg - verify message was sent by connected port's peer - * - * Handles cases where the node's network address has changed from - * the default of <0.0.0> to its configured setting. - */ -int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg) -{ - u32 peernode; - u32 orignode; - - if (msg_origport(msg) != tipc_port_peerport(p_ptr)) - return 0; - - orignode = msg_orignode(msg); - peernode = tipc_port_peernode(p_ptr); - return (orignode == peernode) || - (!orignode && (peernode == tipc_own_addr)) || - (!peernode && (orignode == tipc_own_addr)); -} - -int tipc_publish(struct tipc_port *p_ptr, unsigned int scope, - struct tipc_name_seq const *seq) -{ - struct publication *publ; - u32 key; - - if (p_ptr->connected) - return -EINVAL; - key = p_ptr->ref + p_ptr->pub_count + 1; - if (key == p_ptr->ref) - return -EADDRINUSE; - - publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper, - scope, p_ptr->ref, key); - if (publ) { - list_add(&publ->pport_list, &p_ptr->publications); - p_ptr->pub_count++; - p_ptr->published = 1; - return 0; - } - return -EINVAL; -} - -int tipc_withdraw(struct tipc_port *p_ptr, unsigned int scope, - struct tipc_name_seq const *seq) -{ - struct publication *publ; - struct publication *tpubl; - int res = -EINVAL; - - if (!seq) { - list_for_each_entry_safe(publ, tpubl, - &p_ptr->publications, pport_list) { - tipc_nametbl_withdraw(publ->type, publ->lower, - publ->ref, publ->key); - } - res = 0; - } else { - list_for_each_entry_safe(publ, tpubl, - &p_ptr->publications, pport_list) { - if (publ->scope != scope) - continue; - if (publ->type != seq->type) - continue; - if (publ->lower != seq->lower) - continue; - if (publ->upper != seq->upper) - break; - tipc_nametbl_withdraw(publ->type, publ->lower, - publ->ref, publ->key); - res = 0; - break; - } - } - if (list_empty(&p_ptr->publications)) - p_ptr->published = 0; - return res; -} diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 7e6240e41e6..ea33eab4fb9 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -57,6 +57,10 @@ static int tipc_release(struct socket *sock); static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags); static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p); static void tipc_sk_timeout(unsigned long ref); +static int tipc_sk_publish(struct tipc_port *port, uint scope, + struct tipc_name_seq const *seq); +static int tipc_sk_withdraw(struct tipc_port *port, uint scope, + struct tipc_name_seq const *seq); static const struct proto_ops packet_ops; static const struct proto_ops stream_ops; @@ -138,6 +142,38 @@ static void reject_rx_queue(struct sock *sk) } } +/* tipc_sk_peer_msg - verify if message was sent by connected port's peer + * + * Handles cases where the node's network address has changed from + * the default of <0.0.0> to its configured setting. + */ +static bool tipc_sk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg) +{ + u32 peer_port = tipc_port_peerport(&tsk->port); + u32 orig_node; + u32 peer_node; + + if (unlikely(!tsk->port.connected)) + return false; + + if (unlikely(msg_origport(msg) != peer_port)) + return false; + + orig_node = msg_orignode(msg); + peer_node = tipc_port_peernode(&tsk->port); + + if (likely(orig_node == peer_node)) + return true; + + if (!orig_node && (peer_node == tipc_own_addr)) + return true; + + if (!peer_node && (orig_node == tipc_own_addr)) + return true; + + return false; +} + /** * tipc_sk_create - create a TIPC socket * @net: network namespace (must be default network) @@ -356,7 +392,7 @@ static int tipc_release(struct socket *sock) } } - tipc_withdraw(port, 0, NULL); + tipc_sk_withdraw(port, 0, NULL); tipc_ref_discard(port->ref); k_cancel_timer(&port->timer); if (port->connected) { @@ -407,7 +443,7 @@ static int tipc_bind(struct socket *sock, struct sockaddr *uaddr, lock_sock(sk); if (unlikely(!uaddr_len)) { - res = tipc_withdraw(&tsk->port, 0, NULL); + res = tipc_sk_withdraw(&tsk->port, 0, NULL); goto exit; } @@ -435,8 +471,8 @@ static int tipc_bind(struct socket *sock, struct sockaddr *uaddr, } res = (addr->scope > 0) ? - tipc_publish(&tsk->port, addr->scope, &addr->addr.nameseq) : - tipc_withdraw(&tsk->port, -addr->scope, &addr->addr.nameseq); + tipc_sk_publish(&tsk->port, addr->scope, &addr->addr.nameseq) : + tipc_sk_withdraw(&tsk->port, -addr->scope, &addr->addr.nameseq); exit: release_sock(sk); return res; @@ -663,7 +699,7 @@ static int tipc_sk_proto_rcv(struct tipc_sock *tsk, u32 *dnode, int conn_cong; /* Ignore if connection cannot be validated: */ - if (!port->connected || !tipc_port_peer_msg(port, msg)) + if (!tipc_sk_peer_msg(tsk, msg)) goto exit; port->probing_state = TIPC_CONN_OK; @@ -1444,7 +1480,7 @@ static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf) switch ((int)sock->state) { case SS_CONNECTED: /* Accept only connection-based messages sent by peer */ - if (msg_connected(msg) && tipc_port_peer_msg(port, msg)) { + if (tipc_sk_peer_msg(tsk, msg)) { if (unlikely(msg_errcode(msg))) { sock->state = SS_DISCONNECTING; port->connected = 0; @@ -2027,6 +2063,60 @@ exit: tipc_sk_put(tsk); } +static int tipc_sk_publish(struct tipc_port *port, uint scope, + struct tipc_name_seq const *seq) +{ + struct publication *publ; + u32 key; + + if (port->connected) + return -EINVAL; + key = port->ref + port->pub_count + 1; + if (key == port->ref) + return -EADDRINUSE; + + publ = tipc_nametbl_publish(seq->type, seq->lower, seq->upper, + scope, port->ref, key); + if (unlikely(!publ)) + return -EINVAL; + + list_add(&publ->pport_list, &port->publications); + port->pub_count++; + port->published = 1; + return 0; +} + +static int tipc_sk_withdraw(struct tipc_port *port, uint scope, + struct tipc_name_seq const *seq) +{ + struct publication *publ; + struct publication *safe; + int rc = -EINVAL; + + list_for_each_entry_safe(publ, safe, &port->publications, pport_list) { + if (seq) { + if (publ->scope != scope) + continue; + if (publ->type != seq->type) + continue; + if (publ->lower != seq->lower) + continue; + if (publ->upper != seq->upper) + break; + tipc_nametbl_withdraw(publ->type, publ->lower, + publ->ref, publ->key); + rc = 0; + break; + } + tipc_nametbl_withdraw(publ->type, publ->lower, + publ->ref, publ->key); + rc = 0; + } + if (list_empty(&port->publications)) + port->published = 0; + return rc; +} + static int tipc_sk_show(struct tipc_port *port, char *buf, int len, int full_id) { -- cgit v1.2.3-70-g09d2