Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0080d4f5 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'tipc-next'



Ying Xue says:

====================
purge tipc_net_lock

Now tipc routing hierarchy comprises the structures 'node', 'link'and
'bearer'. The whole hierarchy is protected by a big read/write lock,
tipc_net_lock, to ensure that nothing is added or removed while code
is accessing any of these structures. Obviously the locking policy
makes node, link and bearer components closely bound together so that
their relationship becomes unnecessarily complex. In the worst case,
such locking policy not only has a negative influence on performance,
but also it's prone to lead to deadlock occasionally.

In order o decouple the complex relationship between bearer and node
as well as link, the locking policy is adjusted as follows:

- Bearer level
  RTNL lock is used on update side, and RCU is used on read side.
  Meanwhile, all bearer instances including broadcast bearer are
  saved into bearer_list array.

- Node and link level
  All node instances are saved into two tipc_node_list and node_htable
  lists. The two lists are protected by node_list_lock on write side,
  and they are guarded with RCU lock on read side. All members in node
  structure including link instances are protected by node spin lock.

- The relationship between bearer and node
  When link accesses bearer, it first needs to find the bearer with
  its bearer identity from the bearer_list array. When bearer accesses
  node, it can iterate the node_htable hash list with the node address
  to find the corresponding node.

In the new locking policy, every component has its private locking
solution and the relationship between bearer and node is very simple,
that is, they can find each other with node address or bearer identity
from node_htable hash list or bearer_list array.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5a9d19ab a8b9b96e
Loading
Loading
Loading
Loading
+22 −15
Original line number Diff line number Diff line
@@ -112,6 +112,8 @@ const char tipc_bclink_name[] = "broadcast-link";
static void tipc_nmap_diff(struct tipc_node_map *nm_a,
			   struct tipc_node_map *nm_b,
			   struct tipc_node_map *nm_diff);
static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node);
static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node);

static u32 bcbuf_acks(struct sk_buff *buf)
{
@@ -273,7 +275,7 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
/**
 * tipc_bclink_update_link_state - update broadcast link state
 *
 * tipc_net_lock and node lock set
 * RCU and node lock set
 */
void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
{
@@ -321,7 +323,7 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
				 : n_ptr->bclink.last_sent);

		spin_lock_bh(&bc_lock);
		tipc_bearer_send(&bcbearer->bearer, buf, NULL);
		tipc_bearer_send(MAX_BEARERS, buf, NULL);
		bcl->stats.sent_nacks++;
		spin_unlock_bh(&bc_lock);
		kfree_skb(buf);
@@ -335,8 +337,6 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
 *
 * Delay any upcoming NACK by this node if another node has already
 * requested the first message this node is going to ask for.
 *
 * Only tipc_net_lock set.
 */
static void bclink_peek_nack(struct tipc_msg *msg)
{
@@ -408,7 +408,7 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
/**
 * tipc_bclink_rcv - receive a broadcast packet, and deliver upwards
 *
 * tipc_net_lock is read_locked, no other locks set
 * RCU is locked, no other locks set
 */
void tipc_bclink_rcv(struct sk_buff *buf)
{
@@ -627,13 +627,13 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,

		if (bp_index == 0) {
			/* Use original buffer for first bearer */
			tipc_bearer_send(b, buf, &b->bcast_addr);
			tipc_bearer_send(b->identity, buf, &b->bcast_addr);
		} else {
			/* Avoid concurrent buffer access */
			tbuf = pskb_copy(buf, GFP_ATOMIC);
			if (!tbuf)
				break;
			tipc_bearer_send(b, tbuf, &b->bcast_addr);
			tipc_bearer_send(b->identity, tbuf, &b->bcast_addr);
			kfree_skb(tbuf); /* Bearer keeps a clone */
		}

@@ -655,20 +655,27 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,
/**
 * tipc_bcbearer_sort - create sets of bearer pairs used by broadcast bearer
 */
void tipc_bcbearer_sort(void)
void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action)
{
	struct tipc_bcbearer_pair *bp_temp = bcbearer->bpairs_temp;
	struct tipc_bcbearer_pair *bp_curr;
	struct tipc_bearer *b;
	int b_index;
	int pri;

	spin_lock_bh(&bc_lock);

	if (action)
		tipc_nmap_add(nm_ptr, node);
	else
		tipc_nmap_remove(nm_ptr, node);

	/* Group bearers by priority (can assume max of two per priority) */
	memset(bp_temp, 0, sizeof(bcbearer->bpairs_temp));

	rcu_read_lock();
	for (b_index = 0; b_index < MAX_BEARERS; b_index++) {
		struct tipc_bearer *b = bearer_list[b_index];
		b = rcu_dereference_rtnl(bearer_list[b_index]);
		if (!b || !b->nodes.count)
			continue;

@@ -677,6 +684,7 @@ void tipc_bcbearer_sort(void)
		else
			bp_temp[b->priority].secondary = b;
	}
	rcu_read_unlock();

	/* Create array of bearer pairs for broadcasting */
	bp_curr = bcbearer->bpairs;
@@ -783,8 +791,8 @@ void tipc_bclink_init(void)
	bcl->owner = &bclink->node;
	bcl->max_pkt = MAX_PKT_DEFAULT_MCAST;
	tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT);
	bcl->b_ptr = &bcbearer->bearer;
	bearer_list[BCBEARER] = &bcbearer->bearer;
	bcl->bearer_id = MAX_BEARERS;
	rcu_assign_pointer(bearer_list[MAX_BEARERS], &bcbearer->bearer);
	bcl->state = WORKING_WORKING;
	strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME);
}
@@ -795,16 +803,15 @@ void tipc_bclink_stop(void)
	tipc_link_purge_queues(bcl);
	spin_unlock_bh(&bc_lock);

	bearer_list[BCBEARER] = NULL;
	RCU_INIT_POINTER(bearer_list[BCBEARER], NULL);
	memset(bclink, 0, sizeof(*bclink));
	memset(bcbearer, 0, sizeof(*bcbearer));
}


