Merge branch 'tipc-comm-groups'

Jon Maloy says:

====================
tipc: Introduce Communcation Group feature

With this commit series we introduce a 'Group Communication' feature in
order to resolve the datagram and multicast flow control problem. This
new feature makes it possible for a user to instantiate multiple private
virtual brokerless message buses by just creating and joining member
sockets.

The main features are as follows:
---------------------------------
- Sockets can join a group via a new setsockopt() call TIPC_GROUP_JOIN.
  If it is the first socket of the group this implies creation of the
  group. This call takes four parameters: 'type' serves as group
  identifier, 'instance' serves as member identifier, and 'scope'
  indicates the visibility of the group (node/cluster/zone). Finally,
  'flags' indicates different options for the socket joining the group.
  For the time being, there are only two such flags: 1) 'LOOPBACK'
  indicates if the creator of the socket wants to receive a copy of
  broadcast or multicast messages it sends to the group, 2) EVENTS
  indicates if it wants to receive membership (JOINED/LEFT) events for
  the other members of the group.

- Groups are closed, i.e., sockets which have not joined a group will
  not be able to send messages to or receive messages from members of
  the group, and vice versa. A socket can only be member of one group
  at a time.

- There are four transmission modes.
  1: Unicast. The sender transmits a message using the port identity
     (node:port tuple) of the receiving socket.
  2: Anycast. The sender transmits a message using a port name (type:
     instance:scope) of one of the receiving sockets. If more than
     one member socket matches the given address a destination is
     selected according to a round-robin algorithm, but also considering
     the destination load (advertised window size) as an additional
     criteria.
  3: Multicast. The sender transmits a message using a port name
     (type:instance:scope) of one or more of the receiving sockets.
     All sockets in the group matching the given address will receive
     a copy of the message.
  4: Broadcast. The sender transmits a message using the primtive
     send(). All members of the group, irrespective of their member
     identity (instance) number receive a copy of the message.

- TIPC broadcast is used for carrying messages in mode 3 or 4 when
  this is deemed more efficient, i.e., depending on number of actual
  destinations.

- All transmission modes are flow controlled, so that messages never
  are dropped or rejected, just like we are used to from connection
  oriented communication. A special algorithm guarantees that this is
  true even for multipoint-to-point communication, i.e., at occasions
  where many source sockets may decide to send simultaneously towards
  the same  destination socket.

- Sequence order is always guaranteed, even between the different
  transmission modes.

- Member join/leave events are received in all other member sockets
  in guaranteed order. I.e., a 'JOINED' (an empty message with the OOB
  bit set) will always be received before the first data message from
  a new member, and a 'LEAVE' (like 'JOINED', but with EOR bit set) will
  always arrive after the last data message from a leaving member.

-----
v2: Reordered variable declarations in descending length order, as per
    feedback from David Miller. This was done as far as permitted by the
    the initialization order.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-10-13 08:46:01 -07:00
commit a00344bd1b
16 changed files with 2000 additions and 286 deletions

View File

@ -231,6 +231,21 @@ struct sockaddr_tipc {
#define TIPC_SOCK_RECVQ_DEPTH 132 /* Default: none (read only) */
#define TIPC_MCAST_BROADCAST 133 /* Default: TIPC selects. No arg */
#define TIPC_MCAST_REPLICAST 134 /* Default: TIPC selects. No arg */
#define TIPC_GROUP_JOIN 135 /* Takes struct tipc_group_req* */
#define TIPC_GROUP_LEAVE 136 /* No argument */
/*
* Flag values
*/
#define TIPC_GROUP_LOOPBACK 0x1 /* Receive copy of sent msg when match */
#define TIPC_GROUP_MEMBER_EVTS 0x2 /* Receive membership events in socket */
struct tipc_group_req {
__u32 type; /* group id */
__u32 instance; /* member id */
__u32 scope; /* zone/cluster/node */
__u32 flags;
};
/*
* Maximum sizes of TIPC bearer-related names (including terminating NULL)

View File

@ -8,7 +8,7 @@ tipc-y += addr.o bcast.o bearer.o \
core.o link.o discover.o msg.o \
name_distr.o subscr.o monitor.o name_table.o net.o \
netlink.o netlink_compat.o node.o socket.o eth_media.o \
server.o socket.o
server.o socket.o group.o
tipc-$(CONFIG_TIPC_MEDIA_UDP) += udp_media.o
tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o

View File

@ -258,20 +258,20 @@ static int tipc_bcast_xmit(struct net *net, struct sk_buff_head *pkts,
static int tipc_rcast_xmit(struct net *net, struct sk_buff_head *pkts,
struct tipc_nlist *dests, u16 *cong_link_cnt)
{
struct tipc_dest *dst, *tmp;
struct sk_buff_head _pkts;
struct u32_item *n, *tmp;
u32 dst, selector;
u32 dnode, selector;
selector = msg_link_selector(buf_msg(skb_peek(pkts)));
skb_queue_head_init(&_pkts);
list_for_each_entry_safe(n, tmp, &dests->list, list) {
dst = n->value;
if (!tipc_msg_pskb_copy(dst, pkts, &_pkts))
list_for_each_entry_safe(dst, tmp, &dests->list, list) {
dnode = dst->node;
if (!tipc_msg_pskb_copy(dnode, pkts, &_pkts))
return -ENOMEM;
/* Any other return value than -ELINKCONG is ignored */
if (tipc_node_xmit(net, &_pkts, dst, selector) == -ELINKCONG)
if (tipc_node_xmit(net, &_pkts, dnode, selector) == -ELINKCONG)
(*cong_link_cnt)++;
}
return 0;
@ -554,7 +554,7 @@ void tipc_nlist_add(struct tipc_nlist *nl, u32 node)
{
if (node == nl->self)
nl->local = true;
else if (u32_push(&nl->list, node))
else if (tipc_dest_push(&nl->list, node, 0))
nl->remote++;
}
@ -562,13 +562,13 @@ void tipc_nlist_del(struct tipc_nlist *nl, u32 node)
{
if (node == nl->self)
nl->local = false;
else if (u32_del(&nl->list, node))
else if (tipc_dest_del(&nl->list, node, 0))
nl->remote--;
}
void tipc_nlist_purge(struct tipc_nlist *nl)
{
u32_list_purge(&nl->list);
tipc_dest_list_purge(&nl->list);
nl->remote = 0;
nl->local = 0;
}

View File

