[PATCH net v5 1/4] ax25: Use kernel universal linked list to implement ax25_dev_list

Duoming Zhou posted 4 patches 1 year, 9 months ago
There is a newer version of this series
[PATCH net v5 1/4] ax25: Use kernel universal linked list to implement ax25_dev_list
Posted by Duoming Zhou 1 year, 9 months ago
The origin ax25_dev_list implements its own single linked list,
which is complicated and error-prone. For example, when deleting
the node of ax25_dev_list in ax25_dev_device_down(), we have to
operate on the head node and other nodes separately.

This patch uses kernel universal linked list to replace original
ax25_dev_list, which make the operation of ax25_dev_list easier.
There are two points that need to notice:

[1] We should add a check to judge whether the list is empty before
INIT_LIST_HEAD in ax25_dev_device_up(), otherwise it will empty the
list for each new ax25_dev added.

[2] We should do "dev->ax25_ptr = ax25_dev;" and "dev->ax25_ptr = NULL;"
while holding the spinlock, otherwise the ax25_dev_device_up() and
ax25_dev_device_down() could race, we're not guaranteed to find a match
ax25_dev in ax25_dev_device_down().

Suggested-by: Dan Carpenter <dan.carpenter@linaro.org>
Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
---
 include/net/ax25.h  |  3 +--
 net/ax25/ax25_dev.c | 43 ++++++++++++++++++-------------------------
 2 files changed, 19 insertions(+), 27 deletions(-)

