Skip to contentSkip to conversation

bonding: simplify and use RCU protection for 3ad xmit path

PATCH Superseded Ding Tianhong 5 participants 11 messages Sep 6, 2013 – Sep 12, 2013

Subsystems (4)

v4 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

PATCH v4 1/6 bonding: simplify and use RCU protection for 3ad xmit path

The commit 278b20837511776dc9d5f6ee1c7fabd5479838bb
(bonding: initial RCU conversion) has convert the roundrobin, active-backup, broadcast and xor xmit path to rcu protection, the performance will be better for these mode, so this time, convert xmit path for 3ad mode.

---
 drivers/net/bonding/bond_3ad.c | 32 ++++++++++++++------------------
 drivers/net/bonding/bonding.h  | 33 ++++++++++++++++++++++++++++++++-
 2 files changed, 46 insertions(+), 19 deletions(-)
drivers/net/bonding/bond_3ad.c +14 -18
index 0d8f427..13f1deb 100644
@@ -143,7 +143,7 @@ static inline struct bonding *__get_bond_by_port(struct port *port)
  */
 static inline struct port *__get_first_port(struct bonding *bond)
 {
-	struct slave *first_slave = bond_first_slave(bond);
+	struct slave *first_slave = bond_first_slave_rcu(bond);
 
 	return first_slave ? &(SLAVE_AD_INFO(first_slave).port) : NULL;
 }
@@ -163,7 +163,7 @@ static inline struct port *__get_next_port(struct port *port)
 	// If there's no bond for this port, or this is the last slave
 	if (bond == NULL)
 		return NULL;
-	slave_next = bond_next_slave(bond, slave);
+	slave_next = bond_next_slave_rcu(bond, slave);
 	if (!slave_next || bond_is_first_slave(bond, slave_next))
 		return NULL;
 
@@ -2417,16 +2417,14 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
 
 int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
 {
-	struct slave *slave, *start_at;
 	struct bonding *bond = netdev_priv(dev);
+	struct slave *slave;
 	int slave_agg_no;
 	int slaves_in_agg;
 	int agg_id;
-	int i;
 	struct ad_info ad_info;
 	int res = 1;
 
-	read_lock(&bond->lock);
 	if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
 		pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n",
 			 dev->name);
@@ -2444,13 +2442,17 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
 
 	slave_agg_no = bond->xmit_hash_policy(skb, slaves_in_agg);
 
-	bond_for_each_slave(bond, slave) {
+	bond_for_each_slave_rcu(bond, slave) {
 		struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator;
 
 		if (agg && (agg->aggregator_identifier == agg_id)) {
-			slave_agg_no--;
-			if (slave_agg_no < 0)
-				break;
+			if (--slave_agg_no < 0) {
+				if (SLAVE_IS_OK(slave)) {
+					res = bond_dev_queue_xmit(bond,
+						skb, slave->dev);
+					goto out;
+				}
+			}
 		}
 	}
 
@@ -2460,23 +2462,17 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
 		goto out;
 	}
 