@ -132,6 +132,11 @@ static inline struct list_head *tipc_nodes(struct net *net)
return &tipc_net(net)->node_list;
}
static inline struct tipc_server *tipc_topsrv(struct net *net)
{
return tipc_net(net)->topsrv;
}
static inline unsigned int tipc_hashfn(u32 addr)
{
return addr & (NODE_HTABLE_SIZE - 1);

871
net/tipc/group.c Normal file
View File

@ -0,0 +1,871 @@
/*
* net/tipc/group.c: TIPC group messaging code
*
* Copyright (c) 2017, Ericsson AB
* 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 "addr.h"
#include "group.h"
#include "bcast.h"
#include "server.h"
#include "msg.h"
#include "socket.h"
#include "node.h"
#include "name_table.h"
#include "subscr.h"
#define ADV_UNIT (((MAX_MSG_SIZE + MAX_H_SIZE) / FLOWCTL_BLK_SZ) + 1)
#define ADV_IDLE ADV_UNIT
#define ADV_ACTIVE (ADV_UNIT * 12)
enum mbr_state {
MBR_QUARANTINED,
MBR_DISCOVERED,
MBR_JOINING,
MBR_PUBLISHED,
MBR_JOINED,
MBR_PENDING,
MBR_ACTIVE,
MBR_RECLAIMING,
MBR_REMITTED,
MBR_LEAVING
};
struct tipc_member {
struct rb_node tree_node;
struct list_head list;
struct list_head congested;
struct sk_buff *event_msg;
struct sk_buff_head deferredq;
struct tipc_group *group;
u32 node;
u32 port;
u32 instance;
enum mbr_state state;
u16 advertised;
u16 window;
u16 bc_rcv_nxt;
u16 bc_syncpt;
u16 bc_acked;
bool usr_pending;
};
struct tipc_group {
struct rb_root members;
struct list_head congested;
struct list_head pending;
struct list_head active;
struct list_head reclaiming;
struct tipc_nlist dests;
struct net *net;
int subid;
u32 type;
u32 instance;
u32 domain;
u32 scope;
u32 portid;
u16 member_cnt;
u16 active_cnt;
u16 max_active;
u16 bc_snd_nxt;
u16 bc_ackers;
bool loopback;
bool events;
};
static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m,
int mtyp, struct sk_buff_head *xmitq);
static void tipc_group_decr_active(struct tipc_group *grp,
struct tipc_member *m)
{
if (m->state == MBR_ACTIVE || m->state == MBR_RECLAIMING)
grp->active_cnt--;
}
static int tipc_group_rcvbuf_limit(struct tipc_group *grp)
{
int max_active, active_pool, idle_pool;
int mcnt = grp->member_cnt + 1;
/* Limit simultaneous reception from other members */
max_active = min(mcnt / 8, 64);
max_active = max(max_active, 16);
grp->max_active = max_active;
/* Reserve blocks for active and idle members */
active_pool = max_active * ADV_ACTIVE;
idle_pool = (mcnt - max_active) * ADV_IDLE;
/* Scale to bytes, considering worst-case truesize/msgsize ratio */
return (active_pool + idle_pool) * FLOWCTL_BLK_SZ * 4;
}
u16 tipc_group_bc_snd_nxt(struct tipc_group *grp)
{
return grp->bc_snd_nxt;
}
static bool tipc_group_is_enabled(struct tipc_member *m)
{
return m->state != MBR_QUARANTINED && m->state != MBR_LEAVING;
}
static bool tipc_group_is_receiver(struct tipc_member *m)
{
return m && m->state >= MBR_JOINED;
}
u32 tipc_group_exclude(struct tipc_group *grp)
{
if (!grp->loopback)
return grp->portid;
return 0;
}
int tipc_group_size(struct tipc_group *grp)
{
return grp->member_cnt;
}
struct tipc_group *tipc_group_create(struct net *net, u32 portid,
struct tipc_group_req *mreq)
{
struct tipc_group *grp;
u32 type = mreq->type;
grp = kzalloc(sizeof(*grp), GFP_ATOMIC);
if (!grp)
return NULL;
tipc_nlist_init(&grp->dests, tipc_own_addr(net));
INIT_LIST_HEAD(&grp->congested);
INIT_LIST_HEAD(&grp->active);
INIT_LIST_HEAD(&grp->pending);
INIT_LIST_HEAD(&grp->reclaiming);
grp->members = RB_ROOT;
grp->net = net;
grp->portid = portid;
grp->domain = addr_domain(net, mreq->scope);
grp->type = type;
grp->instance = mreq->instance;
grp->scope = mreq->scope;
grp->loopback = mreq->flags & TIPC_GROUP_LOOPBACK;
grp->events = mreq->flags & TIPC_GROUP_MEMBER_EVTS;
if (tipc_topsrv_kern_subscr(net, portid, type, 0, ~0, &grp->subid))
return grp;
kfree(grp);
return NULL;
}
void tipc_group_delete(struct net *net, struct tipc_group *grp)
{
struct rb_root *tree = &grp->members;
struct tipc_member *m, *tmp;
struct sk_buff_head xmitq;
__skb_queue_head_init(&xmitq);
rbtree_postorder_for_each_entry_safe(m, tmp, tree, tree_node) {
tipc_group_proto_xmit(grp, m, GRP_LEAVE_MSG, &xmitq);
list_del(&m->list);
kfree(m);
}
tipc_node_distr_xmit(net, &xmitq);
tipc_nlist_purge(&grp->dests);
tipc_topsrv_kern_unsubscr(net, grp->subid);
kfree(grp);
}
struct tipc_member *tipc_group_find_member(struct tipc_group *grp,
u32 node, u32 port)
{
struct rb_node *n = grp->members.rb_node;
u64 nkey, key = (u64)node << 32 | port;
struct tipc_member *m;
while (n) {
m = container_of(n, struct tipc_member, tree_node);
nkey = (u64)m->node << 32 | m->port;
if (key < nkey)
n = n->rb_left;
else if (key > nkey)
n = n->rb_right;
else
return m;
}
return NULL;
}
static struct tipc_member *tipc_group_find_dest(struct tipc_group *grp,
u32 node, u32 port)
{
struct tipc_member *m;
m = tipc_group_find_member(grp, node, port);
if (m && tipc_group_is_enabled(m))
return m;
return NULL;
}
static struct tipc_member *tipc_group_find_node(struct tipc_group *grp,
u32 node)
{
struct tipc_member *m;
struct rb_node *n;
for (n = rb_first(&grp->members); n; n = rb_next(n)) {
m = container_of(n, struct tipc_member, tree_node);
if (m->node == node)
return m;
}
return NULL;
}
static void tipc_group_add_to_tree(struct tipc_group *grp,
struct tipc_member *m)
{
u64 nkey, key = (u64)m->node << 32 | m->port;
struct rb_node **n, *parent = NULL;
struct tipc_member *tmp;
n = &grp->members.rb_node;
while (*n) {
tmp = container_of(*n, struct tipc_member, tree_node);
parent = *n;
tmp = container_of(parent, struct tipc_member, tree_node);
nkey = (u64)tmp->node << 32 | tmp->port;
if (key < nkey)
n = &(*n)->rb_left;
else if (key > nkey)
n = &(*n)->rb_right;
else
return;
}
rb_link_node(&m->tree_node, parent, n);
rb_insert_color(&m->tree_node, &grp->members);
}
static struct tipc_member *tipc_group_create_member(struct tipc_group *grp,
u32 node, u32 port,
int state)
{
struct tipc_member *m;
m = kzalloc(sizeof(*m), GFP_ATOMIC);
if (!m)
return NULL;
INIT_LIST_HEAD(&m->list);
INIT_LIST_HEAD(&m->congested);
__skb_queue_head_init(&m->deferredq);
m->group = grp;
m->node = node;
m->port = port;
m->bc_acked = grp->bc_snd_nxt - 1;
grp->member_cnt++;
tipc_group_add_to_tree(grp, m);
tipc_nlist_add(&grp->dests, m->node);
m->state = state;
return m;
}
void tipc_group_add_member(struct tipc_group *grp, u32 node, u32 port)
{
tipc_group_create_member(grp, node, port, MBR_DISCOVERED);
}
static void tipc_group_delete_member(struct tipc_group *grp,
struct tipc_member *m)
{
rb_erase(&m->tree_node, &grp->members);
grp->member_cnt--;
/* Check if we were waiting for replicast ack from this member */
if (grp->bc_ackers && less(m->bc_acked, grp->bc_snd_nxt - 1))
grp->bc_ackers--;
list_del_init(&m->list);
list_del_init(&m->congested);
tipc_group_decr_active(grp, m);
/* If last member on a node, remove node from dest list */
if (!tipc_group_find_node(grp, m->node))
tipc_nlist_del(&grp->dests, m->node);
kfree(m);
}
struct tipc_nlist *tipc_group_dests(struct tipc_group *grp)
{
return &grp->dests;
}
void tipc_group_self(struct tipc_group *grp, struct tipc_name_seq *seq,
int *scope)
{
seq->type = grp->type;
seq->lower = grp->instance;
seq->upper = grp->instance;
*scope = grp->scope;
}
void tipc_group_update_member(struct tipc_member *m, int len)
{
struct tipc_group *grp = m->group;
struct tipc_member *_m, *tmp;
if (!tipc_group_is_enabled(m))
return;
m->window -= len;
if (m->window >= ADV_IDLE)
return;
if (!list_empty(&m->congested))
return;
/* Sort member into congested members' list */
list_for_each_entry_safe(_m, tmp, &grp->congested, congested) {
if (m->window > _m->window)
continue;
list_add_tail(&m->congested, &_m->congested);
return;
}
list_add_tail(&m->congested, &grp->congested);
}
void tipc_group_update_bc_members(struct tipc_group *grp, int len, bool ack)
{
u16 prev = grp->bc_snd_nxt - 1;
struct tipc_member *m;
struct rb_node *n;
for (n = rb_first(&grp->members); n; n = rb_next(n)) {
m = container_of(n, struct tipc_member, tree_node);
if (tipc_group_is_enabled(m)) {
tipc_group_update_member(m, len);
m->bc_acked = prev;
}
}
/* Mark number of acknowledges to expect, if any */
if (ack)
grp->bc_ackers = grp->member_cnt;
grp->bc_snd_nxt++;
}
bool tipc_group_cong(struct tipc_group *grp, u32 dnode, u32 dport,
int len, struct tipc_member **mbr)
{
struct sk_buff_head xmitq;
struct tipc_member *m;
int adv, state;
m = tipc_group_find_dest(grp, dnode, dport);
*mbr = m;
if (!m)
return false;
if (m->usr_pending)
return true;
if (m->window >= len)
return false;
m->usr_pending = true;
/* If not fully advertised, do it now to prevent mutual blocking */
adv = m->advertised;
state = m->state;
if (state < MBR_JOINED)
return true;
if (state == MBR_JOINED && adv == ADV_IDLE)
return true;
if (state == MBR_ACTIVE && adv == ADV_ACTIVE)
return true;
if (state == MBR_PENDING && adv == ADV_IDLE)
return true;
skb_queue_head_init(&xmitq);
tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, &xmitq);
tipc_node_distr_xmit(grp->net, &xmitq);
return true;
}
bool tipc_group_bc_cong(struct tipc_group *grp, int len)
{
struct tipc_member *m = NULL;
/* If prev bcast was replicast, reject until all receivers have acked */
if (grp->bc_ackers)
return true;
if (list_empty(&grp->congested))
return false;
m = list_first_entry(&grp->congested, struct tipc_member, congested);
if (m->window >= len)
return false;
return tipc_group_cong(grp, m->node, m->port, len, &m);
}
/* tipc_group_sort_msg() - sort msg into queue by bcast sequence number
*/
static void tipc_group_sort_msg(struct sk_buff *skb, struct sk_buff_head *defq)
{
struct tipc_msg *_hdr, *hdr = buf_msg(skb);
u16 bc_seqno = msg_grp_bc_seqno(hdr);
struct sk_buff *_skb, *tmp;
int mtyp = msg_type(hdr);
/* Bcast/mcast may be bypassed by ucast or other bcast, - sort it in */
if (mtyp == TIPC_GRP_BCAST_MSG || mtyp == TIPC_GRP_MCAST_MSG) {
skb_queue_walk_safe(defq, _skb, tmp) {
_hdr = buf_msg(_skb);
if (!less(bc_seqno, msg_grp_bc_seqno(_hdr)))
continue;
__skb_queue_before(defq, _skb, skb);
return;
}
/* Bcast was not bypassed, - add to tail */
}
/* Unicasts are never bypassed, - always add to tail */
__skb_queue_tail(defq, skb);
}
/* tipc_group_filter_msg() - determine if we should accept arriving message
*/
void tipc_group_filter_msg(struct tipc_group *grp, struct sk_buff_head *inputq,
struct sk_buff_head *xmitq)
{
struct sk_buff *skb = __skb_dequeue(inputq);
bool ack, deliver, update, leave = false;
struct sk_buff_head *defq;
struct tipc_member *m;
struct tipc_msg *hdr;
u32 node, port;
int mtyp, blks;
if (!skb)
return;
hdr = buf_msg(skb);
node = msg_orignode(hdr);
port = msg_origport(hdr);
if (!msg_in_group(hdr))
goto drop;
m = tipc_group_find_member(grp, node, port);
if (!tipc_group_is_receiver(m))
goto drop;
if (less(msg_grp_bc_seqno(hdr), m->bc_rcv_nxt))
goto drop;
TIPC_SKB_CB(skb)->orig_member = m->instance;
defq = &m->deferredq;
tipc_group_sort_msg(skb, defq);
while ((skb = skb_peek(defq))) {
hdr = buf_msg(skb);
mtyp = msg_type(hdr);
deliver = true;
ack = false;
update = false;
if (more(msg_grp_bc_seqno(hdr), m->bc_rcv_nxt))
break;
/* Decide what to do with message */
switch (mtyp) {
case TIPC_GRP_MCAST_MSG:
if (msg_nameinst(hdr) != grp->instance) {
update = true;
deliver = false;
}
/* Fall thru */
case TIPC_GRP_BCAST_MSG:
m->bc_rcv_nxt++;
ack = msg_grp_bc_ack_req(hdr);
break;
case TIPC_GRP_UCAST_MSG:
break;
case TIPC_GRP_MEMBER_EVT:
if (m->state == MBR_LEAVING)
leave = true;
if (!grp->events)
deliver = false;
break;
default:
break;
}
/* Execute decisions */
__skb_dequeue(defq);
if (deliver)
__skb_queue_tail(inputq, skb);
else
kfree_skb(skb);
if (ack)
tipc_group_proto_xmit(grp, m, GRP_ACK_MSG, xmitq);
if (leave) {
tipc_group_delete_member(grp, m);
__skb_queue_purge(defq);
break;
}
if (!update)
continue;
blks = msg_blocks(hdr);
tipc_group_update_rcv_win(grp, blks, node, port, xmitq);
}
return;
drop:
kfree_skb(skb);
}
void tipc_group_update_rcv_win(struct tipc_group *grp, int blks, u32 node,
u32 port, struct sk_buff_head *xmitq)
{
struct list_head *active = &grp->active;
int max_active = grp->max_active;
int reclaim_limit = max_active * 3 / 4;
int active_cnt = grp->active_cnt;
struct tipc_member *m, *rm;
m = tipc_group_find_member(grp, node, port);
if (!m)
return;
m->advertised -= blks;
switch (m->state) {
case MBR_JOINED:
/* Reclaim advertised space from least active member */
if (!list_empty(active) && active_cnt >= reclaim_limit) {
rm = list_first_entry(active, struct tipc_member, list);
rm->state = MBR_RECLAIMING;
list_move_tail(&rm->list, &grp->reclaiming);
tipc_group_proto_xmit(grp, rm, GRP_RECLAIM_MSG, xmitq);
}
/* If max active, become pending and wait for reclaimed space */
if (active_cnt >= max_active) {
m->state = MBR_PENDING;
list_add_tail(&m->list, &grp->pending);
break;
}
/* Otherwise become active */
m->state = MBR_ACTIVE;
list_add_tail(&m->list, &grp->active);
grp->active_cnt++;
/* Fall through */
case MBR_ACTIVE:
if (!list_is_last(&m->list, &grp->active))
list_move_tail(&m->list, &grp->active);
if (m->advertised > (ADV_ACTIVE * 3 / 4))
break;
tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
break;
case MBR_REMITTED:
if (m->advertised > ADV_IDLE)
break;
m->state = MBR_JOINED;
if (m->advertised < ADV_IDLE) {
pr_warn_ratelimited("Rcv unexpected msg after REMIT\n");
tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
}
break;
case MBR_RECLAIMING:
case MBR_DISCOVERED:
case MBR_JOINING:
case MBR_LEAVING:
default:
break;
}
}
static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m,
int mtyp, struct sk_buff_head *xmitq)
{
struct tipc_msg *hdr;
struct sk_buff *skb;
int adv = 0;
skb = tipc_msg_create(GROUP_PROTOCOL, mtyp, INT_H_SIZE, 0,
m->node, tipc_own_addr(grp->net),
m->port, grp->portid, 0);
if (!skb)
return;
if (m->state == MBR_ACTIVE)
adv = ADV_ACTIVE - m->advertised;
else if (m->state == MBR_JOINED || m->state == MBR_PENDING)
adv = ADV_IDLE - m->advertised;
hdr = buf_msg(skb);
if (mtyp == GRP_JOIN_MSG) {
msg_set_grp_bc_syncpt(hdr, grp->bc_snd_nxt);
msg_set_adv_win(hdr, adv);
m->advertised += adv;
} else if (mtyp == GRP_LEAVE_MSG) {
msg_set_grp_bc_syncpt(hdr, grp->bc_snd_nxt);
} else if (mtyp == GRP_ADV_MSG) {
msg_set_adv_win(hdr, adv);
m->advertised += adv;
} else if (mtyp == GRP_ACK_MSG) {
msg_set_grp_bc_acked(hdr, m->bc_rcv_nxt);
} else if (mtyp == GRP_REMIT_MSG) {
msg_set_grp_remitted(hdr, m->window);
}
__skb_queue_tail(xmitq, skb);
}
void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup,
struct tipc_msg *hdr, struct sk_buff_head *inputq,
struct sk_buff_head *xmitq)
{
u32 node = msg_orignode(hdr);
u32 port = msg_origport(hdr);
struct tipc_member *m, *pm;
struct tipc_msg *ehdr;
u16 remitted, in_flight;
if (!grp)
return;
m = tipc_group_find_member(grp, node, port);
switch (msg_type(hdr)) {
case GRP_JOIN_MSG:
if (!m)
m = tipc_group_create_member(grp, node, port,
MBR_QUARANTINED);
if (!m)
return;
m->bc_syncpt = msg_grp_bc_syncpt(hdr);
m->bc_rcv_nxt = m->bc_syncpt;
m->window += msg_adv_win(hdr);
/* Wait until PUBLISH event is received */
if (m->state == MBR_DISCOVERED) {
m->state = MBR_JOINING;
} else if (m->state == MBR_PUBLISHED) {
m->state = MBR_JOINED;
*usr_wakeup = true;
m->usr_pending = false;
tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
ehdr = buf_msg(m->event_msg);
msg_set_grp_bc_seqno(ehdr, m->bc_syncpt);
__skb_queue_tail(inputq, m->event_msg);
}
if (m->window < ADV_IDLE)
tipc_group_update_member(m, 0);
else
list_del_init(&m->congested);
return;
case GRP_LEAVE_MSG:
if (!m)
return;
m->bc_syncpt = msg_grp_bc_syncpt(hdr);
/* Wait until WITHDRAW event is received */
if (m->state != MBR_LEAVING) {
tipc_group_decr_active(grp, m);
m->state = MBR_LEAVING;
return;
}
/* Otherwise deliver already received WITHDRAW event */
ehdr = buf_msg(m->event_msg);
msg_set_grp_bc_seqno(ehdr, m->bc_syncpt);
__skb_queue_tail(inputq, m->event_msg);
*usr_wakeup = true;
list_del_init(&m->congested);
return;
case GRP_ADV_MSG:
if (!m)
return;
m->window += msg_adv_win(hdr);
*usr_wakeup = m->usr_pending;
m->usr_pending = false;
list_del_init(&m->congested);
return;
case GRP_ACK_MSG:
if (!m)
return;
m->bc_acked = msg_grp_bc_acked(hdr);
if (--grp->bc_ackers)
break;
*usr_wakeup = true;
m->usr_pending = false;
return;
case GRP_RECLAIM_MSG:
if (!m)
return;
*usr_wakeup = m->usr_pending;
m->usr_pending = false;
tipc_group_proto_xmit(grp, m, GRP_REMIT_MSG, xmitq);
m->window = ADV_IDLE;
return;
case GRP_REMIT_MSG:
if (!m || m->state != MBR_RECLAIMING)
return;
list_del_init(&m->list);
grp->active_cnt--;
remitted = msg_grp_remitted(hdr);
/* Messages preceding the REMIT still in receive queue */
if (m->advertised > remitted) {
m->state = MBR_REMITTED;
in_flight = m->advertised - remitted;
}
/* All messages preceding the REMIT have been read */
if (m->advertised <= remitted) {
m->state = MBR_JOINED;
in_flight = 0;
}
/* ..and the REMIT overtaken by more messages => re-advertise */
if (m->advertised < remitted)
tipc_group_proto_xmit(grp, m, GRP_ADV_MSG, xmitq);
m->advertised = ADV_IDLE + in_flight;
/* Set oldest pending member to active and advertise */
if (list_empty(&grp->pending))
return;
pm = list_first_entry(&grp->pending, struct tipc_member, list);
pm->state = MBR_ACTIVE;
list_move_tail(&pm->list, &grp->active);
grp->active_cnt++;
if (pm->advertised <= (ADV_ACTIVE * 3 / 4))
tipc_group_proto_xmit(grp, pm, GRP_ADV_MSG, xmitq);
return;
default:
pr_warn("Received unknown GROUP_PROTO message\n");
}
}
/* tipc_group_member_evt() - receive and handle a member up/down event
*/
void tipc_group_member_evt(struct tipc_group *grp,
bool *usr_wakeup,
int *sk_rcvbuf,
struct sk_buff *skb,
struct sk_buff_head *inputq,
struct sk_buff_head *xmitq)
{
struct tipc_msg *hdr = buf_msg(skb);
struct tipc_event *evt = (void *)msg_data(hdr);
u32 instance = evt->found_lower;
u32 node = evt->port.node;
u32 port = evt->port.ref;
int event = evt->event;
struct tipc_member *m;
struct net *net;
bool node_up;
u32 self;
if (!grp)
goto drop;
net = grp->net;
self = tipc_own_addr(net);
if (!grp->loopback && node == self && port == grp->portid)
goto drop;
/* Convert message before delivery to user */
msg_set_hdr_sz(hdr, GROUP_H_SIZE);
msg_set_user(hdr, TIPC_CRITICAL_IMPORTANCE);
msg_set_type(hdr, TIPC_GRP_MEMBER_EVT);
msg_set_origport(hdr, port);
msg_set_orignode(hdr, node);
msg_set_nametype(hdr, grp->type);
msg_set_grp_evt(hdr, event);
m = tipc_group_find_member(grp, node, port);
if (event == TIPC_PUBLISHED) {
if (!m)
m = tipc_group_create_member(grp, node, port,
MBR_DISCOVERED);
if (!m)
goto drop;
/* Hold back event if JOIN message not yet received */
if (m->state == MBR_DISCOVERED) {
m->event_msg = skb;
m->state = MBR_PUBLISHED;
} else {
msg_set_grp_bc_seqno(hdr, m->bc_syncpt);
__skb_queue_tail(inputq, skb);
m->state = MBR_JOINED;
*usr_wakeup = true;
m->usr_pending = false;
}
m->instance = instance;
TIPC_SKB_CB(skb)->orig_member = m->instance;
tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, xmitq);
if (m->window < ADV_IDLE)
tipc_group_update_member(m, 0);
else
list_del_init(&m->congested);
} else if (event == TIPC_WITHDRAWN) {
if (!m)
goto drop;
TIPC_SKB_CB(skb)->orig_member = m->instance;
*usr_wakeup = true;
m->usr_pending = false;
node_up = tipc_node_is_up(net, node);
/* Hold back event if more messages might be expected */
if (m->state != MBR_LEAVING && node_up) {
m->event_msg = skb;
tipc_group_decr_active(grp, m);
m->state = MBR_LEAVING;
} else {
if (node_up)
msg_set_grp_bc_seqno(hdr, m->bc_syncpt);
else
msg_set_grp_bc_seqno(hdr, m->bc_rcv_nxt);
__skb_queue_tail(inputq, skb);
}
list_del_init(&m->congested);
}
*sk_rcvbuf = tipc_group_rcvbuf_limit(grp);
return;
drop:
kfree_skb(skb);
}