diff --git a/include/net/ax25.h b/include/net/ax25.h
index 0d939e5aee4..c2a85fd3f5e 100644
--- a/include/net/ax25.h
+++ b/include/net/ax25.h
@@ -216,7 +216,7 @@ typedef struct {
 struct ctl_table;
 
 typedef struct ax25_dev {
-	struct ax25_dev		*next;
+	struct list_head	list;
 
 	struct net_device	*dev;
 	netdevice_tracker	dev_tracker;
@@ -330,7 +330,6 @@ int ax25_addr_size(const ax25_digi *);
 void ax25_digi_invert(const ax25_digi *, ax25_digi *);
 
 /* ax25_dev.c */
-extern ax25_dev *ax25_dev_list;
 extern spinlock_t ax25_dev_lock;
 
 #if IS_ENABLED(CONFIG_AX25)
diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c
index 282ec581c07..1557f879377 100644
--- a/net/ax25/ax25_dev.c
+++ b/net/ax25/ax25_dev.c
@@ -22,11 +22,12 @@
 #include <net/sock.h>
 #include <linux/uaccess.h>
 #include <linux/fcntl.h>
+#include <linux/list.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 
-ax25_dev *ax25_dev_list;
+static struct list_head ax25_dev_list;
 DEFINE_SPINLOCK(ax25_dev_lock);
 
 ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
@@ -34,7 +35,7 @@ ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
 	ax25_dev *ax25_dev, *res = NULL;
 
 	spin_lock_bh(&ax25_dev_lock);
-	for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
+	list_for_each_entry(ax25_dev, &ax25_dev_list, list)
 		if (ax25cmp(addr, (const ax25_address *)ax25_dev->dev->dev_addr) == 0) {
 			res = ax25_dev;
 			ax25_dev_hold(ax25_dev);
@@ -52,6 +53,9 @@ void ax25_dev_device_up(struct net_device *dev)
 {
 	ax25_dev *ax25_dev;
 
+	/* Initialized the list for the first entry */
+	if (!ax25_dev_list.next)
+		INIT_LIST_HEAD(&ax25_dev_list);
 	ax25_dev = kzalloc(sizeof(*ax25_dev), GFP_KERNEL);
 	if (!ax25_dev) {
 		printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n");
@@ -59,7 +63,6 @@ void ax25_dev_device_up(struct net_device *dev)
 	}
 
 	refcount_set(&ax25_dev->refcount, 1);
-	dev->ax25_ptr     = ax25_dev;
 	ax25_dev->dev     = dev;
 	netdev_hold(dev, &ax25_dev->dev_tracker, GFP_KERNEL);
 	ax25_dev->forward = NULL;
@@ -85,8 +88,8 @@ void ax25_dev_device_up(struct net_device *dev)
 #endif
 
 	spin_lock_bh(&ax25_dev_lock);
-	ax25_dev->next = ax25_dev_list;
-	ax25_dev_list  = ax25_dev;
+	list_add(&ax25_dev->list, &ax25_dev_list);
+	dev->ax25_ptr     = ax25_dev;
 	spin_unlock_bh(&ax25_dev_lock);
 	ax25_dev_hold(ax25_dev);
 
@@ -111,32 +114,25 @@ void ax25_dev_device_down(struct net_device *dev)
 	/*
 	 *	Remove any packet forwarding that points to this device.
 	 */
-	for (s = ax25_dev_list; s != NULL; s = s->next)
+	list_for_each_entry(s, &ax25_dev_list, list)
 		if (s->forward == dev)
 			s->forward = NULL;
 
-	if ((s = ax25_dev_list) == ax25_dev) {
-		ax25_dev_list = s->next;
-		goto unlock_put;
-	}
-
-	while (s != NULL && s->next != NULL) {
-		if (s->next == ax25_dev) {
-			s->next = ax25_dev->next;
+	list_for_each_entry(s, &ax25_dev_list, list) {
+		if (s == ax25_dev) {
+			list_del(&s->list);
 			goto unlock_put;
 		}
-
-		s = s->next;
 	}
-	spin_unlock_bh(&ax25_dev_lock);
 	dev->ax25_ptr = NULL;
+	spin_unlock_bh(&ax25_dev_lock);
 	ax25_dev_put(ax25_dev);
 	return;
 
 unlock_put:
+	dev->ax25_ptr = NULL;
 	spin_unlock_bh(&ax25_dev_lock);
 	ax25_dev_put(ax25_dev);
-	dev->ax25_ptr = NULL;
 	netdev_put(dev, &ax25_dev->dev_tracker);
 	ax25_dev_put(ax25_dev);
 }
@@ -200,16 +196,13 @@ struct net_device *ax25_fwd_dev(struct net_device *dev)
  */
 void __exit ax25_dev_free(void)
 {
-	ax25_dev *s, *ax25_dev;
+	ax25_dev *s, *n;
 
 	spin_lock_bh(&ax25_dev_lock);
-	ax25_dev = ax25_dev_list;
-	while (ax25_dev != NULL) {
-		s        = ax25_dev;
-		netdev_put(ax25_dev->dev, &ax25_dev->dev_tracker);
-		ax25_dev = ax25_dev->next;
+	list_for_each_entry_safe(s, n, &ax25_dev_list, list) {
+		netdev_put(s->dev, &s->dev_tracker);
+		list_del(&s->list);
 		kfree(s);
 	}
-	ax25_dev_list = NULL;
 	spin_unlock_bh(&ax25_dev_lock);
 }
-- 
2.17.1
Re: [PATCH net v5 1/4] ax25: Use kernel universal linked list to implement ax25_dev_list
Posted by Lars Kellogg-Stedman 1 year, 9 months ago
On Tue, May 07, 2024 at 03:03:39PM GMT, Duoming Zhou wrote:
>  typedef struct ax25_dev {
> -	struct ax25_dev		*next;
> +	struct list_head	list;

Would it make sense to replace this with:

LIST_HEAD(ax25_dev_list);

And then get rid of:

> +	/* Initialized the list for the first entry */
> +	if (!ax25_dev_list.next)
> +		INIT_LIST_HEAD(&ax25_dev_list);

-- 
Lars Kellogg-Stedman <lars@oddbit.com> | larsks @ {irc,twitter,github}
http://blog.oddbit.com/                | N1LKS
Re: [PATCH net v5 1/4] ax25: Use kernel universal linked list to implement ax25_dev_list
Posted by Lars Kellogg-Stedman 1 year, 9 months ago
On Tue, May 07, 2024 at 03:43:11PM GMT, Lars Kellogg-Stedman wrote:
> On Tue, May 07, 2024 at 03:03:39PM GMT, Duoming Zhou wrote:
> >  typedef struct ax25_dev {
> > -	struct ax25_dev		*next;
> > +	struct list_head	list;
> 
> Would it make sense to replace this with:
>
> LIST_HEAD(ax25_dev_list);

Sorry, *this*:

> +static struct list_head ax25_dev_list;


-- 
Lars Kellogg-Stedman <lars@oddbit.com> | larsks @ {irc,twitter,github}
http://blog.oddbit.com/                | N1LKS
Re: [PATCH net v5 1/4] ax25: Use kernel universal linked list to implement ax25_dev_list
Posted by Ratheesh Kannoth 1 year, 9 months ago
On 2024-05-07 at 12:33:39, Duoming Zhou (duoming@zju.edu.cn) wrote:
> The origin ax25_dev_list implements its own single linked list,
> which is complicated and error-prone. For example, when deleting
> the node of ax25_dev_list in ax25_dev_device_down(), we have to
> operate on the head node and other nodes separately.
>
> This patch uses kernel universal linked list to replace original
> ax25_dev_list, which make the operation of ax25_dev_list easier.
> There are two points that need to notice:
>
> [1] We should add a check to judge whether the list is empty before
> INIT_LIST_HEAD in ax25_dev_device_up(), otherwise it will empty the
> list for each new ax25_dev added.
>
> [2] We should do "dev->ax25_ptr = ax25_dev;" and "dev->ax25_ptr = NULL;"
> while holding the spinlock, otherwise the ax25_dev_device_up() and
> ax25_dev_device_down() could race, we're not guaranteed to find a match
> ax25_dev in ax25_dev_device_down().
>
> Suggested-by: Dan Carpenter <dan.carpenter@linaro.org>
> Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
> -ax25_dev *ax25_dev_list;
> +static struct list_head ax25_dev_list;
>  DEFINE_SPINLOCK(ax25_dev_lock);
>
>  ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
> @@ -34,7 +35,7 @@ ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
>  	ax25_dev *ax25_dev, *res = NULL;
>
>  	spin_lock_bh(&ax25_dev_lock);
> -	for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
> +	list_for_each_entry(ax25_dev, &ax25_dev_list, list)
>  		if (ax25cmp(addr, (const ax25_address *)ax25_dev->dev->dev_addr) == 0) {
>  			res = ax25_dev;
>  			ax25_dev_hold(ax25_dev);
> @@ -52,6 +53,9 @@ void ax25_dev_device_up(struct net_device *dev)
>  {
>  	ax25_dev *ax25_dev;
>
> +	/* Initialized the list for the first entry */
> +	if (!ax25_dev_list.next)
> +		INIT_LIST_HEAD(&ax25_dev_list);
if you define ax25_dev_list using 'static LIST_HEAD(ax25_dev_list)', you need this conditional check and
initialization ?

>  	ax25_dev = kzalloc(sizeof(*ax25_dev), GFP_KERNEL);
>  	if (!ax25_dev) {
>  		printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n");
> @@ -59,7 +63,6 @@ void ax25_dev_device_up(struct net_device *dev)
>  	}
>
>  	refcount_set(&ax25_dev->refcount, 1);
>
Re: [PATCH net v5 1/4] ax25: Use kernel universal linked list to implement ax25_dev_list
Posted by Dan Carpenter 1 year, 9 months ago
On Tue, May 07, 2024 at 02:59:17PM +0530, Ratheesh Kannoth wrote:
> On 2024-05-07 at 12:33:39, Duoming Zhou (duoming@zju.edu.cn) wrote:
> > The origin ax25_dev_list implements its own single linked list,
> > which is complicated and error-prone. For example, when deleting
> > the node of ax25_dev_list in ax25_dev_device_down(), we have to
> > operate on the head node and other nodes separately.
> >
> > This patch uses kernel universal linked list to replace original
> > ax25_dev_list, which make the operation of ax25_dev_list easier.
> > There are two points that need to notice:
> >
> > [1] We should add a check to judge whether the list is empty before
> > INIT_LIST_HEAD in ax25_dev_device_up(), otherwise it will empty the
> > list for each new ax25_dev added.
> >
> > [2] We should do "dev->ax25_ptr = ax25_dev;" and "dev->ax25_ptr = NULL;"
> > while holding the spinlock, otherwise the ax25_dev_device_up() and
> > ax25_dev_device_down() could race, we're not guaranteed to find a match
> > ax25_dev in ax25_dev_device_down().
> >
> > Suggested-by: Dan Carpenter <dan.carpenter@linaro.org>
> > Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
> > -ax25_dev *ax25_dev_list;
> > +static struct list_head ax25_dev_list;
> >  DEFINE_SPINLOCK(ax25_dev_lock);
> >
> >  ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
> > @@ -34,7 +35,7 @@ ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
> >  	ax25_dev *ax25_dev, *res = NULL;
> >
> >  	spin_lock_bh(&ax25_dev_lock);
> > -	for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
> > +	list_for_each_entry(ax25_dev, &ax25_dev_list, list)
> >  		if (ax25cmp(addr, (const ax25_address *)ax25_dev->dev->dev_addr) == 0) {
> >  			res = ax25_dev;
> >  			ax25_dev_hold(ax25_dev);
> > @@ -52,6 +53,9 @@ void ax25_dev_device_up(struct net_device *dev)
> >  {
> >  	ax25_dev *ax25_dev;
> >
> > +	/* Initialized the list for the first entry */
> > +	if (!ax25_dev_list.next)
> > +		INIT_LIST_HEAD(&ax25_dev_list);
> if you define ax25_dev_list using 'static LIST_HEAD(ax25_dev_list)', you need this conditional check and
> initialization ?
> 

Ah, yes.  That's the proper way to do it.

regards,
dan carpenter
Re: [PATCH net v5 1/4] ax25: Use kernel universal linked list to implement ax25_dev_list
Posted by Markus Elfring 1 year, 9 months ago
> … that need to notice:

I suggest to improve such a wording.


> [1] We should add a check to judge whether …

Are imperative wordings more desirable for improved change descriptions?
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/submitting-patches.rst?h=v6.9-rc7#n94

Regards,
Markus