Skip to contentSkip to conversation

RFC: Boiler plate functions for ida / idr allocation?

PATCH Jonathan Cameron 5 participants 15 messages Jul 13, 2011 – Jul 22, 2011

Subsystems (8)
SCSI Staging Library code IIO +4 more subsystems
lore
Share & tools
lore
#0 lore raw

One bit of common functionality that a lot of subsystem cores (and some drivers) implement is allocation of unique id's.

Now we have ida / idr for this. However, given I copied someone else's code for those in IIO, I took a look around to see how common these 20 ish lines of code were.

The answer is fairly, though in some cases in subtly different forms. Seems to be from a naive standpoint to be a classic case for a convenient library function.

Taking ida's first, how about the following patch? I'm not at all attached to the form it takes, merely to cutting out on the cut and paste.

Note I've done only a few random places. There aren't that many ida users and I've only played with that for now.

Remaining ida cases:

drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c: (cleans up in case of out of range) drivers/scsi/osd/osd_uld.c: (locks not taken)

However, a few users of idr's that look like they are using them only as idas (perhaps predating ida availability?)

e.g.
drivers/hwmon/hwmon.c

The other thing this highlights is that I suspect quite a few are protected by spin locks when a mutex would be fine. Hence that might be worth tidying up first.

Anyhow, a cleanup worth making? (obviously the exact form needs some work, but I think the following is enough to start a discussion!)

Subject: [PATCH] ida utility function

---
 drivers/misc/cb710/core.c                  |   15 +-----
 drivers/scsi/sd.c                          |   10 +----
 drivers/staging/iio/iio.h                  |    1 -
 drivers/staging/iio/industrialio-core.c    |   38 ++--------------
 drivers/staging/iio/industrialio-trigger.c |   65 ++++++----------------------
 include/linux/idr.h                        |    1 +
 lib/idr.c                                  |   20 +++++++++
 7 files changed, 42 insertions(+), 108 deletions(-)