73
net/tipc/group.h Normal file
View File

@ -0,0 +1,73 @@
/*
* net/tipc/group.h: Include file for TIPC group unicast/multicast functions
*
* Copyright (c) 2017, Ericsson AB
* 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.
*/
#ifndef _TIPC_GROUP_H
#define _TIPC_GROUP_H
#include "core.h"
struct tipc_group;
struct tipc_member;
struct tipc_msg;
struct tipc_group *tipc_group_create(struct net *net, u32 portid,
struct tipc_group_req *mreq);
void tipc_group_delete(struct net *net, struct tipc_group *grp);
void tipc_group_add_member(struct tipc_group *grp, u32 node, u32 port);
struct tipc_nlist *tipc_group_dests(struct tipc_group *grp);
void tipc_group_self(struct tipc_group *grp, struct tipc_name_seq *seq,
int *scope);
u32 tipc_group_exclude(struct tipc_group *grp);
void tipc_group_filter_msg(struct tipc_group *grp,
struct sk_buff_head *inputq,
struct sk_buff_head *xmitq);
void tipc_group_member_evt(struct tipc_group *grp, bool *wakeup,
int *sk_rcvbuf, struct sk_buff *skb,
struct sk_buff_head *inputq,
struct sk_buff_head *xmitq);
void tipc_group_proto_rcv(struct tipc_group *grp, bool *wakeup,
struct tipc_msg *hdr,
struct sk_buff_head *inputq,
struct sk_buff_head *xmitq);
void tipc_group_update_bc_members(struct tipc_group *grp, int len, bool ack);
bool tipc_group_cong(struct tipc_group *grp, u32 dnode, u32 dport,
int len, struct tipc_member **m);
bool tipc_group_bc_cong(struct tipc_group *grp, int len);
void tipc_group_update_rcv_win(struct tipc_group *grp, int blks, u32 node,
u32 port, struct sk_buff_head *xmitq);
u16 tipc_group_bc_snd_nxt(struct tipc_group *grp);
void tipc_group_update_member(struct tipc_member *m, int len);
int tipc_group_size(struct tipc_group *grp);
#endif