/**
 * tipc_nmap_add - add a node to a node map
 */
void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
{
	int n = tipc_node(node);
	int w = n / WSIZE;
@@ -819,7 +826,7 @@ void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
/**
 * tipc_nmap_remove - remove a node from a node map
 */
void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node)
static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node)
{
	int n = tipc_node(node);
	int w = n / WSIZE;
+1 −4
Original line number Diff line number Diff line
@@ -69,9 +69,6 @@ struct tipc_node;

extern const char tipc_bclink_name[];

void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node);
void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node);

/**
 * tipc_nmap_equal - test for equality of node maps
 */
@@ -98,6 +95,6 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent);
int  tipc_bclink_stats(char *stats_buf, const u32 buf_size);
int  tipc_bclink_reset_stats(void);
int  tipc_bclink_set_queue_limits(u32 limit);
void tipc_bcbearer_sort(void);
void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action);

#endif
+63 −54
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ static struct tipc_media * const media_info_array[] = {
	NULL
};

struct tipc_bearer *bearer_list[MAX_BEARERS + 1];
struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1];

static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down);

@@ -178,7 +178,7 @@ struct tipc_bearer *tipc_bearer_find(const char *name)
	u32 i;

	for (i = 0; i < MAX_BEARERS; i++) {
		b_ptr = bearer_list[i];
		b_ptr = rtnl_dereference(bearer_list[i]);
		if (b_ptr && (!strcmp(b_ptr->name, name)))
			return b_ptr;
	}
@@ -198,10 +198,9 @@ struct sk_buff *tipc_bearer_get_names(void)
	if (!buf)
		return NULL;

	read_lock_bh(&tipc_net_lock);
	for (i = 0; media_info_array[i] != NULL; i++) {
		for (j = 0; j < MAX_BEARERS; j++) {
			b = bearer_list[j];
			b = rtnl_dereference(bearer_list[j]);
			if (!b)
				continue;
			if (b->media == media_info_array[i]) {
@@ -211,23 +210,34 @@ struct sk_buff *tipc_bearer_get_names(void)
			}
		}
	}
	read_unlock_bh(&tipc_net_lock);
	return buf;
}