-	start_at = slave;
-
-	bond_for_each_slave_from(bond, slave, i, start_at) {
-		int slave_agg_id = 0;
+	bond_for_each_slave_rcu(bond, slave) {
 		struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator;
 
-		if (agg)
-			slave_agg_id = agg->aggregator_identifier;
-
-		if (SLAVE_IS_OK(slave) && agg && (slave_agg_id == agg_id)) {
+		if (SLAVE_IS_OK(slave) && agg &&
+			agg->aggregator_identifier == agg_id) {
 			res = bond_dev_queue_xmit(bond, skb, slave->dev);
 			break;
 		}
 	}
 
 out:
-	read_unlock(&bond->lock);
 	if (res) {
 		/* no suitable interface, frame not sent */
 		kfree_skb(skb);
drivers/net/bonding/bonding.h +32 -1
index f7ab161..419161d 100644
@@ -74,13 +74,35 @@
 /* slave list primitives */
 #define bond_to_slave(ptr) list_entry(ptr, struct slave, list)
 
+/* slave list primitives, Caller must hold rcu_read_lock */
+#define bond_to_slave_rcu(ptr) list_entry_rcu(ptr, struct slave, list)
+
+/* bond_is_empty return NULL if slave list is empty*/
+#define bond_is_empty(bond) \
+	(list_empty(&(bond)->slave_list))
+
+/* bond_is_empty_rcu return NULL if slave list is empty*/
+#define bond_is_empty_rcu(bond) \
+	(!list_first_or_null_rcu(&(bond)->slave_list, struct slave, list))
+
 /* IMPORTANT: bond_first/last_slave can return NULL in case of an empty list */
 #define bond_first_slave(bond) \
 	list_first_entry_or_null(&(bond)->slave_list, struct slave, list)
 #define bond_last_slave(bond) \
-	(list_empty(&(bond)->slave_list) ? NULL : \
+	(bond_is_empty(bond) ? NULL : \
 					   bond_to_slave((bond)->slave_list.prev))
 
+/**
+ * IMPORTANT: bond_first/last_slave_rcu can return NULL in case of an empty list
+ * Caller must hold rcu_read_lock
+ */
+#define bond_first_slave_rcu(bond) \
+	(bond_is_empty_rcu(bond) ? NULL : \
+					bond_to_slave_rcu((bond)->slave_list.next))
+#define bond_last_slave_rcu(bond) \
+	(bond_is_empty_rcu(bond) ? NULL : \
+					bond_to_slave_rcu((bond)->slave_list.prev))
+
 #define bond_is_first_slave(bond, pos) ((pos)->list.prev == &(bond)->slave_list)
 #define bond_is_last_slave(bond, pos) ((pos)->list.next == &(bond)->slave_list)
 
@@ -93,6 +115,15 @@
 	(bond_is_first_slave(bond, pos) ? bond_last_slave(bond) : \
 					  bond_to_slave((pos)->list.prev))
 
+/* Since bond_first/last_slave_rcu can return NULL, these can return NULL too */
+#define bond_next_slave_rcu(bond, pos) \
+	(bond_is_last_slave(bond, pos) ? bond_first_slave_rcu(bond) : \
+					 bond_to_slave_rcu((pos)->list.next))
+
+#define bond_prev_slave_rcu(bond, pos) \
+	(bond_is_first_slave(bond, pos) ? bond_last_slave_rcu(bond) : \
+					  bond_to_slave_rcu((pos)->list.prev))
+
 /**
  * bond_for_each_slave_from - iterate the slaves list from a starting point
  * @bond:	the bond holding this list.
Signature · 1 line
1.8.2.1
#1 lore raw
Quoted 133 lines · Ding Tianhong

The commit 278b20837511776dc9d5f6ee1c7fabd5479838bb
(bonding: initial RCU conversion) has convert the roundrobin, active-backup, broadcast and xor xmit path to rcu protection, the performance will be better for these mode, so this time, convert xmit path for 3ad mode.

---
 drivers/net/bonding/bond_3ad.c | 32 ++++++++++++++------------------
 drivers/net/bonding/bonding.h  | 33 ++++++++++++++++++++++++++++++++-
 2 files changed, 46 insertions(+), 19 deletions(-)
drivers/net/bonding/bond_3ad.c +14 -18
index 0d8f427..13f1deb 100644
@@ -143,7 +143,7 @@ static inline struct bonding *__get_bond_by_port(struct port *port)
  */
 static inline struct port *__get_first_port(struct bonding *bond)
 {
-	struct slave *first_slave = bond_first_slave(bond);
+	struct slave *first_slave = bond_first_slave_rcu(bond);
 
 	return first_slave ? &(SLAVE_AD_INFO(first_slave).port) : NULL;
 }
@@ -163,7 +163,7 @@ static inline struct port *__get_next_port(struct port *port)
 	// If there's no bond for this port, or this is the last slave
 	if (bond == NULL)
 		return NULL;
-	slave_next = bond_next_slave(bond, slave);
+	slave_next = bond_next_slave_rcu(bond, slave);
 	if (!slave_next || bond_is_first_slave(bond, slave_next))
 		return NULL;
 
@@ -2417,16 +2417,14 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
 
 int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
 {
-	struct slave *slave, *start_at;
 	struct bonding *bond = netdev_priv(dev);
+	struct slave *slave;
 	int slave_agg_no;
 	int slaves_in_agg;
 	int agg_id;
-	int i;
 	struct ad_info ad_info;
 	int res = 1;
 
-	read_lock(&bond->lock);
 	if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
 		pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n",
 			 dev->name);
@@ -2444,13 +2442,17 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
 
 	slave_agg_no = bond->xmit_hash_policy(skb, slaves_in_agg);
 
-	bond_for_each_slave(bond, slave) {
+	bond_for_each_slave_rcu(bond, slave) {
 		struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator;
 
 		if (agg && (agg->aggregator_identifier == agg_id)) {
-			slave_agg_no--;
-			if (slave_agg_no < 0)
-				break;
+			if (--slave_agg_no < 0) {
+				if (SLAVE_IS_OK(slave)) {
+					res = bond_dev_queue_xmit(bond,
+						skb, slave->dev);
+					goto out;
+				}
+			}
 		}
 	}
 
@@ -2460,23 +2462,17 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
 		goto out;
 	}
 
-	start_at = slave;
-
-	bond_for_each_slave_from(bond, slave, i, start_at) {
-		int slave_agg_id = 0;
+	bond_for_each_slave_rcu(bond, slave) {
 		struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator;
 
-		if (agg)
-			slave_agg_id = agg->aggregator_identifier;
-
-		if (SLAVE_IS_OK(slave) && agg && (slave_agg_id == agg_id)) {
+		if (SLAVE_IS_OK(slave) && agg &&
+			agg->aggregator_identifier == agg_id) {
 			res = bond_dev_queue_xmit(bond, skb, slave->dev);
 			break;
 		}
 	}
 
 out:
-	read_unlock(&bond->lock);
 	if (res) {
 		/* no suitable interface, frame not sent */
 		kfree_skb(skb);
drivers/net/bonding/bonding.h +13 -1
index f7ab161..419161d 100644
@@ -74,13 +74,35 @@
 /* slave list primitives */
 #define bond_to_slave(ptr) list_entry(ptr, struct slave, list)
 
+/* slave list primitives, Caller must hold rcu_read_lock */
+#define bond_to_slave_rcu(ptr) list_entry_rcu(ptr, struct slave, list)
+
+/* bond_is_empty return NULL if slave list is empty*/
+#define bond_is_empty(bond) \
+	(list_empty(&(bond)->slave_list))
+
+/* bond_is_empty_rcu return NULL if slave list is empty*/
+#define bond_is_empty_rcu(bond) \
+	(!list_first_or_null_rcu(&(bond)->slave_list, struct slave, list))
+
 /* IMPORTANT: bond_first/last_slave can return NULL in case of an empty list */
 #define bond_first_slave(bond) \
 	list_first_entry_or_null(&(bond)->slave_list, struct slave, list)
 #define bond_last_slave(bond) \
-	(list_empty(&(bond)->slave_list) ? NULL : \
+	(bond_is_empty(bond) ? NULL : \
 					   bond_to_slave((bond)->slave_list.prev))
 
+/**

drivers/net/bonding/bonding.h line 105

Quoted 10 lines
drivers/net/bonding/bonding.h +10 -0
+ * IMPORTANT: bond_first/last_slave_rcu can return NULL in case of an empty list
+ * Caller must hold rcu_read_lock
+ */
+#define bond_first_slave_rcu(bond) \
+	(bond_is_empty_rcu(bond) ? NULL : \
+					bond_to_slave_rcu((bond)->slave_list.next))
+#define bond_last_slave_rcu(bond) \
+	(bond_is_empty_rcu(bond) ? NULL : \
+					bond_to_slave_rcu((bond)->slave_list.prev))
+

This still has the bug that you and Veaceslav were discussing earlier. To be honest, I'm getting tired of these fast re-posts without any actual changes, it really is really starting to get on my nerves. Please before posting the next version take some time (more time) re-think it, go over it more times, don't go with the first thing that comes to mind without thinking it through well.

Now to be specific here for the Nth time:
You _can't_ do this sequence:
if (list_first_or_null_rcu()) -> rcu_dereference(first/last element) because
between the check which actually dereferences it and the second dereference the first/last element might be long gone. You should use the result of list_first_or_null_rcu.

Cheers,
Nik

drivers/net/bonding/bonding.h line 122

Quoted 12 lines
drivers/net/bonding/bonding.h +5 -0
 #define bond_is_first_slave(bond, pos) ((pos)->list.prev == &(bond)->slave_list)
 #define bond_is_last_slave(bond, pos) ((pos)->list.next == &(bond)->slave_list)
 
@@ -93,6 +115,15 @@
 	(bond_is_first_slave(bond, pos) ? bond_last_slave(bond) : \
 					  bond_to_slave((pos)->list.prev))
 
+/* Since bond_first/last_slave_rcu can return NULL, these can return NULL too */
+#define bond_next_slave_rcu(bond, pos) \
+	(bond_is_last_slave(bond, pos) ? bond_first_slave_rcu(bond) : \
+					 bond_to_slave_rcu((pos)->list.next))
+

P.S. Either I'm getting paranoid or I think there's the same bug here, namely:
say list.next != slave_list, but at the time of dereferencing list.next bond_to_slave_rcu() can get slave_list if it was changed.

Quoted 8 lines
drivers/net/bonding/bonding.h +4 -0
+#define bond_prev_slave_rcu(bond, pos) \
+	(bond_is_first_slave(bond, pos) ? bond_last_slave_rcu(bond) : \
+					  bond_to_slave_rcu((pos)->list.prev))
+
 /**
  * bond_for_each_slave_from - iterate the slaves list from a starting point
  * @bond:	the bond holding this list.
#2 lore raw
In reply to Ding Tianhong

On Fri, Sep 06, 2013 at 03:28:07PM +0800, Ding Tianhong wrote:
...snip...

Review

Quoted 11 lines
(quoted diff) +11 -0
+/**
+ * IMPORTANT: bond_first/last_slave_rcu can return NULL in case of an empty list
+ * Caller must hold rcu_read_lock
+ */
+#define bond_first_slave_rcu(bond) \
+	(bond_is_empty_rcu(bond) ? NULL : \
+					bond_to_slave_rcu((bond)->slave_list.next))
+#define bond_last_slave_rcu(bond) \
+	(bond_is_empty_rcu(bond) ? NULL : \
+					bond_to_slave_rcu((bond)->slave_list.prev))
+

Hi Ding,

I didn't have time to actually go into detail about this last week, and, as Eric correctly said, RCU is hard even for experienced kernel developers, so it's really not the easiest part to understand. I, for one, can't say that I understand it completely, though I know the overall, basic usage.

That being said, I really advise you to go through, first of all, Documentation/RCU. There are also tons of online materials already written on this topic - and every one takes its own approach in explaining it.

I'll now try to explain what is wrong with the approach of verifying for list emptyness while holding only RCU read lock.

First of all, you've replied to one of my emails that, quoting - "the slave_list will not changed in the rcu_read_lock". This is completely wrong
- rcu_read_lock() is not a lock, in its classical meaning. It allows the list, and everything else, to be *modified* (in the most basic case) while
holding it. I.e. if we, concurrently, run list_for_each_entry_rcu() and, on another CPU, list_del_rcu() - then we might end up holding a reference to the entry that *was* deleted from the list already. In other words - the list can be freely modified while we're holding rcu_read_lock().

What rcu_read_lock() *does* guarantee is that the entry we've dereferenced won't 'go away' - as in - get freed, get deinitialized (in most cases, and in this one - with slave_list) or whatever else that can stop us from using it. That's, again, a really broad assumption and there are *lots* of small things to be taken care of, and, in some cases it's not even true - however, the basic idea is this - once you rcu_dereference() something - you can use it while holding rcu_read_lock().

So, again - while holding the rcu_read_lock(), working with a list - the list itself can change easily, can become empty, or can become non-empty, or whatever else - even while going through it via list_for_each_entry_rcu(). However, if we dereference an entry there - we can use it.

Now, about the list emptiness under rcu_read_lock(). Lets suppose the following code (imagine that we have list_empty_rcu(), and we're holding the rcu_read_lock()):

   1 if (!list_empty_rcu(&my_list)) {
   2         do_something(get_first_entry(&my_list));
   3 }

on the 1st line, we verify if the list is not empty - and, sometimes, it will be true. For example, we'll have only one element there.

Now, after we execute the first line, on another CPU we *remove* the only remaining element from the list, and thus my_list->next will point to &my_list - classical empty list.

So, back to the first CPU, when we run our code, we execute get_first_entry(&my_list) - which, usually, translates to container_of(my_list->next, ...) - getting the container of the next element of the list - which is our *head* - and not a valid entry. And we will, eventually, bug here.

This is exactly what you're doing:

+#define bond_last_slave_rcu(bond) \
+ (bond_is_empty_rcu(bond) ? NULL : \
+ bond_to_slave_rcu((bond)->slave_list.prev))

You first verify if the list is empty - which can be non-empty *in the time of the verification* - and then get the bond_to_slave_rcu() of the .prev - when the list can already become empty and the prev will point to the list head, and not to a valid slave.

That's exactly why we don't have the list_empty_rcu() - because it's meaningless - cause exactly after it was run the list can become empty or non-empty. It guarantees us nothing. The only, maybe, slightly useful usage of it could be some kind of optimization:

1 rcu_read_lock();
2
3 if (unlikely(list_empty(&bond->slave_list))) {
4 rcu_read_unlock();
5 return -ENODEV;
6 }
7
8 heavy_preparation1(bond);
9 heavy_preparation2(bond);
10 ...
11
12 bond_for_each_slave(bond, slave)
13 do_something_with_a_slave(slave);
14
15 rcu_read_unlock();

Here, as you can see, we must make some heavy (time/memory-consuming) preparations to the bond *if it has slaves* before working *with each slave*, and if he doesn't - we can just omit those preparations (if we can...). But still it's a bit useless, and usually we anyway need proper RTNL or bond->lock locking before doing something like that.

Back to the real world - to implement the bond_first/last_slave_rcu(), we *must not* rely on list emptiness before taking the reference to the first/last element of the list. To make this happen, we *first* take the reference to list.next/list.prev, and only *after* it we verify if what we've dereferenced is an actual slave or the list head (i.e. the list was empty). If it's the list head - then we assume that the list was empty and return NULL, otherwise - if it's a normal slave - we return the container_of(what we've dereferenced).

Here's the function that gets the first element in the list:

268 #define list_first_or_null_rcu(ptr, type, member) \
269 ({struct list_head *__ptr = (ptr); \
270 struct list_head __rcu *__next = list_next_rcu(__ptr); \
271 likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \
272 })

As you can see, we first make a copy of the list_head pointer, which we were given - cause in the meanwhile *it can also, possibly, change*.

269 ({struct list_head *__ptr = (ptr); \

Then we dereference the pointer's copy.next, and also save it - cause it can *also* change - become (non)empty, for example.

270 struct list_head __rcu *__next = list_next_rcu(__ptr); \

And here comes the rcu magic, actually. We know that the ptr can change, the ptr.next can also change, however we also know that if ptr.next, when we've dereferenced it, while holding the rcu_read_lock(), was a normal list element - then we're sure that this element *will not go away*. Thus, everything what remained for us is just to verify - is our next pointer the list head or not? I.e. do we actually have an element in __next, or was the list empty at that time and we can return NULL? And, of course, if the list was not empty - we have a valid __next, and we can return its container:

271 likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \

(Also, mind the "likely(__ptr != __next)" - usually, and bonding is a very good example, we always have at least one element in the list.)

This way, I'd recommend you to do bond_last_slave_rcu() the same way as here - you, though, might omit saving the slave_list pointer, it's not needed in case of bonding AFAIK. Something like that (I'm writing it in my mail editor - so it's only for the reference):

#define bond_last_slave_rcu(bond) \
	({struct list_head *__slave_ptr = list_next_rcu(&bond->slave_list); \
	  likely(__slave_ptr != &bond->slave_list) ? \
		bond_to_slave_rcu(__slave_ptr) : NULL;})

Or, even better (from my POV), add a generic macro to rculist.h (again, didn't even compile it) - it can be used later on:

include/linux/rculist.h +7 -0
index f4b1001..37b49d1 100644
@@ -23,6 +23,7 @@
  * way, we must not access it directly
  */
 #define list_next_rcu(list)	(*((struct list_head __rcu **)(&(list)->next)))
+#define list_prev_rcu(list)	(*((struct list_head __rcu **)(&(list)->prev)))
 
 /*
  * Insert a new entry between two known consecutive entries.
@@ -271,6 +272,12 @@ static inline void list_splice_init_rcu(struct list_head *list,
 	  likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \
 	})
 
+#define list_last_or_null_rcu(ptr, type, member) \
+	({struct list_head *__ptr = (ptr); \
+	  struct list_head __rcu *__last = list_prev_rcu(__ptr); \
+	  likely(__ptr != __last) ? container_of(__prev, type, member) : NULL; \
+	})
+
 /**
  * list_for_each_entry_rcu	-	iterate over rcu list of given type
  * @pos:	the type * to use as a loop cursor.

------- END OF PATCH ------

Anyway, it's up to you.

Hope that helps.

#3 lore raw
Quoted 1 line · Veaceslav Falico

On Fri, Sep 06, 2013 at 03:28:07PM +0800, Ding Tianhong wrote:

<snip>

Quoted 32 lines

(Also, mind the "likely(__ptr != __next)" - usually, and bonding is a very good example, we always have at least one element in the list.)

This way, I'd recommend you to do bond_last_slave_rcu() the same way as here - you, though, might omit saving the slave_list pointer, it's not needed in case of bonding AFAIK. Something like that (I'm writing it in my mail editor - so it's only for the reference):

#define bond_last_slave_rcu(bond) \
    ({struct list_head *__slave_ptr = list_next_rcu(&bond->slave_list); \
      likely(__slave_ptr != &bond->slave_list) ? \
        bond_to_slave_rcu(__slave_ptr) : NULL;})

Or, even better (from my POV), add a generic macro to rculist.h (again, didn't even compile it) - it can be used later on:

include/linux/rculist.h +1 -0
index f4b1001..37b49d1 100644
@@ -23,6 +23,7 @@
  * way, we must not access it directly
  */
 #define list_next_rcu(list)    (*((struct list_head __rcu
 **)(&(list)->next)))
+#define list_prev_rcu(list)    (*((struct list_head __rcu
 **)(&(list)->prev)))
 

/*
* Insert a new entry between two known consecutive entries.

include/linux/rculist.h static inline void list_splice_init_rcu(struct · line 281

Quoted 10 lines
include/linux/rculist.h +6 -0
@@ -271,6 +272,12 @@ static inline void list_splice_init_rcu(struct
 list_head *list,
       likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \
     })
 
+#define list_last_or_null_rcu(ptr, type, member) \
+    ({struct list_head *__ptr = (ptr); \
+      struct list_head __rcu *__last = list_prev_rcu(__ptr); \
+      likely(__ptr != __last) ? container_of(__prev, type, member) : NULL; \
+    })
+

Hi,
Actually I don't think you can dereference ->prev and use the standard list_del_rcu because it guarantees only the ->next ptr will be valid and ->prev is set to LIST_POISON2.
IMO, you'll need something like this: https://lkml.org/lkml/2012/7/25/193
with the bidir_del and all that.

But in any case I complete agree with Veaceslav here. Read all the documentation carefully :-)

Cheers,
Nik

Quoted 8 lines
include/linux/rculist.h +0 -0
 /**
  * list_for_each_entry_rcu    -    iterate over rcu list of given type

* @pos: the type * to use as a loop cursor.
------- END OF PATCH ------

Anyway, it's up to you.

Hope that helps.

#4 lore raw
Quoted 3 lines · Nikolay Aleksandrov
Quoted 1 line · Veaceslav Falico

On Fri, Sep 06, 2013 at 03:28:07PM +0800, Ding Tianhong wrote:

...snip...

Quoted 19 lines
Quoted 19 lines
include/linux/rculist.h +1 -0
index f4b1001..37b49d1 100644
@@ -23,6 +23,7 @@
  * way, we must not access it directly
  */
 #define list_next_rcu(list)    (*((struct list_head __rcu
 **)(&(list)->next)))
+#define list_prev_rcu(list)    (*((struct list_head __rcu
 **)(&(list)->prev)))
 