View File

@ -1039,6 +1039,7 @@ int tipc_link_retrans(struct tipc_link *l, struct tipc_link *nacker,
static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,
struct sk_buff_head *inputq)
{
struct sk_buff_head *mc_inputq = l->bc_rcvlink->inputq;
struct tipc_msg *hdr = buf_msg(skb);
switch (msg_user(hdr)) {
@ -1046,12 +1047,14 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb,
case TIPC_MEDIUM_IMPORTANCE:
case TIPC_HIGH_IMPORTANCE:
case TIPC_CRITICAL_IMPORTANCE:
if (unlikely(msg_type(hdr) == TIPC_MCAST_MSG)) {
skb_queue_tail(l->bc_rcvlink->inputq, skb);
if (unlikely(msg_in_group(hdr) || msg_mcast(hdr))) {
skb_queue_tail(mc_inputq, skb);
return true;
}
case CONN_MANAGER:
skb_queue_tail(inputq, skb);
return true;
case GROUP_PROTOCOL:
skb_queue_tail(mc_inputq, skb);
return true;
case NAME_DISTRIBUTOR:
l->bc_rcvlink->state = LINK_ESTABLISHED;

View File

@ -666,3 +666,10 @@ void __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno,
}
kfree_skb(skb);
}
void tipc_skb_reject(struct net *net, int err, struct sk_buff *skb,
struct sk_buff_head *xmitq)
{
if (tipc_msg_reverse(tipc_own_addr(net), &skb, err))
__skb_queue_tail(xmitq, skb);
}

View File

@ -1,7 +1,7 @@
/*
* net/tipc/msg.h: Include file for TIPC message header routines
*
* Copyright (c) 2000-2007, 2014-2015 Ericsson AB
* Copyright (c) 2000-2007, 2014-2017 Ericsson AB
* Copyright (c) 2005-2008, 2010-2011, Wind River Systems
* All rights reserved.
*
@ -61,10 +61,14 @@ struct plist;
/*
* Payload message types
*/
#define TIPC_CONN_MSG 0
#define TIPC_MCAST_MSG 1
#define TIPC_NAMED_MSG 2
#define TIPC_DIRECT_MSG 3
#define TIPC_CONN_MSG 0
#define TIPC_MCAST_MSG 1
#define TIPC_NAMED_MSG 2
#define TIPC_DIRECT_MSG 3
#define TIPC_GRP_MEMBER_EVT 4
#define TIPC_GRP_BCAST_MSG 5
#define TIPC_GRP_MCAST_MSG 6
#define TIPC_GRP_UCAST_MSG 7
/*
* Internal message users
@ -73,11 +77,13 @@ struct plist;
#define MSG_BUNDLER 6
#define LINK_PROTOCOL 7
#define CONN_MANAGER 8
#define GROUP_PROTOCOL 9
#define TUNNEL_PROTOCOL 10
#define NAME_DISTRIBUTOR 11
#define MSG_FRAGMENTER 12
#define LINK_CONFIG 13
#define SOCK_WAKEUP 14 /* pseudo user */
#define TOP_SRV 15 /* pseudo user */
/*
* Message header sizes
@ -86,6 +92,7 @@ struct plist;
#define BASIC_H_SIZE 32 /* Basic payload message */
#define NAMED_H_SIZE 40 /* Named payload message */
#define MCAST_H_SIZE 44 /* Multicast payload message */
#define GROUP_H_SIZE 44 /* Group payload message */
#define INT_H_SIZE 40 /* Internal messages */
#define MIN_H_SIZE 24 /* Smallest legal TIPC header size */
#define MAX_H_SIZE 60 /* Largest possible TIPC header size */
@ -96,6 +103,7 @@ struct plist;
struct tipc_skb_cb {
u32 bytes_read;
u32 orig_member;
struct sk_buff *tail;
bool validated;
u16 chain_imp;
@ -188,6 +196,11 @@ static inline u32 msg_size(struct tipc_msg *m)
return msg_bits(m, 0, 0, 0x1ffff);
}
static inline u32 msg_blocks(struct tipc_msg *m)
{
return (msg_size(m) / 1024) + 1;
}
static inline u32 msg_data_sz(struct tipc_msg *m)
{
return msg_size(m) - msg_hdr_sz(m);
@ -251,6 +264,18 @@ static inline void msg_set_type(struct tipc_msg *m, u32 n)
msg_set_bits(m, 1, 29, 0x7, n);
}
static inline int msg_in_group(struct tipc_msg *m)
{
int mtyp = msg_type(m);
return mtyp >= TIPC_GRP_MEMBER_EVT && mtyp <= TIPC_GRP_UCAST_MSG;
}
static inline bool msg_is_grp_evt(struct tipc_msg *m)
{
return msg_type(m) == TIPC_GRP_MEMBER_EVT;
}
static inline u32 msg_named(struct tipc_msg *m)
{
return msg_type(m) == TIPC_NAMED_MSG;
@ -258,7 +283,10 @@ static inline u32 msg_named(struct tipc_msg *m)
static inline u32 msg_mcast(struct tipc_msg *m)
{
return msg_type(m) == TIPC_MCAST_MSG;
int mtyp = msg_type(m);
return ((mtyp == TIPC_MCAST_MSG) || (mtyp == TIPC_GRP_BCAST_MSG) ||
(mtyp == TIPC_GRP_MCAST_MSG));
}
static inline u32 msg_connected(struct tipc_msg *m)
@ -513,6 +541,16 @@ static inline void msg_set_nameupper(struct tipc_msg *m, u32 n)
#define DSC_REQ_MSG 0
#define DSC_RESP_MSG 1
/*
* Group protocol message types
*/
#define GRP_JOIN_MSG 0
#define GRP_LEAVE_MSG 1
#define GRP_ADV_MSG 2
#define GRP_ACK_MSG 3
#define GRP_RECLAIM_MSG 4
#define GRP_REMIT_MSG 5
/*
* Word 1
*/
@ -764,12 +802,12 @@ static inline void msg_set_conn_ack(struct tipc_msg *m, u32 n)
msg_set_bits(m, 9, 16, 0xffff, n);
}
static inline u32 msg_adv_win(struct tipc_msg *m)
static inline u16 msg_adv_win(struct tipc_msg *m)
{
return msg_bits(m, 9, 0, 0xffff);
}
static inline void msg_set_adv_win(struct tipc_msg *m, u32 n)
static inline void msg_set_adv_win(struct tipc_msg *m, u16 n)
{
msg_set_bits(m, 9, 0, 0xffff, n);
}
@ -794,6 +832,68 @@ static inline void msg_set_link_tolerance(struct tipc_msg *m, u32 n)
msg_set_bits(m, 9, 0, 0xffff, n);
}
static inline u16 msg_grp_bc_syncpt(struct tipc_msg *m)
{
return msg_bits(m, 9, 16, 0xffff);
}
static inline void msg_set_grp_bc_syncpt(struct tipc_msg *m, u16 n)
{
msg_set_bits(m, 9, 16, 0xffff, n);
}
static inline u16 msg_grp_bc_acked(struct tipc_msg *m)
{
return msg_bits(m, 9, 16, 0xffff);
}
static inline void msg_set_grp_bc_acked(struct tipc_msg *m, u16 n)
{
msg_set_bits(m, 9, 16, 0xffff, n);
}
static inline u16 msg_grp_remitted(struct tipc_msg *m)
{
return msg_bits(m, 9, 16, 0xffff);
}
static inline void msg_set_grp_remitted(struct tipc_msg *m, u16 n)
{
msg_set_bits(m, 9, 16, 0xffff, n);
}
/* Word 10
*/
static inline u16 msg_grp_evt(struct tipc_msg *m)
{
return msg_bits(m, 10, 0, 0x3);
}
static inline void msg_set_grp_evt(struct tipc_msg *m, int n)
{
msg_set_bits(m, 10, 0, 0x3, n);
}
static inline u16 msg_grp_bc_ack_req(struct tipc_msg *m)
{
return msg_bits(m, 10, 0, 0x1);
}
static inline void msg_set_grp_bc_ack_req(struct tipc_msg *m, bool n)
{
msg_set_bits(m, 10, 0, 0x1, n);
}
static inline u16 msg_grp_bc_seqno(struct tipc_msg *m)
{
return msg_bits(m, 10, 16, 0xffff);
}
static inline void msg_set_grp_bc_seqno(struct tipc_msg *m, u32 n)
{
msg_set_bits(m, 10, 16, 0xffff, n);
}
static inline bool msg_peer_link_is_up(struct tipc_msg *m)
{
if (likely(msg_user(m) != LINK_PROTOCOL))
@ -818,6 +918,8 @@ static inline bool msg_is_reset(struct tipc_msg *hdr)
struct sk_buff *tipc_buf_acquire(u32 size, gfp_t gfp);
bool tipc_msg_validate(struct sk_buff *skb);
bool tipc_msg_reverse(u32 own_addr, struct sk_buff **skb, int err);
void tipc_skb_reject(struct net *net, int err, struct sk_buff *skb,
struct sk_buff_head *xmitq);
void tipc_msg_init(u32 own_addr, struct tipc_msg *m, u32 user, u32 type,
u32 hsize, u32 destnode);
struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz,

View File

@ -43,6 +43,7 @@
#include "bcast.h"
#include "addr.h"
#include "node.h"
#include "group.h"
#include <net/genetlink.h>
#define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */
@ -596,18 +597,47 @@ u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance,
return ref;
}
/**
* tipc_nametbl_mc_translate - find multicast destinations
*
* Creates list of all local ports that overlap the given multicast address;
* also determines if any off-node ports overlap.
*
* Note: Publications with a scope narrower than 'limit' are ignored.
* (i.e. local node-scope publications mustn't receive messages arriving
* from another node, even if the multcast link brought it here)
*
* Returns non-zero if any off-node ports overlap
*/
bool tipc_nametbl_lookup(struct net *net, u32 type, u32 instance, u32 domain,
struct list_head *dsts, int *dstcnt, u32 exclude,
bool all)
{
u32 self = tipc_own_addr(net);
struct publication *publ;
struct name_info *info;
struct name_seq *seq;
struct sub_seq *sseq;
if (!tipc_in_scope(domain, self))
return false;
*dstcnt = 0;
rcu_read_lock();
seq = nametbl_find_seq(net, type);
if (unlikely(!seq))
goto exit;
spin_lock_bh(&seq->lock);
sseq = nameseq_find_subseq(seq, instance);
if (likely(sseq)) {
info = sseq->info;
list_for_each_entry(publ, &info->zone_list, zone_list) {
if (!tipc_in_scope(domain, publ->node))
continue;
if (publ->ref == exclude && publ->node == self)
continue;
tipc_dest_push(dsts, publ->node, publ->ref);
(*dstcnt)++;
if (all)
continue;
list_move_tail(&publ->zone_list, &info->zone_list);
break;
}
}
spin_unlock_bh(&seq->lock);
exit:
rcu_read_unlock();
return !list_empty(dsts);
}
int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper,
u32 limit, struct list_head *dports)
{
@ -634,7 +664,7 @@ int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper,
info = sseq->info;
list_for_each_entry(publ, &info->node_list, node_list) {
if (publ->scope <= limit)
u32_push(dports, publ->ref);
tipc_dest_push(dports, 0, publ->ref);
}
if (info->cluster_list_size != info->node_list_size)
@ -679,6 +709,37 @@ void tipc_nametbl_lookup_dst_nodes(struct net *net, u32 type, u32 lower,
rcu_read_unlock();
}
/* tipc_nametbl_build_group - build list of communication group members
*/
void tipc_nametbl_build_group(struct net *net, struct tipc_group *grp,
u32 type, u32 domain)
{
struct sub_seq *sseq, *stop;
struct name_info *info;
struct publication *p;
struct name_seq *seq;
rcu_read_lock();
seq = nametbl_find_seq(net, type);
if (!seq)
goto exit;
spin_lock_bh(&seq->lock);
sseq = seq->sseqs;
stop = seq->sseqs + seq->first_free;
for (; sseq != stop; sseq++) {
info = sseq->info;
list_for_each_entry(p, &info->zone_list, zone_list) {
if (!tipc_in_scope(domain, p->node))
continue;
tipc_group_add_member(grp, p->node, p->ref);
}
}
spin_unlock_bh(&seq->lock);
exit:
rcu_read_unlock();
}
/*
* tipc_nametbl_publish - add name publication to network name tables
*/
@ -1057,78 +1118,79 @@ int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
bool u32_find(struct list_head *l, u32 value)
struct tipc_dest *tipc_dest_find(struct list_head *l, u32 node, u32 port)
{
struct u32_item *item;
u64 value = (u64)node << 32 | port;
struct tipc_dest *dst;
list_for_each_entry(item, l, list) {
if (item->value == value)
return true;
list_for_each_entry(dst, l, list) {
if (dst->value != value)
continue;
return dst;
}
return false;
return NULL;
}
bool u32_push(struct list_head *l, u32 value)
bool tipc_dest_push(struct list_head *l, u32 node, u32 port)
{
struct u32_item *item;
u64 value = (u64)node << 32 | port;
struct tipc_dest *dst;
list_for_each_entry(item, l, list) {
if (item->value == value)
return false;
}
item = kmalloc(sizeof(*item), GFP_ATOMIC);
if (unlikely(!item))
if (tipc_dest_find(l, node, port))
return false;
item->value = value;
list_add(&item->list, l);
dst = kmalloc(sizeof(*dst), GFP_ATOMIC);
if (unlikely(!dst))
return false;
dst->value = value;
list_add(&dst->list, l);
return true;
}
u32 u32_pop(struct list_head *l)
bool tipc_dest_pop(struct list_head *l, u32 *node, u32 *port)
{
struct u32_item *item;
u32 value = 0;
struct tipc_dest *dst;
if (list_empty(l))
return 0;
item = list_first_entry(l, typeof(*item), list);
value = item->value;
list_del(&item->list);
kfree(item);
return value;
return false;
dst = list_first_entry(l, typeof(*dst), list);
if (port)
*port = dst->port;
if (node)
*node = dst->node;
list_del(&dst->list);
kfree(dst);
return true;
}
bool u32_del(struct list_head *l, u32 value)
bool tipc_dest_del(struct list_head *l, u32 node, u32 port)
{
struct u32_item *item, *tmp;
struct tipc_dest *dst;
list_for_each_entry_safe(item, tmp, l, list) {
if (item->value != value)
continue;
list_del(&item->list);
kfree(item);
return true;
}
return false;
dst = tipc_dest_find(l, node, port);
if (!dst)
return false;
list_del(&dst->list);
kfree(dst);
return true;
}
void u32_list_purge(struct list_head *l)
void tipc_dest_list_purge(struct list_head *l)
{
struct u32_item *item, *tmp;
struct tipc_dest *dst, *tmp;
list_for_each_entry_safe(item, tmp, l, list) {
list_del(&item->list);
kfree(item);
list_for_each_entry_safe(dst, tmp, l, list) {
list_del(&dst->list);
kfree(dst);
}
}
int u32_list_len(struct list_head *l)
int tipc_dest_list_len(struct list_head *l)
{
struct u32_item *item;
struct tipc_dest *dst;
int i = 0;
list_for_each_entry(item, l, list) {
list_for_each_entry(dst, l, list) {
i++;
}
return i;

View File

@ -40,6 +40,7 @@
struct tipc_subscription;
struct tipc_plist;
struct tipc_nlist;
struct tipc_group;
/*
* TIPC name types reserved for internal TIPC use (both current and planned)
@ -101,9 +102,14 @@ int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb);
u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *node);
int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper,
u32 limit, struct list_head *dports);
void tipc_nametbl_build_group(struct net *net, struct tipc_group *grp,
u32 type, u32 domain);
void tipc_nametbl_lookup_dst_nodes(struct net *net, u32 type, u32 lower,
u32 upper, u32 domain,
struct tipc_nlist *nodes);
bool tipc_nametbl_lookup(struct net *net, u32 type, u32 instance, u32 domain,
struct list_head *dsts, int *dstcnt, u32 exclude,
bool all);
struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower,
u32 upper, u32 scope, u32 port_ref,
u32 key);
@ -120,16 +126,22 @@ void tipc_nametbl_unsubscribe(struct tipc_subscription *s);
int tipc_nametbl_init(struct net *net);
void tipc_nametbl_stop(struct net *net);
struct u32_item {
struct tipc_dest {
struct list_head list;
u32 value;
union {
struct {
u32 port;
u32 node;
};
u64 value;
};
};
bool u32_push(struct list_head *l, u32 value);
u32 u32_pop(struct list_head *l);
bool u32_find(struct list_head *l, u32 value);
bool u32_del(struct list_head *l, u32 value);
void u32_list_purge(struct list_head *l);
int u32_list_len(struct list_head *l);
struct tipc_dest *tipc_dest_find(struct list_head *l, u32 node, u32 port);
bool tipc_dest_push(struct list_head *l, u32 node, u32 port);
bool tipc_dest_pop(struct list_head *l, u32 *node, u32 *port);
bool tipc_dest_del(struct list_head *l, u32 node, u32 port);
void tipc_dest_list_purge(struct list_head *l);
int tipc_dest_list_len(struct list_head *l);
#endif

View File

@ -157,7 +157,7 @@ static void tipc_node_timeout(unsigned long data);
static void tipc_node_fsm_evt(struct tipc_node *n, int evt);
static struct tipc_node *tipc_node_find(struct net *net, u32 addr);
static void tipc_node_put(struct tipc_node *node);
static bool tipc_node_is_up(struct tipc_node *n);
static bool node_is_up(struct tipc_node *n);
struct tipc_sock_conn {
u32 port;
@ -657,7 +657,7 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
*slot1 = i;
}
if (!tipc_node_is_up(n)) {
if (!node_is_up(n)) {
if (tipc_link_peer_is_down(l))
tipc_node_fsm_evt(n, PEER_LOST_CONTACT_EVT);
tipc_node_fsm_evt(n, SELF_LOST_CONTACT_EVT);
@ -717,11 +717,27 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
tipc_sk_rcv(n->net, &le->inputq);
}
static bool tipc_node_is_up(struct tipc_node *n)
static bool node_is_up(struct tipc_node *n)
{
return n->active_links[0] != INVALID_BEARER_ID;
}
bool tipc_node_is_up(struct net *net, u32 addr)
{
struct tipc_node *n;
bool retval = false;
if (in_own_node(net, addr))
return true;
n = tipc_node_find(net, addr);
if (!n)
return false;
retval = node_is_up(n);
tipc_node_put(n);
return retval;
}
void tipc_node_check_dest(struct net *net, u32 onode,
struct tipc_bearer *b,
u16 capabilities, u32 signature,
@ -1149,7 +1165,7 @@ static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)
if (nla_put_u32(msg->skb, TIPC_NLA_NODE_ADDR, node->addr))
goto attr_msg_full;
if (tipc_node_is_up(node))
if (node_is_up(node))
if (nla_put_flag(msg->skb, TIPC_NLA_NODE_UP))
goto attr_msg_full;
@ -1238,6 +1254,22 @@ int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dnode,
return 0;
}
/* tipc_node_distr_xmit(): send single buffer msgs to individual destinations
* Note: this is only for SYSTEM_IMPORTANCE messages, which cannot be rejected
*/
int tipc_node_distr_xmit(struct net *net, struct sk_buff_head *xmitq)
{
struct sk_buff *skb;
u32 selector, dnode;
while ((skb = __skb_dequeue(xmitq))) {
selector = msg_origport(buf_msg(skb));
dnode = msg_destnode(buf_msg(skb));
tipc_node_xmit_skb(net, skb, dnode, selector);
}
return 0;
}
void tipc_node_broadcast(struct net *net, struct sk_buff *skb)
{
struct sk_buff *txskb;
@ -1249,7 +1281,7 @@ void tipc_node_broadcast(struct net *net, struct sk_buff *skb)
dst = n->addr;
if (in_own_node(net, dst))
continue;
if (!tipc_node_is_up(n))
if (!node_is_up(n))
continue;
txskb = pskb_copy(skb, GFP_ATOMIC);
if (!txskb)

View File

@ -48,7 +48,8 @@ enum {
TIPC_BCAST_SYNCH = (1 << 1),
TIPC_BCAST_STATE_NACK = (1 << 2),
TIPC_BLOCK_FLOWCTL = (1 << 3),
TIPC_BCAST_RCAST = (1 << 4)
TIPC_BCAST_RCAST = (1 << 4),
TIPC_MCAST_GROUPS = (1 << 5)
};
#define TIPC_NODE_CAPABILITIES (TIPC_BCAST_SYNCH | \
@ -68,6 +69,7 @@ int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 node,
char *linkname, size_t len);
int tipc_node_xmit(struct net *net, struct sk_buff_head *list, u32 dnode,
int selector);
int tipc_node_distr_xmit(struct net *net, struct sk_buff_head *list);
int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dest,
u32 selector);
void tipc_node_subscribe(struct net *net, struct list_head *subscr, u32 addr);
@ -76,6 +78,7 @@ void tipc_node_broadcast(struct net *net, struct sk_buff *skb);
int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port);
void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port);
int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel);
bool tipc_node_is_up(struct net *net, u32 addr);
u16 tipc_node_get_capabilities(struct net *net, u32 addr);
int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb);
int tipc_nl_node_dump_link(struct sk_buff *skb, struct netlink_callback *cb);