void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest)
void tipc_bearer_add_dest(u32 bearer_id, u32 dest)
{
	tipc_nmap_add(&b_ptr->nodes, dest);
	tipc_bcbearer_sort();
	struct tipc_bearer *b_ptr;

	rcu_read_lock();
	b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]);
	if (b_ptr) {
		tipc_bcbearer_sort(&b_ptr->nodes, dest, true);
		tipc_disc_add_dest(b_ptr->link_req);
	}
	rcu_read_unlock();
}

void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest)
void tipc_bearer_remove_dest(u32 bearer_id, u32 dest)
{
	tipc_nmap_remove(&b_ptr->nodes, dest);
	tipc_bcbearer_sort();
	struct tipc_bearer *b_ptr;

	rcu_read_lock();
	b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]);
	if (b_ptr) {
		tipc_bcbearer_sort(&b_ptr->nodes, dest, false);
		tipc_disc_remove_dest(b_ptr->link_req);
	}
	rcu_read_unlock();
}

/**
 * tipc_enable_bearer - enable bearer with the given name
@@ -271,13 +281,11 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
		return -EINVAL;
	}

	write_lock_bh(&tipc_net_lock);

	m_ptr = tipc_media_find(b_names.media_name);
	if (!m_ptr) {
		pr_warn("Bearer <%s> rejected, media <%s> not registered\n",
			name, b_names.media_name);
		goto exit;
		return -EINVAL;
	}

	if (priority == TIPC_MEDIA_LINK_PRI)
@@ -287,7 +295,7 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
	bearer_id = MAX_BEARERS;
	with_this_prio = 1;
	for (i = MAX_BEARERS; i-- != 0; ) {
		b_ptr = bearer_list[i];
		b_ptr = rtnl_dereference(bearer_list[i]);
		if (!b_ptr) {
			bearer_id = i;
			continue;
@@ -295,14 +303,14 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
		if (!strcmp(name, b_ptr->name)) {
			pr_warn("Bearer <%s> rejected, already enabled\n",
				name);
			goto exit;
			return -EINVAL;
		}
		if ((b_ptr->priority == priority) &&
		    (++with_this_prio > 2)) {
			if (priority-- == 0) {
				pr_warn("Bearer <%s> rejected, duplicate priority\n",
					name);
				goto exit;
				return -EINVAL;
			}
			pr_warn("Bearer <%s> priority adjustment required %u->%u\n",
				name, priority + 1, priority);
@@ -312,21 +320,20 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
	if (bearer_id >= MAX_BEARERS) {
		pr_warn("Bearer <%s> rejected, bearer limit reached (%u)\n",
			name, MAX_BEARERS);
		goto exit;
		return -EINVAL;
	}

	b_ptr = kzalloc(sizeof(*b_ptr), GFP_ATOMIC);
	if (!b_ptr) {
		res = -ENOMEM;
		goto exit;
	}
	if (!b_ptr)
		return -ENOMEM;

	strcpy(b_ptr->name, name);
	b_ptr->media = m_ptr;
	res = m_ptr->enable_media(b_ptr);
	if (res) {
		pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
			name, -res);
		goto exit;
		return -EINVAL;
	}

	b_ptr->identity = bearer_id;
@@ -341,16 +348,14 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
		bearer_disable(b_ptr, false);
		pr_warn("Bearer <%s> rejected, discovery object creation failed\n",
			name);
		goto exit;
		return -EINVAL;
	}

	bearer_list[bearer_id] = b_ptr;
	rcu_assign_pointer(bearer_list[bearer_id], b_ptr);

	pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
		name,
		tipc_addr_string_fill(addr_string, disc_domain), priority);
exit:
	write_unlock_bh(&tipc_net_lock);
	return res;
}

@@ -359,19 +364,16 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
 */