/*
* Insert a new entry between two known consecutive entries.

include/linux/rculist.h +0 -0
@@ -271,6 +272,12 @@ static inline void list_splice_init_rcu(struct
 list_head *list,
       likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \
     })
 
Quoted 12 lines

include/linux/rculist.h static inline void list_splice_init_rcu(struct list_head *list, · line 280

Quoted 6 lines
include/linux/rculist.h +6 -0
+#define list_last_or_null_rcu(ptr, type, member) \
+    ({struct list_head *__ptr = (ptr); \
+      struct list_head __rcu *__last = list_prev_rcu(__ptr); \
+      likely(__ptr != __last) ? container_of(__prev, type, member) : NULL; \
+    })
+

Hi,
Actually I don't think you can dereference ->prev and use the standard list_del_rcu because it guarantees only the ->next ptr will be valid and ->prev is set to LIST_POISON2.
IMO, you'll need something like this: https://lkml.org/lkml/2012/7/25/193
with the bidir_del and all that.

Yeah, right, my bad - we can rely only on the ->next pointer, indeed, missed that part. RCU is hard :).

So it'll be a lot harder to implement bond_last_slave_rcu() in a 'straightforward' approach.

I'd rather go in the opposite direction here - i.e. drop the 'reverse' traversal completely, and all the use cases for bond_last_slave_rcu(). I've got some patches already - http://patchwork.ozlabs.org/patch/272076/ doing that, and hopefully will remove the whole 'backword' traversal completely in the future.

Quoted 16 lines

But in any case I complete agree with Veaceslav here. Read all the documentation carefully :-)

Cheers,
Nik

Quoted 8 lines
include/linux/rculist.h +0 -0
 /**
  * list_for_each_entry_rcu    -    iterate over rcu list of given type

* @pos: the type * to use as a loop cursor.
------- END OF PATCH ------

Anyway, it's up to you.

Hope that helps.

#5 lore raw
Quoted 41 lines · Veaceslav Falico
Quoted 3 lines · Nikolay Aleksandrov
Quoted 1 line · Veaceslav Falico

On Fri, Sep 06, 2013 at 03:28:07PM +0800, Ding Tianhong wrote:

...snip...

Quoted 19 lines
Quoted 19 lines
include/linux/rculist.h +1 -0
index f4b1001..37b49d1 100644
@@ -23,6 +23,7 @@
 * way, we must not access it directly
 */
 #define list_next_rcu(list) (*((struct list_head __rcu
 **)(&(list)->next)))