drivers/misc/cb710/core.c +3 -12
index efec413..914be30 100644
@@ -254,18 +254,9 @@ static int __devinit cb710_probe(struct pci_dev *pdev,
 	if (err)
 		return err;
 
-	do {
-		if (!ida_pre_get(&cb710_ida, GFP_KERNEL))
-			return -ENOMEM;
-
-		spin_lock_irqsave(&cb710_ida_lock, flags);
-		err = ida_get_new(&cb710_ida, &chip->platform_id);
-		spin_unlock_irqrestore(&cb710_ida_lock, flags);
-
-		if (err && err != -EAGAIN)
-			return err;
-	} while (err);
-
+	err = ida_get_id(&cb710_ida_lock, &cb710_ida, &chip->platform_id);
+	if (err)
+		return err;
 
 	dev_info(&pdev->dev, "id %d, IO 0x%p, IRQ %d\n",
 		chip->platform_id, chip->iobase, pdev->irq);
drivers/scsi/sd.c +1 -9
index 953773c..aadf8a3 100644
@@ -2580,15 +2580,7 @@ static int sd_probe(struct device *dev)
 	if (!gd)
 		goto out_free;
 
-	do {
-		if (!ida_pre_get(&sd_index_ida, GFP_KERNEL))
-			goto out_put;
-
-		spin_lock(&sd_index_lock);
-		error = ida_get_new(&sd_index_ida, &index);
-		spin_unlock(&sd_index_lock);
-	} while (error == -EAGAIN);
-
+	error = ida_get_id(&sd_index_lock, &sd_index_ida, &index);
 	if (error)
 		goto out_put;
 
drivers/staging/iio/iio.h +0 -1
index 2a8c8e2..7bdda68 100644
@@ -426,6 +426,5 @@ static inline bool iio_ring_enabled(struct iio_dev *dev_info)
 
 struct ida;
 
-int iio_get_new_ida_val(struct ida *this_ida);
 void iio_free_ida_val(struct ida *this_ida, int id);
 #endif /* _INDUSTRIAL_IO_H_ */
drivers/staging/iio/industrialio-core.c +4 -34
index a9628ed..258d68e 100644
@@ -250,15 +250,8 @@ int iio_device_get_chrdev_minor(void)
 {
 	int ret, val;
 
-ida_again:
-	if (unlikely(ida_pre_get(&iio_chrdev_ida, GFP_KERNEL) == 0))
-		return -ENOMEM;
-	spin_lock(&iio_ida_lock);
-	ret = ida_get_new(&iio_chrdev_ida, &val);
-	spin_unlock(&iio_ida_lock);
-	if (unlikely(ret == -EAGAIN))
-		goto ida_again;
-	else if (unlikely(ret))
+	ret = ida_get_id(&iio_ida_lock, &iio_chrdev_ida, &val);
+	if (ret < 0)
 		return ret;
 	if (val > IIO_DEV_MAX)
 		return -ENOMEM;
@@ -794,28 +787,6 @@ static void iio_device_unregister_sysfs(struct iio_dev *dev_info)
 		sysfs_remove_group(&dev_info->dev.kobj, dev_info->info->attrs);
 }
 
-/* Return a negative errno on failure */
-int iio_get_new_ida_val(struct ida *this_ida)
-{
-	int ret;
-	int val;
-
-ida_again:
-	if (unlikely(ida_pre_get(this_ida, GFP_KERNEL) == 0))
-		return -ENOMEM;
-
-	spin_lock(&iio_ida_lock);
-	ret = ida_get_new(this_ida, &val);
-	spin_unlock(&iio_ida_lock);
-	if (unlikely(ret == -EAGAIN))
-		goto ida_again;
-	else if (unlikely(ret))
-		return ret;
-
-	return val;
-}
-EXPORT_SYMBOL(iio_get_new_ida_val);
-
 void iio_free_ida_val(struct ida *this_ida, int id)
 {
 	spin_lock(&iio_ida_lock);
@@ -1179,9 +1150,8 @@ int iio_device_register(struct iio_dev *dev_info)
 {
 	int ret;
 
-	dev_info->id = iio_get_new_ida_val(&iio_ida);
-	if (dev_info->id < 0) {
-		ret = dev_info->id;
+	ret = ida_get_id(&iio_ida_lock, &iio_ida, &dev_info->id);
+	if (ret < 0) {
 		dev_err(&dev_info->dev, "Failed to get id\n");
 		goto error_ret;
 	}
drivers/staging/iio/industrialio-trigger.c +13 -52

100 lines not on this page · raw message

include/linux/idr.h +1 -0
index 13a801f..412b46c 100644
@@ -147,5 +147,6 @@ void ida_destroy(struct ida *ida);
 void ida_init(struct ida *ida);
 
 void __init idr_init_cache(void);
+int ida_get_id(spinlock_t *lock, struct ida *ida, int *val);
 
 #endif /* __IDR_H__ */
lib/idr.c +20 -0
index e15502e..d68e222 100644
@@ -939,3 +939,23 @@ void ida_init(struct ida *ida)
 
 }
 EXPORT_SYMBOL(ida_init);
+
+int ida_get_id(spinlock_t *lock, struct ida *ida, int *val)
+{
+	int ret = 0;
+ida_again:
+	if (unlikely(ida_pre_get(ida, GFP_KERNEL) == 0))
+		return -ENOMEM;
+
+	spin_lock(lock);
+	ret = ida_get_new(ida, val);
+	spin_unlock(lock);
+
+	if (unlikely (ret == -EAGAIN))
+		goto ida_again;
+	else if (likely(!ret))
+		*val = *val & MAX_ID_MASK;
+
+	return ret;
+}
+EXPORT_SYMBOL(ida_get_id);
Signature · 1 line
1.7.3.4
#1 lore raw

On Jul 13 Jonathan Cameron wrote at LKML:
[...]

Quoted 11 lines

Taking ida's first, how about the following patch? I'm not at all attached to the form it takes, merely to cutting out on the cut and paste.

Note I've done only a few random places. There aren't that many ida users and I've only played with that for now.

Remaining ida cases:

drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c: (cleans up in case of out of range) drivers/scsi/osd/osd_uld.c: (locks not taken)

[...]

Boaz, Benny,

osd_uld.c::osd_minor_ida is accessed unsafely.
Device probe() and remove() methods are not globally serialized.

Signature · 3 lines
Stefan Richter
-=====-==-== -=== -==-=
http://arcgraph.de/sr/
#2 lore raw
Quoted 11 lines · Stefan Richter

On Jul 13 Jonathan Cameron wrote at LKML:
[...]

Quoted 9 lines

Taking ida's first, how about the following patch? I'm not at all attached to the form it takes, merely to cutting out on the cut and paste.

Note I've done only a few random places. There aren't that many ida users and I've only played with that for now.

Remaining ida cases:

Quoted 8 lines
Quoted 2 lines

drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c: (cleans up in case of out of range) drivers/scsi/osd/osd_uld.c: (locks not taken)

[...]

Boaz, Benny,

osd_uld.c::osd_minor_ida is accessed unsafely.
Device probe() and remove() methods are not globally serialized.

Sorry for the delay. Vacation

Thanks Stefan I'll look into it. I remember I thought about it and tested it at the time, but I might be wrong. I'll look into it.

Thanks
Boaz

#3 lore raw
Quoted 9 lines · On Jul 15 Boaz Harrosh wrote:
Quoted 2 lines · Stefan Richter

osd_uld.c::osd_minor_ida is accessed unsafely.
Device probe() and remove() methods are not globally serialized.

Sorry for the delay. Vacation

Thanks Stefan I'll look into it. I remember I thought about it and tested it at the time, but I might be wrong. I'll look into it.

Could actually be that there was or even still is some degree of serialization by the SCSI core in typical usage. But I think the general case is nowadays fully concurrent. E.g.
echo $name > /sys/bus/scsi/drivers/osd*/{,un}bind
if issued in parallel for different device names. Though it is probably borderline impossible to actually hit a concurrent osd_minor_ida access on purpose.

Signature · 3 lines
Stefan Richter
-=====-==-== -=== -====
http://arcgraph.de/sr/
#4 lore raw
In reply to Jonathan Cameron
Quoted 3 lines · On Jul 13 Jonathan Cameron wrote:

Taking ida's first, how about the following patch? I'm not at all attached to the form it takes, merely to cutting out on the cut and paste.

Not a big-picture opinion here whether this is a good thing; only some small comments on side issues:

[...]

Quoted 2 lines

The other thing this highlights is that I suspect quite a few are protected by spin locks when a mutex would be fine. Hence that might be worth tidying up first.

It seems to be the other way around in this case: Why use a mutex if a spinlock is fine?

[...]

Quoted 14 lines
drivers/misc/cb710/core.c +0 -8
@@ -254,18 +254,9 @@ static int __devinit cb710_probe(struct pci_dev *pdev,
 	if (err)
 		return err;
 
-	do {
-		if (!ida_pre_get(&cb710_ida, GFP_KERNEL))
-			return -ENOMEM;
-
-		spin_lock_irqsave(&cb710_ida_lock, flags);
-		err = ida_get_new(&cb710_ida, &chip->platform_id);
-		spin_unlock_irqrestore(&cb710_ida_lock, flags);
-

drivers/misc/cb710/core.c static int __devinit cb710_probe(struct pci_dev *pdev, · line 262

Quoted 10 lines
drivers/misc/cb710/core.c +3 -4
-		if (err && err != -EAGAIN)
-			return err;
-	} while (err);
-
+	err = ida_get_id(&cb710_ida_lock, &cb710_ida, &chip->platform_id);
+	if (err)
+		return err;
 
 	dev_info(&pdev->dev, "id %d, IO 0x%p, IRQ %d\n",
 		chip->platform_id, chip->iobase, pdev->irq);

To balance this change to cb710_probe, also switch from spin_lock_irqsave/ spin_unlock_irqrestore to spin_lock/spin_unlock in cb710_remove_one for clarity.

[...]

Quoted 16 lines
lib/idr.c +10 -0
@@ -939,3 +939,23 @@ void ida_init(struct ida *ida)
 
 }
 EXPORT_SYMBOL(ida_init);
+
+int ida_get_id(spinlock_t *lock, struct ida *ida, int *val)
+{
+	int ret = 0;
+ida_again:
+	if (unlikely(ida_pre_get(ida, GFP_KERNEL) == 0))
+		return -ENOMEM;
+
+	spin_lock(lock);
+	ret = ida_get_new(ida, val);

lib/idr.c void ida_init(struct ida *ida) · line 961

Quoted 10 lines
lib/idr.c +10 -0
+	spin_unlock(lock);
+
+	if (unlikely (ret == -EAGAIN))
+		goto ida_again;
+	else if (likely(!ret))
+		*val = *val & MAX_ID_MASK;
+
+	return ret;
+}
+EXPORT_SYMBOL(ida_get_id);

A new exported function (in lib/ even) should come with a kerneldoc comment of course. Here it is among else noteworthy that the caller must provide GFP_KERNEL allocations capable context and that @lock cannot be shared with users in IRQ or softIRQ contexts.

Signature · 3 lines
Stefan Richter
-=====-==-== -=== -==-=
http://arcgraph.de/sr/
#5 lore raw
Quoted 38 lines · Stefan Richter
Quoted 3 lines · On Jul 13 Jonathan Cameron wrote:

Taking ida's first, how about the following patch? I'm not at all attached to the form it takes, merely to cutting out on the cut and paste.

Not a big-picture opinion here whether this is a good thing; only some small comments on side issues:

[...]

Quoted 2 lines

The other thing this highlights is that I suspect quite a few are protected by spin locks when a mutex would be fine. Hence that might be worth tidying up first.

It seems to be the other way around in this case: Why use a mutex if a spinlock is fine?

[...]

Quoted 22 lines
drivers/misc/cb710/core.c +3 -12
@@ -254,18 +254,9 @@ static int __devinit cb710_probe(struct pci_dev *pdev,
 	if (err)
 		return err;
 
-	do {
-		if (!ida_pre_get(&cb710_ida, GFP_KERNEL))
-			return -ENOMEM;
-
-		spin_lock_irqsave(&cb710_ida_lock, flags);
-		err = ida_get_new(&cb710_ida, &chip->platform_id);
-		spin_unlock_irqrestore(&cb710_ida_lock, flags);
-
-		if (err && err != -EAGAIN)
-			return err;
-	} while (err);
-
+	err = ida_get_id(&cb710_ida_lock, &cb710_ida, &chip->platform_id);
+	if (err)
+		return err;
 
Quoted 6 lines

drivers/misc/cb710/core.c static int __devinit cb710_probe(struct pci_dev *pdev, · line 262

Quoted 2 lines
drivers/misc/cb710/core.c +0 -0
 	dev_info(&pdev->dev, "id %d, IO 0x%p, IRQ %d\n",
 		chip->platform_id, chip->iobase, pdev->irq);

To balance this change to cb710_probe, also switch from spin_lock_irqsave/ spin_unlock_irqrestore to spin_lock/spin_unlock in cb710_remove_one for clarity.

Good point. I'd actually suggest adding a paired release_id function with similar semantics to the get function just to keep things consistent.

Quoted 25 lines

[...]

Quoted 23 lines
lib/idr.c +17 -0
@@ -939,3 +939,23 @@ void ida_init(struct ida *ida)
 
 }
 EXPORT_SYMBOL(ida_init);
+
+int ida_get_id(spinlock_t *lock, struct ida *ida, int *val)
+{
+	int ret = 0;
+ida_again:
+	if (unlikely(ida_pre_get(ida, GFP_KERNEL) == 0))
+		return -ENOMEM;
+
+	spin_lock(lock);
+	ret = ida_get_new(ida, val);
+	spin_unlock(lock);
+
+	if (unlikely (ret == -EAGAIN))
+		goto ida_again;
+	else if (likely(!ret))
+		*val = *val & MAX_ID_MASK;
+
Quoted 8 lines

lib/idr.c void ida_init(struct ida *ida) · line 961

Quoted 3 lines
lib/idr.c +3 -0
+	return ret;
+}
+EXPORT_SYMBOL(ida_get_id);

A new exported function (in lib/ even) should come with a kerneldoc comment of course. Here it is among else noteworthy that the caller must provide GFP_KERNEL allocations capable context and that @lock cannot be shared with users in IRQ or softIRQ contexts.

Of course, I was just being lazy for an RFC ;) Is suspect any formal submission will need a few iterations before everyone is happy.

#6 lore raw
In reply to Jonathan Cameron

(cc'ing Rusty Russell)
Hello,

Quoted 7 lines · Jonathan Cameron

The other thing this highlights is that I suspect quite a few are protected by spin locks when a mutex would be fine. Hence that might be worth tidying up first.

Anyhow, a cleanup worth making? (obviously the exact form needs some work, but I think the following is enough to start a discussion!)

Subject: [PATCH] ida utility function

Rusty suggested similar addition some weeks ago, so people are really getting annoyed by this.

http://thread.gmane.org/gmane.linux.kernel/1148513/focus=74236

Here's an interface that I think should work,

	int ida_get(struct ida *ida, int begin, int end, gfp_t gfp);

It uses an internal spinlock, returns the allocated ID and @end <= 0 indicates no limit.

Thanks.

Signature · 1 line
tejun
#7 lore raw
Quoted 15 lines · Tejun Heo

(cc'ing Rusty Russell)
Hello,

Quoted 7 lines · Jonathan Cameron

The other thing this highlights is that I suspect quite a few are protected by spin locks when a mutex would be fine. Hence that might be worth tidying up first.

Anyhow, a cleanup worth making? (obviously the exact form needs some work, but I think the following is enough to start a discussion!)

Subject: [PATCH] ida utility function

Rusty suggested similar addition some weeks ago, so people are really getting annoyed by this.

Quoted 8 lines

http://thread.gmane.org/gmane.linux.kernel/1148513/focus=74236

Here's an interface that I think should work,

	int ida_get(struct ida *ida, int begin, int end, gfp_t gfp);

It uses an internal spinlock, returns the allocated ID and @end <= 0 indicates no limit.

Cool.

Rusty, are you pursuing this? Approach looks sensible to me.

#8 lore raw
Quoted 1 line · Jonathan Cameron

Rusty, are you pursuing this? Approach looks sensible to me.

OK, I'll polish up what I had based on Tejun's feedback and repost.

Thanks for the prod...
Rusty.

#9 lore raw
In reply to Jonathan Cameron
Quoted 9 lines · Jonathan Cameron
Quoted 6 lines

Here's an interface that I think should work,

	int ida_get(struct ida *ida, int begin, int end, gfp_t gfp);

It uses an internal spinlock, returns the allocated ID and @end <= 0 indicates no limit.

Cool.

Rusty, are you pursuing this? Approach looks sensible to me.

Yep, here's my latest version. Feel free to run with it, or not.

Thanks!
Rusty.

From: Rusty Russell <[email protected]>
Subject: ida: Simplified functions for id allocation.

The current hyper-optimized functions are overkill if you simply want to allocate an id for a device. Create versions which use an internal lock.

Thanks to Tejun for feedback. Feel free to delete the #ifdef TEST code.

---
 include/linux/idr.h |    4 +
 lib/idr.c           |  116 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 120 insertions(+)
include/linux/idr.h +4 -0
@@ -146,6 +146,10 @@ void ida_remove(struct ida *ida, int id)
 void ida_destroy(struct ida *ida);
 void ida_init(struct ida *ida);
 
+int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
+		   gfp_t gfp_mask);
+void ida_simple_remove(struct ida *ida, unsigned int id);
+
 void __init idr_init_cache(void);
 
 #endif /* __IDR_H__ */
lib/idr.c +116 -0

130 lines not on this page · raw message

Acked-by
#10 lore raw
Quoted 11 lines · Rusty Russell

From: Rusty Russell <[email protected]>
Subject: ida: Simplified functions for id allocation.

The current hyper-optimized functions are overkill if you simply want to allocate an id for a device. Create versions which use an internal lock.

Thanks to Tejun for feedback. Feel free to delete the #ifdef TEST code.

...

Quoted 2 lines

static struct kmem_cache *idr_layer_cache;
+static DEFINE_SPINLOCK(simple_ida);

I think the name is a bit confusing. Maybe simple_ida_lock is better? Other than that,

I guess this is best routed through -mm?

Thanks.

Signature · 1 line
tejun
author Acked-by
#11 lore raw
Quoted 16 lines · Tejun Heo
Quoted 11 lines · Rusty Russell

From: Rusty Russell <[email protected]>
Subject: ida: Simplified functions for id allocation.

The current hyper-optimized functions are overkill if you simply want to allocate an id for a device. Create versions which use an internal lock.

Thanks to Tejun for feedback. Feel free to delete the #ifdef TEST code.

...

Quoted 2 lines

static struct kmem_cache *idr_layer_cache;
+static DEFINE_SPINLOCK(simple_ida);

Quoted 9 lines

I think the name is a bit confusing. Maybe simple_ida_lock is better? Other than that,

I guess this is best routed through -mm?

Thanks.

Thanks Rusty. Just what I was looking for. I'll push out some patches using this once it's picked up.

#12 lore raw
In reply to Tejun Heo
Quoted 11 lines · Tejun Heo
Quoted 10 lines · Rusty Russell

From: Rusty Russell <[email protected]>
Subject: ida: Simplified functions for id allocation.

The current hyper-optimized functions are overkill if you simply want to allocate an id for a device. Create versions which use an internal lock.

Thanks to Tejun for feedback. Feel free to delete the #ifdef TEST code.

Quoted 7 lines
Quoted 1 line

...

Quoted 2 lines

static struct kmem_cache *idr_layer_cache;
+static DEFINE_SPINLOCK(simple_ida);

I think the name is a bit confusing. Maybe simple_ida_lock is better? Other than that,

Ooh, one more thing, maybe it would be better to use spin_lock_irq() to allow calling free under other irq locks.

Thanks.

Signature · 1 line
tejun
#13 lore raw
Quoted 14 lines · Tejun Heo
Quoted 13 lines · Tejun Heo
Quoted 11 lines · Rusty Russell

From: Rusty Russell <[email protected]>
Subject: ida: Simplified functions for id allocation.

The current hyper-optimized functions are overkill if you simply want to allocate an id for a device. Create versions which use an internal lock.

Thanks to Tejun for feedback. Feel free to delete the #ifdef TEST code.

...

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

static struct kmem_cache *idr_layer_cache;
+static DEFINE_SPINLOCK(simple_ida);

I think the name is a bit confusing. Maybe simple_ida_lock is better? Other than that,

Ooh, one more thing, maybe it would be better to use spin_lock_irq() to allow calling free under other irq locks.

Jonathan, please take original patch and mod it to taste, and produce a series on it you can push to Andrew.

As for irqsave etc, as soon as someone needs that we can add it... Jonathan will run into it soon enough.

Thanks,
Rusty.

#14 lore raw
Quoted 24 lines · Rusty Russell
Quoted 14 lines · Tejun Heo
Quoted 13 lines · Tejun Heo
Quoted 11 lines · Rusty Russell

From: Rusty Russell <[email protected]>
Subject: ida: Simplified functions for id allocation.

The current hyper-optimized functions are overkill if you simply want to allocate an id for a device. Create versions which use an internal lock.

Thanks to Tejun for feedback. Feel free to delete the #ifdef TEST code.

...

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

static struct kmem_cache *idr_layer_cache;
+static DEFINE_SPINLOCK(simple_ida);

I think the name is a bit confusing. Maybe simple_ida_lock is better? Other than that,

Ooh, one more thing, maybe it would be better to use spin_lock_irq() to allow calling free under other irq locks.

Quoted 9 lines

Jonathan, please take original patch and mod it to taste, and produce a series on it you can push to Andrew.

As for irqsave etc, as soon as someone needs that we can add it... Jonathan will run into it soon enough.

Thanks,
Rusty.

Given the timing, it's clearly 3.2 merge window material anyway. Too late to push any users this time round even if we hustled it through.

Quite a few 'interesting' users out there now I look into them. Might take a little while to get them past the relevant maintainers.

I've just sent out a few of the simpler cases. I guess Andrew may take the core patch and the others will route through relevant subsystems.

A few more to do. Some of them need a little more thought.

Jonathan