static int tipc_reset_bearer(struct tipc_bearer *b_ptr)
{
	read_lock_bh(&tipc_net_lock);
	pr_info("Resetting bearer <%s>\n", b_ptr->name);
	tipc_disc_delete(b_ptr->link_req);
	tipc_link_reset_list(b_ptr->identity);
	tipc_disc_create(b_ptr, &b_ptr->bcast_addr);
	read_unlock_bh(&tipc_net_lock);
	tipc_disc_reset(b_ptr);
	return 0;
}

/**
 * bearer_disable
 *
 * Note: This routine assumes caller holds tipc_net_lock.
 * Note: This routine assumes caller holds RTNL lock.
 */
static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down)
{
@@ -385,12 +387,12 @@ static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down)
		tipc_disc_delete(b_ptr->link_req);

	for (i = 0; i < MAX_BEARERS; i++) {
		if (b_ptr == bearer_list[i]) {
			bearer_list[i] = NULL;
		if (b_ptr == rtnl_dereference(bearer_list[i])) {
			RCU_INIT_POINTER(bearer_list[i], NULL);
			break;
		}
	}
	kfree(b_ptr);
	kfree_rcu(b_ptr, rcu);
}

int tipc_disable_bearer(const char *name)
@@ -398,7 +400,6 @@ int tipc_disable_bearer(const char *name)
	struct tipc_bearer *b_ptr;
	int res;

	write_lock_bh(&tipc_net_lock);
	b_ptr = tipc_bearer_find(name);
	if (b_ptr == NULL) {
		pr_warn("Attempt to disable unknown bearer <%s>\n", name);
@@ -407,7 +408,6 @@ int tipc_disable_bearer(const char *name)
		bearer_disable(b_ptr, false);
		res = 0;
	}
	write_unlock_bh(&tipc_net_lock);
	return res;
}

@@ -444,7 +444,7 @@ int tipc_enable_l2_media(struct tipc_bearer *b)
		return -ENODEV;

	/* Associate TIPC bearer with Ethernet bearer */
	b->media_ptr = dev;
	rcu_assign_pointer(b->media_ptr, dev);
	memset(b->bcast_addr.value, 0, sizeof(b->bcast_addr.value));
	memcpy(b->bcast_addr.value, dev->broadcast, b->media->hwaddr_len);
	b->bcast_addr.media_id = b->media->type_id;
@@ -463,8 +463,12 @@ int tipc_enable_l2_media(struct tipc_bearer *b)
 */
void tipc_disable_l2_media(struct tipc_bearer *b)
{
	struct net_device *dev = (struct net_device *)b->media_ptr;
	struct net_device *dev;

	dev = (struct net_device *)rtnl_dereference(b->media_ptr);
	RCU_INIT_POINTER(b->media_ptr, NULL);
	RCU_INIT_POINTER(dev->tipc_ptr, NULL);
	synchronize_net();
	dev_put(dev);
}

@@ -478,8 +482,12 @@ int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
		     struct tipc_media_addr *dest)
{
	struct sk_buff *clone;
	struct net_device *dev;
	int delta;
	struct net_device *dev = (struct net_device *)b->media_ptr;

	dev = (struct net_device *)rcu_dereference_rtnl(b->media_ptr);
	if (!dev)
		return 0;

	clone = skb_clone(buf, GFP_ATOMIC);
	if (!clone)
@@ -507,10 +515,16 @@ int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
 * The media send routine must not alter the buffer being passed in
 * as it may be needed for later retransmission!
 */
void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf,
		      struct tipc_media_addr *dest)
{
	b->media->send_msg(buf, b, dest);
	struct tipc_bearer *b_ptr;

	rcu_read_lock();
	b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]);
	if (likely(b_ptr))
		b_ptr->media->send_msg(buf, b_ptr, dest);
	rcu_read_unlock();
}