+#define list_prev_rcu(list) (*((struct list_head __rcu
 **)(&(list)->prev)))
 

/*
* Insert a new entry between two known consecutive entries.

include/linux/rculist.h +0 -0
@@ -271,6 +272,12 @@ static inline void list_splice_init_rcu(struct
 list_head *list,
 likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \
 })
 
Quoted 13 lines

include/linux/rculist.h static inline void list_splice_init_rcu(struct list_head *list, · line 280

Quoted 7 lines
include/linux/rculist.h +6 -0
+#define list_last_or_null_rcu(ptr, type, member) \
+ ({struct list_head *__ptr = (ptr); \
+ struct list_head __rcu *__last = list_prev_rcu(__ptr); \
+ likely(__ptr != __last) ? container_of(__prev, type, member) : 
 NULL; \
+ })
+

Hi,
Actually I don't think you can dereference ->prev and use the standard list_del_rcu because it guarantees only the ->next ptr will be valid and ->prev is set to LIST_POISON2.
IMO, you'll need something like this: https://lkml.org/lkml/2012/7/25/193
with the bidir_del and all that.

Yeah, right, my bad - we can rely only on the ->next pointer, indeed, missed that part. RCU is hard :).

Quoted 9 lines

So it'll be a lot harder to implement bond_last_slave_rcu() in a 'straightforward' approach.

I'd rather go in the opposite direction here - i.e. drop the 'reverse' traversal completely, and all the use cases for bond_last_slave_rcu(). I've got some patches already - http://patchwork.ozlabs.org/patch/272076/ doing that, and hopefully will remove the whole 'backword' traversal completely in the future.

Although RCU is truely difficult to understand, but it is very interesting and beautiful, I will follow your footsteps and finish it.

Quoted 7 lines
Quoted 7 lines

But in any case I complete agree with Veaceslav here. Read all the documentation carefully :-)

Cheers,
Nik

good lession, read it complete and agreed, it is a hard work to do. I need more time to review the Veaceslav's patchset and improve my thought, thanks Nik and Veaceslav.

Best regards
Ding

Quoted 14 lines
Quoted 9 lines
Quoted 8 lines
include/linux/rculist.h +0 -0
 /**

* list_for_each_entry_rcu - iterate over rcu list of given type
* @pos: the type * to use as a loop cursor.
------- END OF PATCH ------

Anyway, it's up to you.

Hope that helps.

Signature · 3 lines
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
#6 lore raw

On 2013/9/8 14:05, Ding Tianhong wrote:

Hi Veaceslav and Nik:

please take a moment to reveiw the function just modify for bond_XXX_rcu, and give me some advice. thanks for the help again.:)

+#define bond_first_slave_rcu(bond) \
+ list_first_or_null_rcu(&(bond)->slave_list, struct slave, list);
+#define bond_last_slave_rcu(bond) \
+ ({struct list_head *__slave_list = &(bond)->slave_list; \

+    struct list_head __rcu *__prev = \
+    (*((struct list_head __rcu **)(&(__slave_list)->prev)));\
+    likely(__slave_list != __prev) ? \
+    container_of(__prev, struct slave, list) : NULL;})

+
#define bond_is_first_slave(bond, pos) ((pos)->list.prev == &(bond)->slave_list) #define bond_is_last_slave(bond, pos) ((pos)->list.next == &(bond)->slave_list)

@@ -93,6 +117,29 @@

    (bond_is_first_slave(bond, pos) ? bond_last_slave(bond) : \
                      bond_to_slave((pos)->list.prev))

+/* Since bond_first/last_slave_rcu can return NULL, these can return NULL too */ +#define bond_next_slave_rcu(bond, pos) \
+ ({struct list_head *__slave_list = &(bond)->slave_list; \

+    struct list_head __rcu *__next = list_next_rcu(__slave_list); \
+    struct list_head *__pos_list = &(pos)->list; \
+    struct list_head __rcu *__pos_next = list_next_rcu(__pos_list); \
+    likely(__pos_next != __slave_list) ? \
+    container_of(__pos_next, struct slave, list) : \
+    container_of(__next, struct slave, list); \
+    })
+
+#define bond_prev_slave_rcu(bond, pos) \
+   ({struct list_head *__slave_list = &(bond)->slave_list; \
+    struct list_head __rcu *__prev = \
+    (*((struct list_head __rcu **)(&(__slave_list)->prev)));\
+    struct list_head *__pos_list = &(pos)->list; \
+    struct list_head __rcu *__pos_prev = (__pos_list->prev != LIST_POISON2) ? \
+    (*((struct list_head __rcu **)(&(__pos_list)->prev))) : NULL; \
+    likely(__pos_prev != __slave_list) ? \
+    ((__pos_prev) ? list_entry_rcu(__pos_prev, struct slave, list) : NULL;) : \
+    (list_entry_rcu(__prev, struct slave, list)); \
+    })