View File

@ -36,6 +36,8 @@
#include "server.h"
#include "core.h"
#include "socket.h"
#include "addr.h"
#include "msg.h"
#include <net/sock.h>
#include <linux/module.h>
@ -105,13 +107,11 @@ static void tipc_conn_kref_release(struct kref *kref)
kernel_bind(sock, (struct sockaddr *)saddr, sizeof(*saddr));
sock_release(sock);
con->sock = NULL;
spin_lock_bh(&s->idr_lock);
idr_remove(&s->conn_idr, con->conid);
s->idr_in_use--;
spin_unlock_bh(&s->idr_lock);
}
spin_lock_bh(&s->idr_lock);
idr_remove(&s->conn_idr, con->conid);
s->idr_in_use--;
spin_unlock_bh(&s->idr_lock);
tipc_clean_outqueues(con);
kfree(con);
}
@ -197,7 +197,8 @@ static void tipc_close_conn(struct tipc_conn *con)
struct tipc_server *s = con->server;
if (test_and_clear_bit(CF_CONNECTED, &con->flags)) {
tipc_unregister_callbacks(con);
if (con->sock)
tipc_unregister_callbacks(con);
if (con->conid)
s->tipc_conn_release(con->conid, con->usr_data);
@ -207,8 +208,8 @@ static void tipc_close_conn(struct tipc_conn *con)
* are harmless for us here as we have already deleted this
* connection from server connection list.
*/
kernel_sock_shutdown(con->sock, SHUT_RDWR);
if (con->sock)
kernel_sock_shutdown(con->sock, SHUT_RDWR);
conn_put(con);
}
}
@ -487,38 +488,104 @@ void tipc_conn_terminate(struct tipc_server *s, int conid)
}
}
bool tipc_topsrv_kern_subscr(struct net *net, u32 port, u32 type,
u32 lower, u32 upper, int *conid)
{
struct tipc_subscriber *scbr;
struct tipc_subscr sub;
struct tipc_server *s;
struct tipc_conn *con;
sub.seq.type = type;
sub.seq.lower = lower;
sub.seq.upper = upper;
sub.timeout = TIPC_WAIT_FOREVER;
sub.filter = TIPC_SUB_PORTS;
*(u32 *)&sub.usr_handle = port;
con = tipc_alloc_conn(tipc_topsrv(net));
if (!con)
return false;
*conid = con->conid;
s = con->server;
scbr = s->tipc_conn_new(*conid);
if (!scbr) {
tipc_close_conn(con);
return false;
}
con->usr_data = scbr;
con->sock = NULL;
s->tipc_conn_recvmsg(net, *conid, NULL, scbr, &sub, sizeof(sub));
return true;
}
void tipc_topsrv_kern_unsubscr(struct net *net, int conid)
{
struct tipc_conn *con;
con = tipc_conn_lookup(tipc_topsrv(net), conid);
if (!con)
return;
tipc_close_conn(con);
conn_put(con);
}
static void tipc_send_kern_top_evt(struct net *net, struct tipc_event *evt)
{
u32 port = *(u32 *)&evt->s.usr_handle;
u32 self = tipc_own_addr(net);
struct sk_buff_head evtq;
struct sk_buff *skb;
skb = tipc_msg_create(TOP_SRV, 0, INT_H_SIZE, sizeof(*evt),
self, self, port, port, 0);
if (!skb)
return;
msg_set_dest_droppable(buf_msg(skb), true);
memcpy(msg_data(buf_msg(skb)), evt, sizeof(*evt));
skb_queue_head_init(&evtq);
__skb_queue_tail(&evtq, skb);
tipc_sk_rcv(net, &evtq);
}
static void tipc_send_to_sock(struct tipc_conn *con)
{
int count = 0;
struct tipc_server *s = con->server;
struct outqueue_entry *e;
struct tipc_event *evt;
struct msghdr msg;
int count = 0;
int ret;
spin_lock_bh(&con->outqueue_lock);
while (test_bit(CF_CONNECTED, &con->flags)) {
e = list_entry(con->outqueue.next, struct outqueue_entry,
list);
e = list_entry(con->outqueue.next, struct outqueue_entry, list);
if ((struct list_head *) e == &con->outqueue)
break;
spin_unlock_bh(&con->outqueue_lock);
memset(&msg, 0, sizeof(msg));
msg.msg_flags = MSG_DONTWAIT;
if (s->type == SOCK_DGRAM || s->type == SOCK_RDM) {
msg.msg_name = &e->dest;
msg.msg_namelen = sizeof(struct sockaddr_tipc);
if (con->sock) {
memset(&msg, 0, sizeof(msg));
msg.msg_flags = MSG_DONTWAIT;
if (s->type == SOCK_DGRAM || s->type == SOCK_RDM) {
msg.msg_name = &e->dest;
msg.msg_namelen = sizeof(struct sockaddr_tipc);
}
ret = kernel_sendmsg(con->sock, &msg, &e->iov, 1,
e->iov.iov_len);
if (ret == -EWOULDBLOCK || ret == 0) {
cond_resched();
goto out;
} else if (ret < 0) {
goto send_err;
}
} else {
evt = e->iov.iov_base;
tipc_send_kern_top_evt(s->net, evt);
}
ret = kernel_sendmsg(con->sock, &msg, &e->iov, 1,
e->iov.iov_len);
if (ret == -EWOULDBLOCK || ret == 0) {
cond_resched();
goto out;
} else if (ret < 0) {
goto send_err;
}
/* Don't starve users filling buffers */
if (++count >= MAX_SEND_MSG_COUNT) {
cond_resched();

View File

@ -83,13 +83,16 @@ struct tipc_server {
int tipc_conn_sendmsg(struct tipc_server *s, int conid,
struct sockaddr_tipc *addr, void *data, size_t len);
bool tipc_topsrv_kern_subscr(struct net *net, u32 port, u32 type,
u32 lower, u32 upper, int *conid);
void tipc_topsrv_kern_unsubscr(struct net *net, int conid);
/**
* tipc_conn_terminate - terminate connection with server
*
* Note: Must call it in process context since it might sleep
*/
void tipc_conn_terminate(struct tipc_server *s, int conid);
int tipc_server_start(struct tipc_server *s);
void tipc_server_stop(struct tipc_server *s);

File diff suppressed because it is too large Load Diff