/**
@@ -535,7 +549,7 @@ static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,
	}

	rcu_read_lock();
	b_ptr = rcu_dereference(dev->tipc_ptr);
	b_ptr = rcu_dereference_rtnl(dev->tipc_ptr);
	if (likely(b_ptr)) {
		if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
			buf->next = NULL;
@@ -568,12 +582,9 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
	if (!net_eq(dev_net(dev), &init_net))
		return NOTIFY_DONE;

	rcu_read_lock();
	b_ptr = rcu_dereference(dev->tipc_ptr);
	if (!b_ptr) {
		rcu_read_unlock();
	b_ptr = rtnl_dereference(dev->tipc_ptr);
	if (!b_ptr)
		return NOTIFY_DONE;
	}

	b_ptr->mtu = dev->mtu;

@@ -592,11 +603,9 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
		break;
	case NETDEV_UNREGISTER:
	case NETDEV_CHANGENAME:
		tipc_disable_bearer(b_ptr->name);
		bearer_disable(b_ptr, false);
		break;
	}
	rcu_read_unlock();

	return NOTIFY_OK;
}

@@ -633,7 +642,7 @@ void tipc_bearer_stop(void)
	u32 i;

	for (i = 0; i < MAX_BEARERS; i++) {
		b_ptr = bearer_list[i];
		b_ptr = rtnl_dereference(bearer_list[i]);
		if (b_ptr) {
			bearer_disable(b_ptr, true);
			bearer_list[i] = NULL;
+7 −5
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ struct tipc_media {
 * @name: bearer name (format = media:interface)
 * @media: ptr to media structure associated with bearer
 * @bcast_addr: media address used in broadcasting
 * @rcu: rcu struct for tipc_bearer
 * @priority: default link priority for bearer
 * @window: default window size for bearer
 * @tolerance: default link tolerance for bearer
@@ -127,12 +128,13 @@ struct tipc_media {
 * care of initializing all other fields.
 */
struct tipc_bearer {
	void *media_ptr;			/* initalized by media */
	void __rcu *media_ptr;			/* initalized by media */
	u32 mtu;				/* initalized by media */
	struct tipc_media_addr addr;		/* initalized by media */
	char name[TIPC_MAX_BEARER_NAME];
	struct tipc_media *media;
	struct tipc_media_addr bcast_addr;
	struct rcu_head rcu;
	u32 priority;
	u32 window;
	u32 tolerance;
@@ -150,7 +152,7 @@ struct tipc_bearer_names {

struct tipc_link;

extern struct tipc_bearer *bearer_list[];
extern struct tipc_bearer __rcu *bearer_list[];

/*
 * TIPC routines available to supported media types
@@ -181,14 +183,14 @@ int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
		     struct tipc_media_addr *dest);

struct sk_buff *tipc_bearer_get_names(void);
void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest);
void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest);
void tipc_bearer_add_dest(u32 bearer_id, u32 dest);
void tipc_bearer_remove_dest(u32 bearer_id, u32 dest);
struct tipc_bearer *tipc_bearer_find(const char *name);
struct tipc_media *tipc_media_find(const char *name);
int tipc_bearer_setup(void);
void tipc_bearer_cleanup(void);
void tipc_bearer_stop(void);
void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf,
		      struct tipc_media_addr *dest);

#endif	/* _TIPC_BEARER_H */
+2 −4
Original line number Diff line number Diff line
@@ -42,8 +42,6 @@

#define REPLY_TRUNCATED "<truncated>\n"

static DEFINE_MUTEX(config_mutex);

static const void *req_tlv_area;	/* request message TLV area */
static int req_tlv_space;		/* request message TLV area size */
static int rep_headroom;		/* reply message headroom to use */
@@ -223,7 +221,7 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
{
	struct sk_buff *rep_tlv_buf;

	mutex_lock(&config_mutex);
	rtnl_lock();

	/* Save request and reply details in a well-known location */
	req_tlv_area = request_area;
@@ -337,6 +335,6 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area

	/* Return reply buffer */
exit:
	mutex_unlock(&config_mutex);
	rtnl_unlock();
	return rep_tlv_buf;
}
Loading