+

-#define bond_for_each_slave_from(bond, pos, cnt, start) \
- for (cnt = 0, pos = start; pos && cnt < (bond)->slave_cnt; \
- cnt++, pos = bond_next_slave(bond, pos))
-
+#define bond_for_each_slave_from(bond, pos, start) \
+ for (pos = start; pos; (pos = bond_next_slave(bond, pos)) != start ? \
+ (pos) : (pos = NULL))
+
+#define bond_for_each_slave_from_rcu(bond, pos, start) \
+ for ({struct list_head *__start = &(start)->list; \

+        struct list_head *__slave_list = &(bond)->slave_list; \
+        pos = list_entry_rcu(__start, struct slave, list);}; \
+        pos; \
+        {struct list_head __rcu *__next = list_next_rcu(pos->next); \
+        __next != __slave_list ? \
+        __next : __next = list_next_rcu(__next->next); \
+        __next != __start ? \
+        pos = list_entry_rcu(__next, struct slave, list) : \
+        pos = NULL; \
+        })

+

Best regards
Ding

Quoted 9 lines
Quoted 5 lines
Signature · 3 lines
To unsubscribe from this list: send the line "unsubsc ribe netdev" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

.

#7 lore raw
Quoted 7 lines · Ding Tianhong

On 2013/9/8 14:05, Ding Tianhong wrote:

Hi Veaceslav and Nik:

please take a moment to reveiw the function just modify for bond_XXX_rcu, and give me some advice. thanks for the help again.:)

Review

Quoted 8 lines
(quoted diff) +8 -0
+#define bond_first_slave_rcu(bond) \
+   list_first_or_null_rcu(&(bond)->slave_list, struct slave, list);
+#define bond_last_slave_rcu(bond) \
+   ({struct list_head *__slave_list = &(bond)->slave_list; \
+    struct list_head __rcu *__prev = \
+    (*((struct list_head __rcu **)(&(__slave_list)->prev)));\
+    likely(__slave_list != __prev) ? \
+    container_of(__prev, struct slave, list) : NULL;})

Please take a look at Nikolay's reply to my RCU email -
http://www.spinics.net/lists/netdev/msg249805.html . And mine also, to his email. In short - RCU doesn't guarantee ->prev, so better take the approach of eliminating bond_last/prev_slave completely.

Quoted 8 lines

+
#define bond_is_first_slave(bond, pos) ((pos)->list.prev == &(bond)->slave_list) #define bond_is_last_slave(bond, pos) ((pos)->list.next == &(bond)->slave_list)

(quoted diff) +0 -0
@@ -93,6 +117,29 @@
   (bond_is_first_slave(bond, pos) ? bond_last_slave(bond) : \
                     bond_to_slave((pos)->list.prev))
 

Review line 129

Quoted 10 lines
(quoted diff) +10 -0
+/* Since bond_first/last_slave_rcu can return NULL, these can return NULL too */
+#define bond_next_slave_rcu(bond, pos) \
+   ({struct list_head *__slave_list = &(bond)->slave_list; \
+    struct list_head __rcu *__next = list_next_rcu(__slave_list); \
+    struct list_head *__pos_list = &(pos)->list; \
+    struct list_head __rcu *__pos_next = list_next_rcu(__pos_list); \
+    likely(__pos_next != __slave_list) ? \
+    container_of(__pos_next, struct slave, list) : \
+    container_of(__next, struct slave, list); \
+    })

Nice, but can be shortened - we know that pos won't go away.

Review line 141

Quoted 12 lines
(quoted diff) +12 -0
+
+#define bond_prev_slave_rcu(bond, pos) \
+   ({struct list_head *__slave_list = &(bond)->slave_list; \
+    struct list_head __rcu *__prev = \
+    (*((struct list_head __rcu **)(&(__slave_list)->prev)));\
+    struct list_head *__pos_list = &(pos)->list; \
+    struct list_head __rcu *__pos_prev = (__pos_list->prev != LIST_POISON2) ? \
+    (*((struct list_head __rcu **)(&(__pos_list)->prev))) : NULL; \
+    likely(__pos_prev != __slave_list) ? \
+    ((__pos_prev) ? list_entry_rcu(__pos_prev, struct slave, list) : NULL;) : \
+    (list_entry_rcu(__prev, struct slave, list)); \
+    })

Same remark as above about prev.

Quoted 13 lines
(quoted diff) +7 -4
+
 
 
-#define bond_for_each_slave_from(bond, pos, cnt, start) \
-   for (cnt = 0, pos = start; pos && cnt < (bond)->slave_cnt; \
-        cnt++, pos = bond_next_slave(bond, pos))
-
+#define bond_for_each_slave_from(bond, pos, start) \
+   for (pos = start; pos; (pos = bond_next_slave(bond, pos)) != start ? \
+       (pos) : (pos = NULL))
+
+#define bond_for_each_slave_from_rcu(bond, pos, start) \
+   for ({struct list_head *__start = &(start)->list; \

Review

Quoted 10 lines
(quoted diff) +10 -0
+        struct list_head *__slave_list = &(bond)->slave_list; \
+        pos = list_entry_rcu(__start, struct slave, list);}; \
+        pos; \
+        {struct list_head __rcu *__next = list_next_rcu(pos->next); \
+        __next != __slave_list ? \
+        __next : __next = list_next_rcu(__next->next); \
+        __next != __start ? \
+        pos = list_entry_rcu(__next, struct slave, list) : \
+        pos = NULL; \
+        })

Jeez, I don't even want to review it. It's too complex and too hard to maintain, even if it works. Can you please make something shorter/easier to understand?

Quoted 17 lines

+

Best regards
Ding

Quoted 9 lines
Quoted 5 lines

--
To unsubscribe from this list: send the line "unsubsc ribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

.

#8 lore raw
Quoted 8 lines · Veaceslav Falico
Quoted 7 lines · Ding Tianhong

On 2013/9/8 14:05, Ding Tianhong wrote:

Hi Veaceslav and Nik:

please take a moment to reveiw the function just modify for bond_XXX_rcu, and give me some advice. thanks for the help again.:)

Quoted 14 lines

Review

Quoted 8 lines
(quoted diff) +8 -0
+#define bond_first_slave_rcu(bond) \
+ list_first_or_null_rcu(&(bond)->slave_list, struct slave, list);
+#define bond_last_slave_rcu(bond) \
+ ({struct list_head *__slave_list = &(bond)->slave_list; \
+ struct list_head __rcu *__prev = \
+ (*((struct list_head __rcu **)(&(__slave_list)->prev)));\
+ likely(__slave_list != __prev) ? \
+ container_of(__prev, struct slave, list) : NULL;})

Please take a look at Nikolay's reply to my RCU email -
http://www.spinics.net/lists/netdev/msg249805.html . And mine also, to his email. In short - RCU doesn't guarantee ->prev, so better take the approach of eliminating bond_last/prev_slave completely.

yes, I see the message, the list_del_rcu will make the slave->list ->prev = LIST_POISON2, the bond->slave_list will not be set to the messae, the prev will point a slave->list or itself, so I think it will be ok here, please correct me if I miss something.

Best Regards
Ding

Quoted 8 lines
Quoted 8 lines

+
#define bond_is_first_slave(bond, pos) ((pos)->list.prev == &(bond)->slave_list) #define bond_is_last_slave(bond, pos) ((pos)->list.next == &(bond)->slave_list)

(quoted diff) +0 -0
@@ -93,6 +117,29 @@
 (bond_is_first_slave(bond, pos) ? bond_last_slave(bond) : \
 bond_to_slave((pos)->list.prev))
 
Quoted 13 lines
Quoted 11 lines
(quoted diff) +1 -0
+/* Since bond_first/last_slave_rcu can return NULL, these can return 

NULL too */
+#define bond_next_slave_rcu(bond, pos) \
+ ({struct list_head *__slave_list = &(bond)->slave_list; \
+ struct list_head __rcu *__next = list_next_rcu(__slave_list); \
+ struct list_head *__pos_list = &(pos)->list; \
+ struct list_head __rcu *__pos_next = list_next_rcu(__pos_list); \
+ likely(__pos_next != __slave_list) ? \
+ container_of(__pos_next, struct slave, list) : \
+ container_of(__next, struct slave, list); \
+ })

Nice, but can be shortened - we know that pos won't go away.

OK, clean it soon.

Quoted 9 lines
Quoted 9 lines
(quoted diff) +7 -0
+
+#define bond_prev_slave_rcu(bond, pos) \
+ ({struct list_head *__slave_list = &(bond)->slave_list; \
+ struct list_head __rcu *__prev = \
+ (*((struct list_head __rcu **)(&(__slave_list)->prev)));\
+ struct list_head *__pos_list = &(pos)->list; \
+ struct list_head __rcu *__pos_prev = (__pos_list->prev 

!=LIST_POISON2) ? \

yes, the pos->list will be set to LIST_POISON2 by list_del_rcu, so I add a check for it, But take the approach of eliminating bond_last/prev_slave completely is a wise decision, I agree.

Quoted 12 lines
Quoted 6 lines

+ (*((struct list_head __rcu **)(&(__pos_list)->prev))) : NULL; \
+ likely(__pos_prev != __slave_list) ? \
+ ((__pos_prev) ? list_entry_rcu(__pos_prev, struct slave, list) :
NULL;) : \
+ (list_entry_rcu(__prev, struct slave, list)); \
+ })

Same remark as above about prev.

Quoted 3 lines

+

Quoted 9 lines

Review

Quoted 9 lines
(quoted diff) +5 -4
-#define bond_for_each_slave_from(bond, pos, cnt, start) \
- for (cnt = 0, pos = start; pos && cnt < (bond)->slave_cnt; \
- cnt++, pos = bond_next_slave(bond, pos))
-
+#define bond_for_each_slave_from(bond, pos, start) \
+ for (pos = start; pos; (pos = bond_next_slave(bond, pos)) != start ? \
+ (pos) : (pos = NULL))
+
+#define bond_for_each_slave_from_rcu(bond, pos, start) \

yes, it is a little tedious. I think it could be more easier and shorter.

Quoted 4 lines
Quoted 4 lines

+ for ({struct list_head *__start = &(start)->list; \
+ struct list_head *__slave_list = &(bond)->slave_list; \
+ pos = list_entry_rcu(__start, struct slave, list);}; \
+ pos; \

the only way to get out of the loop is that pos is NULL.

Quoted 3 lines
Quoted 3 lines

+ {struct list_head __rcu *__next = list_next_rcu(pos->next); \
+ __next != __slave_list ? \
+ __next : __next = list_next_rcu(__next->next); \

first, check whether the pos->next is the last one in the slave_list, if it does, get the first slave of the bond->slave_list.

Quoted 4 lines
Quoted 4 lines

+ __next != __start ? \
+ pos = list_entry_rcu(__next, struct slave, list) : \
+ pos = NULL; \

second, check whether the pos is reach the start, if not, continue, otherwise, the pos will be set to NULL, so break the loop.

Quoted 6 lines
Quoted 1 line

+ })

Jeez, I don't even want to review it. It's too complex and too hard to maintain, even if it works. Can you please make something shorter/easier to understand?

Best Regards.
Ding

Quoted 22 lines
Quoted 17 lines

+

Best regards
Ding

Quoted 9 lines
Quoted 5 lines
Signature · 3 lines
To unsubscribe from this list: send the line "unsubsc ribe netdev" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

.

Signature · 3 lines
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
#9 lore raw
Quoted 23 lines · Ding Tianhong
Quoted 8 lines · Veaceslav Falico
Quoted 7 lines · Ding Tianhong

On 2013/9/8 14:05, Ding Tianhong wrote:

Hi Veaceslav and Nik:

please take a moment to reveiw the function just modify for bond_XXX_rcu, and give me some advice. thanks for the help again.:)

Quoted 14 lines

Review

Quoted 8 lines
(quoted diff) +8 -0
+#define bond_first_slave_rcu(bond) \
+ list_first_or_null_rcu(&(bond)->slave_list, struct slave, list);
+#define bond_last_slave_rcu(bond) \
+ ({struct list_head *__slave_list = &(bond)->slave_list; \
+ struct list_head __rcu *__prev = \
+ (*((struct list_head __rcu **)(&(__slave_list)->prev)));\
+ likely(__slave_list != __prev) ? \
+ container_of(__prev, struct slave, list) : NULL;})

Please take a look at Nikolay's reply to my RCU email -
http://www.spinics.net/lists/netdev/msg249805.html . And mine also, to his email. In short - RCU doesn't guarantee ->prev, so better take the approach of eliminating bond_last/prev_slave completely.

Quoted 4 lines

yes, I see the message, the list_del_rcu will make the slave->list
->prev = LIST_POISON2,
the bond->slave_list will not be set to the messae, the prev will point a slave->list or itself, so I think it will be ok here, please correct me if I miss something.

Hi Ding,

Please take a look at the patchset I've just sent to net-next:

[PATCH net-next 0/26] bonding: use neighbours instead of own lists

It removes all the ->prev usage, and uses completely different primitives for list traversal, and - *drops* the bond->slave_list and slave->list completely. I think it would be a lot easier to RCUify bonding further providing this patchset.

Thanks.

Quoted 112 lines

Best Regards
Ding

Quoted 8 lines
Quoted 8 lines

+
#define bond_is_first_slave(bond, pos) ((pos)->list.prev == &(bond)->slave_list) #define bond_is_last_slave(bond, pos) ((pos)->list.next == &(bond)->slave_list)

(quoted diff) +0 -0
@@ -93,6 +117,29 @@
 (bond_is_first_slave(bond, pos) ? bond_last_slave(bond) : \
 bond_to_slave((pos)->list.prev))
 
Quoted 13 lines
Quoted 11 lines
(quoted diff) +1 -0
+/* Since bond_first/last_slave_rcu can return NULL, these can 

return NULL too */
+#define bond_next_slave_rcu(bond, pos) \
+ ({struct list_head *__slave_list = &(bond)->slave_list; \
+ struct list_head __rcu *__next = list_next_rcu(__slave_list); \
+ struct list_head *__pos_list = &(pos)->list; \
+ struct list_head __rcu *__pos_next = list_next_rcu(__pos_list); \
+ likely(__pos_next != __slave_list) ? \
+ container_of(__pos_next, struct slave, list) : \
+ container_of(__next, struct slave, list); \
+ })

Nice, but can be shortened - we know that pos won't go away.

OK, clean it soon.

Quoted 9 lines
Quoted 9 lines
(quoted diff) +7 -0
+
+#define bond_prev_slave_rcu(bond, pos) \
+ ({struct list_head *__slave_list = &(bond)->slave_list; \
+ struct list_head __rcu *__prev = \
+ (*((struct list_head __rcu **)(&(__slave_list)->prev)));\
+ struct list_head *__pos_list = &(pos)->list; \
+ struct list_head __rcu *__pos_prev = (__pos_list->prev 

!=LIST_POISON2) ? \

yes, the pos->list will be set to LIST_POISON2 by list_del_rcu, so I add a check for it, But take the approach of eliminating bond_last/prev_slave completely is a wise decision, I agree.

Quoted 12 lines
Quoted 6 lines

+ (*((struct list_head __rcu **)(&(__pos_list)->prev))) : NULL; \
+ likely(__pos_prev != __slave_list) ? \
+ ((__pos_prev) ? list_entry_rcu(__pos_prev, struct slave, list) : NULL;) : \
+ (list_entry_rcu(__prev, struct slave, list)); \
+ })

Same remark as above about prev.

Quoted 3 lines

+

Quoted 9 lines

Review

Quoted 9 lines
(quoted diff) +5 -4
-#define bond_for_each_slave_from(bond, pos, cnt, start) \
- for (cnt = 0, pos = start; pos && cnt < (bond)->slave_cnt; \
- cnt++, pos = bond_next_slave(bond, pos))
-
+#define bond_for_each_slave_from(bond, pos, start) \
+ for (pos = start; pos; (pos = bond_next_slave(bond, pos)) != start ? \
+ (pos) : (pos = NULL))
+
+#define bond_for_each_slave_from_rcu(bond, pos, start) \

yes, it is a little tedious. I think it could be more easier and shorter.

Quoted 4 lines
Quoted 4 lines

+ for ({struct list_head *__start = &(start)->list; \
+ struct list_head *__slave_list = &(bond)->slave_list; \
+ pos = list_entry_rcu(__start, struct slave, list);}; \
+ pos; \

the only way to get out of the loop is that pos is NULL.

Quoted 3 lines
Quoted 3 lines

+ {struct list_head __rcu *__next = list_next_rcu(pos->next); \
+ __next != __slave_list ? \
+ __next : __next = list_next_rcu(__next->next); \

first, check whether the pos->next is the last one in the slave_list, if it does, get the first slave of the bond->slave_list.

Quoted 4 lines
Quoted 4 lines

+ __next != __start ? \
+ pos = list_entry_rcu(__next, struct slave, list) : \
+ pos = NULL; \

second, check whether the pos is reach the start, if not, continue, otherwise, the pos will be set to NULL, so break the loop.

Quoted 6 lines
Quoted 1 line

+ })

Jeez, I don't even want to review it. It's too complex and too hard to maintain, even if it works. Can you please make something shorter/easier to understand?

Best Regards.
Ding

Quoted 22 lines
Quoted 17 lines

+

Best regards
Ding

Quoted 9 lines
Quoted 5 lines
Signature · 3 lines
To unsubscribe from this list: send the line "unsubsc ribe netdev" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

.

Signature · 3 lines
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
#10 lore raw
In reply to Veaceslav Falico
Quoted 40 lines · Veaceslav Falico
Quoted 3 lines · Nikolay Aleksandrov
Quoted 1 line · Veaceslav Falico

On Fri, Sep 06, 2013 at 03:28:07PM +0800, Ding Tianhong wrote:

...snip...

Quoted 19 lines
Quoted 19 lines
include/linux/rculist.h +1 -0
index f4b1001..37b49d1 100644
@@ -23,6 +23,7 @@
 * way, we must not access it directly
 */
 #define list_next_rcu(list)    (*((struct list_head __rcu
 **)(&(list)->next)))
+#define list_prev_rcu(list)    (*((struct list_head __rcu
 **)(&(list)->prev)))
 

/*
* Insert a new entry between two known consecutive entries.

include/linux/rculist.h +0 -0
@@ -271,6 +272,12 @@ static inline void list_splice_init_rcu(struct
 list_head *list,
      likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \
    })
 
Quoted 12 lines

include/linux/rculist.h static inline void list_splice_init_rcu(struct list_head *list, · line 280

Quoted 6 lines
include/linux/rculist.h +6 -0
+#define list_last_or_null_rcu(ptr, type, member) \
+    ({struct list_head *__ptr = (ptr); \
+      struct list_head __rcu *__last = list_prev_rcu(__ptr); \
+      likely(__ptr != __last) ? container_of(__prev, type, member) : NULL; \
+    })
+

Hi,
Actually I don't think you can dereference ->prev and use the standard list_del_rcu because it guarantees only the ->next ptr will be valid and ->prev is set to LIST_POISON2.
IMO, you'll need something like this: https://lkml.org/lkml/2012/7/25/193
with the bidir_del and all that.

Yeah, right, my bad - we can rely only on the ->next pointer, indeed, missed that part. RCU is hard :).

Quoted 8 lines

So it'll be a lot harder to implement bond_last_slave_rcu() in a 'straightforward' approach.

I'd rather go in the opposite direction here - i.e. drop the 'reverse' traversal completely, and all the use cases for bond_last_slave_rcu(). I've got some patches already - http://patchwork.ozlabs.org/patch/272076/ doing that, and hopefully will remove the whole 'backword' traversal completely in the future.

If it is important, it would be possible to create an RCU-protected linked list that allowed readers to go in both directions -- mostly just leave out the poisoning of the ->prev pointers. We do -not- want to do this for the normal RCU-protected linked lists because the poisoning does find bugs.

However, you would have to be quite careful with a bi-directional RCU-protected linked list, because p->next->prev will not necessarily be equal to p, for all the reasons that you guys listed out earlier in this thread. So be very careful what you wish for! ;-)

Thanx, Paul

Quoted 20 lines
Quoted 15 lines

But in any case I complete agree with Veaceslav here. Read all the documentation carefully :-)

Cheers,
Nik

Quoted 8 lines
include/linux/rculist.h +0 -0
 /**
 * list_for_each_entry_rcu    -    iterate over rcu list of given type

* @pos: the type * to use as a loop cursor.
------- END OF PATCH ------

Anyway, it's up to you.

Hope that helps.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html