generic gpio -- gpio_chip support
Subsystems (3)
PATCH 1/3 generic gpio -- gpio_chip support
Provide new implementation infrastructure that platforms may choose to use when implementing the GPIO programming interface. Platforms can update their GPIO support to use this. In many cases the incremental cost to access a non-inlined GPIO should be on the order of a dozen instructions, so it won't normally be a problem. The upside is:
* Providing two features which were "want to have (but OK to defer)" when GPIO interfaces were first discussed in November 2006:
- A "struct gpio_chip" to plug in GPIOs that aren't directly supported
by SOC platforms, but come from FPGAs or other multifunction devices using conventional device registers (like UCB-1x00 or SM501 GPIOs, and southbridges in PCs with more open specs than usual).
- Full support for message-based GPIO expanders, where registers are
accessed through sleeping I/O calls. Previous support for these "cansleep" calls was just stubs. (One example: the widely used pcf8574 I2C chips, with 8 GPIOs each.)
* Including a non-stub implementation of the gpio_{request,free}() calls, making those calls much more useful. The diagnostic labels are also recorded given DEBUG_FS, so /sys/kernel/debug/gpio can show a snapshot of all GPIOs known to this infrastructure.
The driver programming interfaces introduced in 2.6.21 do not change at all; this infrastructure is entirely below those covers.
This opens the door to an augmented programming interface, addressing GPIOs by chip and index. That could be used as a performance tweak (lookup once then cache, avoiding locking and lookup overheads) or to support transient GPIOs not registered in the integer GPIO namespace (maybe a USB-to-GPIO adapter, or GPIOs coupled to some other type of add-on card).
--- Documentation/gpio.txt | 15 - include/asm-generic/gpio.h | 127 ++++++++++ lib/Kconfig | 6 lib/Kconfig.debug | 9 lib/Makefile | 2 lib/gpiolib.c | 538 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 693 insertions(+), 4 deletions(-)
Documentation/gpio.txt +11 -4
@@ -63,7 +63,9 @@ and that can be critical for glue logic. Plus, this doesn't define an implementation framework, just an interface. One platform might implement it as simple inline functions accessing chip registers; another might implement it by delegating through abstractions -used for several very different kinds of GPIO controller. +used for several very different kinds of GPIO controller. (There is some +library code supporting such an implementation strategy, but drivers acting +as clients to the GPIO interface should not care how it's implemented.) That said, if the convention is supported on their platform, drivers should use it when possible. Platforms should declare GENERIC_GPIO support in
@@ -133,6 +135,7 @@ Spinlock-Safe GPIO access ------------------------- Most GPIO controllers can be accessed with memory read/write instructions. That doesn't need to sleep, and can safely be done from inside IRQ handlers. +(That includes hardirq contexts on RT kernels.) Use these calls to access such GPIOs:
@@ -223,6 +226,9 @@ Note that requesting a GPIO does NOT cau way; it just marks that GPIO as in use. Separate code must handle any pin setup (e.g. controlling which pin the GPIO uses, pullup/pulldown). +Also note that it's your responsibility to have stopped using a GPIO +before you free it. + GPIOs mapped to IRQs --------------------
@@ -238,7 +244,7 @@ map between them using calls like: Those return either the corresponding number in the other namespace, or else a negative errno code if the mapping can't be done. (For example, -some GPIOs can't used as IRQs.) It is an unchecked error to use a GPIO +some GPIOs can't be used as IRQs.) It is an unchecked error to use a GPIO number that wasn't set up as an input using gpio_direction_input(), or to use an IRQ number that didn't originally come from gpio_to_irq().
@@ -299,6 +305,7 @@ Related to multiplexing is configuration pulldowns integrated on some platforms. Not all platforms support them, or support them in the same way; and any given board might use external pullups (or pulldowns) so that the on-chip ones should not be used. +(When a circuit needs 5 kOhm, on-chip 100 kOhm resistors won't do.) There are other system-specific mechanisms that are not specified here, like the aforementioned options for input de-glitching and wire-OR output.
@@ -308,8 +315,8 @@ commonly grouped in banks of 16 or 32, w banks.) Some systems can trigger IRQs from output GPIOs. Code relying on such mechanisms will necessarily be nonportable. -Dynamic definition of GPIOs is not currently supported; for example, as +Dynamic definition of GPIOs is not currently standard; for example, as a side effect of configuring an add-on board with some GPIO expanders. These calls are purely for kernel space, but a userspace API could be built -on top of it. +on top of them.
include/asm-generic/gpio.h +127 -0
137 lines not on this page · raw message
lib/Kconfig +6 -0
@@ -141,4 +141,10 @@ config HAS_DMA config CHECK_SIGNATURE bool +# +# gpiolib is selected if needed +# +config GPIO_LIB + boolean + endmenu
lib/Kconfig.debug +9 -0
@@ -156,6 +156,15 @@ config DEBUG_SLAB allocation as well as poisoning memory on free to catch use of freed memory. This can make kmalloc/kfree-intensive workloads much slower. +config DEBUG_GPIO + bool "Debug GPIO calls" + depends on DEBUG_KERNEL && GPIO_LIB + help + Say Y here to add some extra checks to GPIO calls, ensuring that + GPIOs have been properly initialized before they are used and + that sleeping calls aren't made from nonsleeping contexts. This + can make bitbanged serial protocols slower. + config DEBUG_SLAB_LEAK bool "Memory leak debugging" depends on DEBUG_SLAB
lib/Makefile +2 -0
@@ -68,6 +68,8 @@ obj-$(CONFIG_FAULT_INJECTION) += fault-i lib-$(CONFIG_GENERIC_BUG) += bug.o +lib-$(CONFIG_GPIO_LIB) += gpiolib.o + hostprogs-y := gen_crc32table clean-files := crc32table.h
lib/gpiolib.c new file +538 -0
538 lines not on this page · raw message
Andrew Morton
maintainerQuoted 35 lines · On Fri, 9 Nov 2007 11:36:19 -0800 David Brownell <[email protected]> wrote:
Provide new implementation infrastructure that platforms may choose to use when implementing the GPIO programming interface. Platforms can update their GPIO support to use this. In many cases the incremental cost to access a non-inlined GPIO should be on the order of a dozen instructions, so it won't normally be a problem. The upside is:
* Providing two features which were "want to have (but OK to defer)" when GPIO interfaces were first discussed in November 2006:
- A "struct
gpio_chip" to plug in GPIOs that aren't directly supported
by SOC platforms, but come from FPGAs or other multifunction devices using conventional device registers (like UCB-1x00 or SM501 GPIOs, and southbridges in PCs with more open specs than usual).- Full support for message-based GPIO expanders, where registers are
accessed through sleeping I/O calls. Previous support for these "cansleep" calls was just stubs. (One example: the widely used pcf8574 I2C chips, with 8 GPIOs each.)* Including a non-stub implementation of the gpio_{request,free}() calls, making those calls much more useful. The diagnostic labels are also recorded given
DEBUG_FS, so/sys/kernel/debug/gpiocan show a snapshot of all GPIOs known to this infrastructure.The driver programming interfaces introduced in 2.6.21 do not change at all; this infrastructure is entirely below those covers.
This opens the door to an augmented programming interface, addressing GPIOs by chip and index. That could be used as a performance tweak (lookup once then cache, avoiding locking and lookup overheads) or to support transient GPIOs not registered in the integer GPIO namespace (maybe a USB-to-GPIO adapter, or GPIOs coupled to some other type of add-on card).
...
lib/gpiolib.c line 36
Quoted 7 lines
lib/gpiolib.c new file +7
-0+ +/* gpio_lock protects the table of chips and to gpio_chip->requested. +* While any gpio is requested, its gpio_chip is not removable. It's +* a raw spinlock to ensure safe access from hardirq contexts, and to +* shrink bitbang overhead: per-bit preemption would be very wrong. +*/ +static raw_spinlock_t gpio_lock = __RAW_SPIN_LOCK_UNLOCKED;
Well that's weird.
For starters, this initialisation will confound lockdep: it should use DEFINE_SPINLOCK.
And the rationale seems dubious. All you're saving here is a couple of accesses to task_struct at spin_unlock()-time. If the current task has a preemption pending then yes, we'll schedule away but that's a very rare thing and that's just what we're supposed to do.
So please tell us more about this. Perhaps there are performance problems with the current core preemption machinery.
lib/gpiolib.c line 101
Quoted 8 lines
lib/gpiolib.c new file +6
-0+local_irq_save (flags); +__raw_spin_lock (&gpio_lock); +__raw_spin_unlock (&gpio_lock); +local_irq_restore (flags); +return status; +}
And of course if this code is converted to conventional locking, the above becomes spin_lock_irqsave()/spin_lock_irqrestore() in many places.
Quoted 1 line
+/* There's no value in inlining GPIO calls that may sleep.
There's no value in inlining anything, hardly ;)
Quoted 1 line
+postcore_initcall(gpiolib_debugfs_init);
postcore_initcall() is unusual, hence a comment describing why it was employed would be a good thing to have.
Quoted 16 lines · Andrew Morton
lib/gpiolib.c line 36
Quoted 11 lines · On Fri, 9 Nov 2007 11:36:19 -0800 David Brownell <[email protected]> wrote:
Provide new implementation infrastructure that platforms may choose to use when implementing the GPIO programming interface. ...
...
lib/gpiolib.c new file +6
-0+/* gpio_lock protects the table of chips and to gpio_chip->requested. +* While any gpio is requested, its gpio_chip is not removable. It's +* a raw spinlock to ensure safe access from hardirq contexts, and to +* shrink bitbang overhead: per-bit preemption would be very wrong. +*/ +static raw_spinlock_t gpio_lock = __RAW_SPIN_LOCK_UNLOCKED;Well that's weird.
Quoted 2 lines
For starters, this initialisation will confound lockdep: it should use
DEFINE_SPINLOCK.
Then it wouldn't be "raw"! It seems info on raw spinlocks is out of sync with current code. Allegedly it should be possible to just pass a raw_spinlock_t pointer to spin_lock_irqsave() and friends and have GCC sort out the right stuff ... but that didn't work.
I speculate that either the design has changed (without fanfare), or else that stuff is in RT kernels and has not yet gone upstream.
Quoted 4 lines
And the rationale seems dubious. All you're saving here is a couple of accesses to
task_structat spin_unlock()-time. If the current task has a preemption pending then yes, we'll schedule away but that's a very rare thing and that's just what we're supposed to do.
Unfortunately, that's not what I observed/measured.
http://marc.info/?l=linux-kernel&m=119429680220361&w=2
Plus, note the comment about hardirq context and RT environments. When that spinlock is really a mutex, and the non-hardirq contexts are tasks, non-raw spinlocks would wrongly prevent access to GPIOs from true hardirq contexts.
Quoted 2 lines
So please tell us more about this. Perhaps there are performance problems with the current core preemption machinery.
The above email summarizes significant slowdown I observed using just i2c-gpio, which happened to be easy to measure down to the microsecond level.
Quoted 11 lines
lib/gpiolib.c line 101
Quoted 8 lines
lib/gpiolib.c new file +6
-0+local_irq_save (flags); +__raw_spin_lock (&gpio_lock); +__raw_spin_unlock (&gpio_lock); +local_irq_restore (flags); +return status; +}And of course if this code is converted to conventional locking, the above becomes spin_lock_irqsave()/spin_lock_irqrestore() in many places.
See above. Allegedly we ought to be able to use those calls even with raw spinlocks ... but that didn't work when I tried it. I'd certainly prefer if it did work.
Quoted 3 lines
Quoted 1 line
+/* There's no value in inlining GPIO calls that may sleep.
There's no value in inlining anything, hardly ;)
Remember that GPIOs do get used for bitbanging, and there's lots of kernel code to make bit ops stay fast!
The canonical reason to inline GPIO operations is so that when you bitbang some serial protocol, you're closer to one instruction per bit toggle than fifty ... at one point I measured a factor of nearly three speedup for byte-level bitbanging for SPI (ca. 70 Kbit with quick function calls, vs about 2 Mbits inlined, ISTR) ... with most of the overhead being to pass the next byte down to the bitbang loop. Using a bigger wordsize saw even more improvement.
Quoted 4 lines
Quoted 1 line
+postcore_initcall(gpiolib_debugfs_init);
postcore_initcall()is unusual, hence a comment describing why it was employed would be a good thing to have.
Actually that one could easily be later, fs_initcall or whatnot. I think that's debris from other versions.
- Dave
Andrew Morton
maintainerQuoted 20 lines · On Mon, 12 Nov 2007 14:32:53 -0800 David Brownell <[email protected]> wrote:
Quoted 16 lines · Andrew Morton
lib/gpiolib.c line 36
Quoted 11 lines · On Fri, 9 Nov 2007 11:36:19 -0800 David Brownell <[email protected]> wrote:
Provide new implementation infrastructure that platforms may choose to use when implementing the GPIO programming interface. ...
...
lib/gpiolib.c new file +6
-0+/* gpio_lock protects the table of chips and to gpio_chip->requested. +* While any gpio is requested, its gpio_chip is not removable. It's +* a raw spinlock to ensure safe access from hardirq contexts, and to +* shrink bitbang overhead: per-bit preemption would be very wrong. +*/ +static raw_spinlock_t gpio_lock = __RAW_SPIN_LOCK_UNLOCKED;Well that's weird.
Quoted 2 lines
For starters, this initialisation will confound lockdep: it should use
DEFINE_SPINLOCK.
Quoted 7 lines
Then it wouldn't be "raw"! It seems info on raw spinlocks is out of sync with current code. Allegedly it should be possible to just pass a
raw_spinlock_tpointer tospin_lock_irqsave()and friends and have GCC sort out the right stuff ... but that didn't work.I speculate that either the design has changed (without fanfare), or else that stuff is in RT kernels and has not yet gone upstream.
Well whatever. We shouldn't have to resort to caller-side party tricks like this to get acceptable performance.
Quoted 9 lines
Quoted 5 lines
And the rationale seems dubious. All you're saving here is a couple of accesses to
task_structat spin_unlock()-time. If the current task has a preemption pending then yes, we'll schedule away but that's a very rare thing and that's just what we're supposed to do.Unfortunately, that's not what I observed/measured.
There's something missing here. It went from 6.4 usec/bit up to 11.2 usec/bit. You seem to be saying that enabling preemption accounts for 1 usec. What caused the rest of the slowdown?
Quoted 4 lines
Plus, note the comment about hardirq context and RT environments. When that spinlock is really a mutex, and the non-hardirq contexts are tasks, non-raw spinlocks would wrongly prevent access to GPIOs from true hardirq contexts.
I don't understand all this about raw spinlocks and hardirq context. We use plain old spin_lock()/spin_unlock() in hardirq context all the time?
I'm still trying to understand what you've observed here. Is it the case that a single gpio operation went from 6.4 up to 11.2 usecs? And that this operation does a single spin_lock/spin_unlock? If so, something would have had to have gone seriously wrong for a spin_lock/unlock time to increase by 4.8 microseconds.
I assume these timings are from a reasonably fast machine?
Quoted 21 lines
Quoted 3 lines
So please tell us more about this. Perhaps there are performance problems with the current core preemption machinery.
The above email summarizes significant slowdown I observed using just i2c-gpio, which happened to be easy to measure down to the microsecond level.
Quoted 11 lines
lib/gpiolib.c line 101
Quoted 8 lines
lib/gpiolib.c new file +6
-0+local_irq_save (flags); +__raw_spin_lock (&gpio_lock); +__raw_spin_unlock (&gpio_lock); +local_irq_restore (flags); +return status; +}And of course if this code is converted to conventional locking, the above becomes spin_lock_irqsave()/spin_lock_irqrestore() in many places.
Quoted 3 lines
See above. Allegedly we ought to be able to use those calls even with raw spinlocks ... but that didn't work when I tried it. I'd certainly prefer if it did work.
It would of course be better to not use raw spinlocks at all. It's an internal implementation detail and drivers shouldn't go poking at it:
box:/usr/src/linux-2.6.24-rc2> grep -rl raw_spinlock_t . ./arch/x86/kernel/traps_32.c ./arch/x86/kernel/traps_64.c ./arch/x86/kernel/tsc_sync.c ./arch/mips/kernel/gdb-stub.c ./arch/parisc/lib/bitops.c ./arch/powerpc/lib/locks.c ./arch/s390/lib/spinlock.c ./include/asm-alpha/spinlock.h ./include/asm-alpha/spinlock_types.h ./include/asm-arm/spinlock.h ./include/asm-arm/spinlock_types.h ./include/asm-generic/bitops/atomic.h ./include/asm-x86/spinlock_32.h ./include/asm-x86/spinlock_64.h ./include/asm-x86/spinlock_types.h ./include/asm-ia64/spinlock.h ./include/asm-ia64/spinlock_types.h ./include/asm-m32r/spinlock.h ./include/asm-m32r/spinlock_types.h ./include/asm-mips/spinlock.h ./include/asm-mips/spinlock_types.h ./include/asm-parisc/atomic.h ./include/asm-parisc/spinlock.h ./include/asm-parisc/spinlock_types.h ./include/asm-powerpc/spinlock.h ./include/asm-powerpc/spinlock_types.h ./include/asm-ppc/spinlock.h ./include/asm-s390/spinlock.h ./include/asm-s390/spinlock_types.h ./include/asm-sh/spinlock.h ./include/asm-sh/spinlock_types.h ./include/asm-sparc/spinlock.h ./include/asm-sparc/spinlock_types.h ./include/asm-sparc64/spinlock.h ./include/asm-sparc64/spinlock_types.h ./include/linux/spinlock.h ./include/linux/spinlock_types.h ./include/linux/spinlock_types_up.h ./include/linux/spinlock_up.h ./kernel/lockdep.c./lib/spinlock_debug.c
So can we please drill down and work out what went wrong and see if we can fix it all up properly?
Quoted 26 lines
Quoted 4 lines
Quoted 2 lines
+/* There's no value in inlining GPIO calls that may sleep.
There's no value in inlining anything, hardly ;)
Remember that GPIOs do get used for bitbanging, and there's lots of kernel code to make bit ops stay fast!
The canonical reason to inline GPIO operations is so that when you bitbang some serial protocol, you're closer to one instruction per bit toggle than fifty ... at one point I measured a factor of nearly three speedup for byte-level bitbanging for SPI (ca. 70 Kbit with quick function calls, vs about 2 Mbits inlined, ISTR) ... with most of the overhead being to pass the next byte down to the bitbang loop. Using a bigger wordsize saw even more improvement.
Quoted 4 lines
Quoted 1 line
+postcore_initcall(gpiolib_debugfs_init);
postcore_initcall()is unusual, hence a comment describing why it was employed would be a good thing to have.Actually that one could easily be later,
fs_initcallor whatnot. I think that's debris from other versions.- Dave
Quoted 10 lines
Quoted 7 lines
It seems info on raw spinlocks is out
of sync with current code. Allegedly it should be possible to just pass araw_spinlock_tpointer tospin_lock_irqsave()and friends and have GCC sort out the right stuff ... but that didn't work.I speculate that either the design has changed (without fanfare), or else that stuff is in RT kernels and has not yet gone upstream.
Well whatever. We shouldn't have to resort to caller-side party tricks like this to get acceptable performance.
I'd be happy if, as originally presented, it were possible to just pass a raw_spinlock_t to spin_lock_irqsave() and friends.
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
And the rationale seems dubious. All you're saving here is a couple of accesses to
task_structat spin_unlock()-time. If the current task has a preemption pending then yes, we'll schedule away but that's a very rare thing and that's just what we're supposed to do.
I don't think we really want to be scheduling on bit ops; that seems a bit more fundamental.
Quoted 8 lines
Quoted 4 lines
Unfortunately, that's not what I observed/measured.
There's something missing here. It went from 6.4 usec/bit up to 11.2 usec/bit. You seem to be saying that enabling preemption accounts for 1 usec. What caused the rest of the slowdown?
It was caused by some debug options I run with all the time, which don't otherwise have easily observed performance effects.
DEBUG_PREEMPT, DEBUG_SPINLOCK, and DEBUG_SPINLOCK_SLEEP, but not LOCKDEP (which I run with all the time, on platforms where it's available). There were other options too, which didn't matter. This was with a basically idle system, note ... there was no preemption happening.
Quoted 7 lines
Quoted 4 lines
Plus, note the comment about hardirq context and RT environments. When that spinlock is really a mutex, and the non-hardirq contexts are tasks, non-raw spinlocks would wrongly prevent access to GPIOs from true hardirq contexts.
I don't understand all this about raw spinlocks and hardirq context. We use plain old spin_lock()/spin_unlock() in hardirq context all the time?
Think RT kernels, with threaded IRQs and spinlocks morphed to mutexes.
Only irq handlers marked IRQF_NODELAY|IRQF_DISABLED will always be "hard irqs" [1] which run the way you describe ... i.e. the way the current mainstream kernel always works for IRQ handlers.
Quoted 2 lines
I'm still trying to understand what you've observed here. Is it the case that a single gpio operation went from 6.4 up to 11.2 usecs?
That was a single bitbanged I2C bit transfer, with embedded udelay()s. I believe that was four gpio operations, as summarized at the end of that email above. Enabling preempt + debug increased the cost of each GPIO call from whatever it was (reasonable) by 1.2 usecs.
Quoted 4 lines
And that this
operation does a single spin_lock/spin_unlock? If so, something would have had to have gone seriously wrong for a spin_lock/unlock time to increase by 4.8 microseconds.
See above; that was probably four lock/unlocks; 1.2 usec a pair.
But per-bit preemption for GPIOs would in any case be Just Wrong; much like making test_bit(), set_bit(), and clear_bit() become preemption points would be wrong.
Quoted 1 line
I assume these timings are from a reasonably fast machine?
They were from a relatively normal speed embedded processor; exactly the kind which tends to both use these operations, and not uncommonly also involve bitbanging. In this case it was an AVR32 devel board, BogoMips about 250 (comparable to ARM5 ones with BogoMips around 90).
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
lib/gpiolib.c line 101
Quoted 8 lines
lib/gpiolib.c new file +6
-0+local_irq_save (flags); +__raw_spin_lock (&gpio_lock); +__raw_spin_unlock (&gpio_lock); +local_irq_restore (flags); +return status; +}And of course if this code is converted to conventional locking, the above becomes spin_lock_irqsave()/spin_lock_irqrestore() in many places.
Quoted 6 lines
Quoted 3 lines
See above. Allegedly we ought to be able to use those calls even with raw spinlocks ... but that didn't work when I tried it. I'd certainly prefer if it did work.
It would of course be better to not use raw spinlocks at all. It's an internal implementation detail and drivers shouldn't go poking at it:
They weren't originally supposed to be like that. They were to be reserved for cases where their behavior was necessary ... like, to synch with IRQF_NODELAY ("hard irq") handlers, and in other cases where preemption would be wrong.
Quoted 2 lines
So can we please drill down and work out what went wrong and see if we can fix it all up properly?
That's what I thought I did. As I summarized it in the message with the URL given above:
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Fortuntely it turns out those problems all go away if the gpiolib code uses a *raw* spinlock to guard its table lookups...
That's as it should be, since the only substantive changes were to grab and release a lock, do one table lookup a bit differently, and add one indirect function call ... changes which should not have any visible performance impact on per-bit codepaths, and one might expect to cost on the order of one dozen instructions.
At most, I think one counter-argument would be that the costs of that preempt (and debug) code aren't "enough" to merit using raw spinlocks; a judgement call.
But that would have ignored the fact that such bit operations shouldn't really be preemption points. Only the "cansleep" versions should be; they need to work through e.g. an I2C or SPI gpio expanders.
- Dave
Quoted 8 lines · David Brownell
Quoted 5 lines
Quoted 2 lines
I speculate that either the design has changed (without fanfare), or else that stuff is in RT kernels and has not yet gone upstream.
Well whatever. We shouldn't have to resort to caller-side party tricks like this to get acceptable performance.
I'd be happy if, as originally presented, it were possible to just pass a
raw_spinlock_ttospin_lock_irqsave()and friends.
that's a spinlock type abstraction of PREEMPT_RT, not of mainline. In mainline there's basically almost never any valid reason to use a raw spinlock - please use spinlock_t instead. Why do you want to use raw_spinlock_t?
Ingo
Quoted 6 lines · Ingo Molnar
Quoted 3 lines · David Brownell
Quoted 3 lines
Quoted 2 lines
I speculate that either the design has changed (without fanfare), or else that stuff is in RT kernels and has not yet gone upstream.
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
Well whatever. We shouldn't have to resort to caller-side party tricks like this to get acceptable performance.
I'd be happy if, as originally presented, it were possible to just pass a
raw_spinlock_ttospin_lock_irqsave()and friends.that's a spinlock type abstraction of
PREEMPT_RT, not of mainline.
Any reason that stuff shouldn't move into mainline?
Quoted 1 line
Why do you want to use
raw_spinlock_t?
Already answered elsewhere in this thread ... I'll highlight the point that such bitops shouldn't be preemption points.
Andrew Morton
maintainerQuoted 13 lines · On Tue, 13 Nov 2007 11:22:45 -0800 David Brownell <[email protected]> wrote:
Quoted 12 lines · Ingo Molnar
Quoted 8 lines · David Brownell
Quoted 5 lines
Quoted 2 lines
I speculate that either the design has changed (without fanfare), or else that stuff is in RT kernels and has not yet gone upstream.
Well whatever. We shouldn't have to resort to caller-side party tricks like this to get acceptable performance.
I'd be happy if, as originally presented, it were possible to just pass a
raw_spinlock_ttospin_lock_irqsave()and friends.
Quoted 8 lines
Quoted 1 line
that's a spinlock type abstraction of
PREEMPT_RT, not of mainline.Any reason that stuff shouldn't move into mainline?
Quoted 1 line
Why do you want to use
raw_spinlock_t?Already answered elsewhere in this thread ...
Can't say I really understood the answer. I don't think we actually know where all of this extra time is being spent?
Quoted 2 lines
I'll highlight the
point that such bitops shouldn't be preemption points.
Disagree. *everything* should be a preemption point. For internal-implementation details we do need to disable preemtion sometimes (to prevent deadlocks and to protect per-cpu resources). But those preemption-off periods should be minimised.
Quoted 7 lines · Andrew Morton
Quoted 4 lines
Quoted 2 lines
Why do you want to use
raw_spinlock_t?Already answered elsewhere in this thread ...
Can't say I really understood the answer. I don't think we actually know where all of this extra time is being spent?
Reading that, one could think performance was the only factor. But it isn't, and wasn't the one which really persuaded me.
And yes we do where that time went. Although it seems that repeating that info once again won't help...
Quoted 4 lines
Quoted 2 lines
I'll highlight the
point that such bitops shouldn't be preemption points.Disagree. *everything* should be a preemption point.
So it's wrong that <asm-generic/bitops/atomic.h> uses the same calls to prevent *those* bitops from being prempted?
Thus, that code should switch over to normal spinlocks...
I believe that if I submitted patches to do that, there'd be a not-so-small riot. And the arguments would all boil down to much the same ones applying to *these* bitops...
Quoted 3 lines
For internal-implementation
details we do need to disable preemtion sometimes (to prevent deadlocks and to protect per-cpu resources).
You're certainly talking about "internal-implementation details" in this case. It's not like the lock is used outside of those routines. Or as if other implementations would even *need* such a lock. (Just like the IRQ table, if the entries can't be removed and are all set up very early, locking would be pointless.)
Quoted 2 lines
But those
preemption-off periods should be minimised.
Like the three instructions in the "hot paths" for getting and setting GPIO values, when using raw spinlocks ... check.
- Dave
Quoted 9 lines · David Brownell
Quoted 4 lines · Andrew Morton
Quoted 2 lines
I'll highlight the
point that such bitops shouldn't be preemption points.Disagree. *everything* should be a preemption point.
So it's wrong that <
asm-generic/bitops/atomic.h> uses the same calls to prevent *those* bitops from being prempted?
Upstream, all spinlocks prevent preemption. But these ones are raw locks rather than normal locks probably because that they are trivially an innermost and correct lock. It's not going to be very helpful to bloat it and slow it down when debugging is turned on (atomic operations are _very_ frequent).
For -rt, who knows. They probably don't even run on parisc or sparc so don't really care at this point.
Quoted 1 line
Thus, that code should switch over to normal spinlocks...
For upstream, there is little reason to switch them over, and some reasons not to.
Quoted 3 lines
I believe that if I submitted patches to do that, there'd be a not-so-small riot. And the arguments would all boil down to much the same ones applying to *these* bitops...
I don't think you have valid reasons. Also, core/arch code has some different considerations to driver code.
Quoted 10 lines
Quoted 3 lines
For internal-implementation
details we do need to disable preemtion sometimes (to prevent deadlocks and to protect per-cpu resources).You're certainly talking about "internal-implementation details" in this case. It's not like the lock is used outside of those routines. Or as if other implementations would even *need* such a lock. (Just like the IRQ table, if the entries can't be removed and are all set up very early, locking would be pointless.)
Internal implementation details, as in: spin lock code has to disable preemption otherwise they deadlock; get_cpu_var() has to disable preemption to give coherent access to the variable etc.
Quoted 6 lines · Nick Piggin
Quoted 5 lines · David Brownell
Quoted 3 lines · Andrew Morton
Quoted 2 lines
I'll highlight the
point that such bitops shouldn't be preemption points.
Quoted 6 lines
Quoted 4 lines
Quoted 1 line
Disagree. *everything* should be a preemption point.
So it's wrong that <
asm-generic/bitops/atomic.h> uses the same calls to prevent *those* bitops from being prempted?Upstream, all spinlocks prevent preemption.
I chose my wording carefully though. A preemption point is more than just a small region where preemption isn't allowed.
It's one of those where preemption is *INVITED* ...
Now, in the RT case, I believe the rationale for inviting preemption when dropping a lock is largely related to the way priority inversion is handled. When lock contention can block higher priority activities, dropping the lock must be able to trigger the relevant activity switch.
... and the raw spinlocks don't support that machinery, while "normal" spinlocks become inversion-aware mutexes.
Quoted 3 lines
But these ones
are raw locks rather than normal locks probably because that they are trivially an innermost and correct lock.
As in the $SUBJECT case, I'd say.
Although another point is related to "trivial": the data is being protected through an operation too trivial to be worth paying for any of that priority logic.
Quoted 8 lines · David Brownell
Quoted 1 line · Nick Piggin
Upstream, all spinlocks prevent preemption.
I chose my wording carefully though. A preemption point is more than just a small region where preemption isn't allowed.
It's one of those where preemption is *INVITED* ...
With CONFIG_PREEMPT upstream, that's exactly the same (unless you're considering preempt breaking points, which you don't seem t obe).
Quoted 5 lines
Now, in the RT case, I believe the rationale for inviting preemption when dropping a lock is largely related to the way priority inversion is handled. When lock contention can block higher priority activities, dropping the lock must be able to trigger the relevant activity switch.
There is no specific inviting of preemption. The locks are preemptible -- they can be preempted even while being *held*
Quoted 7 lines
... and the raw spinlocks don't support that machinery, while "normal" spinlocks become inversion-aware mutexes.
Quoted 3 lines
But these ones
are raw locks rather than normal locks probably because that they are trivially an innermost and correct lock.
Quoted 5 lines
As in the $SUBJECT case, I'd say.
Although another point is related to "trivial": the data is being protected through an operation too trivial to be worth paying for any of that priority logic.
A driver shouldn't get to decide that, IMO. And if there is some policy in the -rt tree allowing these decisions, then it's exactly the kind of thing we don't want upsream.
Quoted 11 lines · Nick Piggin
Quoted 9 lines
Quoted 3 lines
But these ones
are raw locks rather than normal locks probably because that they are trivially an innermost and correct lock.As in the $SUBJECT case, I'd say.
Although another point is related to "trivial": the data is being protected through an operation too trivial to be worth paying for any of that priority logic.
A driver shouldn't get to decide that, IMO.
Not that I was talking about driver code...
Quoted 3 lines
And if there is
some policy in the -rt tree allowing these decisions, then it's exactly the kind of thing we don't want upsream.
Making raw spinlocks available allows those decisions...
On the other hand, I can't see things working sanely without them being available. The problem seems to be the usual one that crops up whenever anyone tries to create a "bright line" decision algorithm in areas that need flexibility. Any "bright line" rule will lead to wrong results.
- Dave
Quoted 3 lines · On Wed, 14 Nov 2007 00:37:57 -0800 David Brownell <[email protected]> wrote:
Although another point is related to "trivial": the data is being protected through an operation too trivial to be worth paying for any of that priority logic.
But isn't there any way we can remove the lock from the fast path altogether? What is it really protecting?
Since this is the code that runs under the lock (excluding the "extra checks" case):
+static inline struct gpio_chip *gpio_to_chip(unsigned gpio) +{
+ return chips[gpio / ARCH_GPIOS_PER_CHIP];
+}
I'd say it protects against chips being removed in the middle of the operation. However, this comment says that chips cannot be removed while any gpio on it is requested:
+/* gpio_lock protects the table of chips and to gpio_chip->requested.
+ * While any gpio is requested, its gpio_chip is not removable. It's
+ * a raw spinlock to ensure safe access from hardirq contexts, and to
+ * shrink bitbang overhead: per-bit preemption would be very wrong.
+ */
And since we drop the lock before calling the actual get/set bit operation, we would be screwed anyway if the chip was removed during the call to __gpio_set_value(). So what does the lock really buy us?
Håvard
Quoted 9 lines · Haavard Skinnemoen
Quoted 3 lines · On Wed, 14 Nov 2007 00:37:57 -0800 David Brownell <[email protected]> wrote:
Although another point is related to "trivial": the data is being protected through an operation too trivial to be worth paying for any of that priority logic.
But isn't there any way we can remove the lock from the fast path altogether? What is it really protecting?
The integrity of the table. Entries can be added and removed (both operations being *RARE* which is good!) at any time.
Quoted 1 line
Since this is the code that runs under the lock
No, there's more than that. This is what runs under it in the hot paths, yes, but the gpio request/free paths do more work than this. (That includes direction setting, since that can be an implicit request.)
Quoted 17 lines
(excluding the "extra checks" case):
(quoted diff) +4
-0+static inline struct gpio_chip *gpio_to_chip(unsigned gpio) +{ + return chips[gpio / ARCH_GPIOS_PER_CHIP]; +}I'd say it protects against chips being removed in the middle of the operation. However, this comment says that chips cannot be removed while any gpio on it is requested:
+/*
gpio_lockprotects the table of chips and togpio_chip->requested.
+ * While any gpio is requested, itsgpio_chipis not removable. It's
+ * a raw spinlock to ensure safe access from hardirq contexts, and to
+ * shrink bitbang overhead: per-bit preemption would be very wrong.
+ */
Quoted 3 lines
And since we drop the lock before calling the actual get/set bit operation, we would be screwed anyway if the chip was removed during the call to
__gpio_set_value(). So what does the lock really buy us?
The get/set bit calls are the hot paths. Locking on those paths buys us a consistent locking policy, which is obviously correct. It's consistent with the request/free paths.
But I think what you're suggesting is that the "requested" flag is effectively a long-term lock, so grabbing the spinlock on those paths is not necessary. Right?
Hmm ... that makes some sense. I hadn't started out thinking of that "requested" flag as a lock bit, but in fact that's what it ended up becoming.
Removing the spinlock from those paths -- at least in the "no extra checks case" -- would let us avoid all this flamage about whether raw spinlocks are ever OK.
I think I forsee a patch coming...
- Dave
Quoted 6 lines · On Wed, 14 Nov 2007 22:50:17 -0800 David Brownell <[email protected]> wrote:
Quoted 1 line
Since this is the code that runs under the lock
No, there's more than that. This is what runs under it in the hot paths, yes, but the gpio request/free paths do more work than this. (That includes direction setting, since that can be an implicit request.)
Yeah, I was talking about the hot paths. That's the only place where raw vs. non-raw performance matters.
Quoted 7 lines
The get/set bit calls are the hot paths. Locking on those paths buys us a consistent locking policy, which is obviously correct. It's consistent with the request/free paths.
But I think what you're suggesting is that the "requested" flag is effectively a long-term lock, so grabbing the spinlock on those paths is not necessary. Right?
Exactly. If we add two (quite reasonable) restrictions:
* The GPIO framework must ensure that GPIO chips cannot be removed when one or more pins have been assigned to a client.
* The client must ensure that it never calls gpio_free() simultaneously with gpio_[sg]et_value(), adding locking of its own if necessary.
this should be safe.
Håvard
Quoted 5 lines · David Brownell
Quoted 4 lines · Ingo Molnar
Quoted 3 lines · David Brownell
Quoted 3 lines
Quoted 2 lines
I speculate that either the design has changed (without fanfare), or else that stuff is in RT kernels and has not yet gone upstream.
Quoted 7 lines
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
Well whatever. We shouldn't have to resort to caller-side party tricks like this to get acceptable performance.
I'd be happy if, as originally presented, it were possible to just pass a
raw_spinlock_ttospin_lock_irqsave()and friends.that's a spinlock type abstraction of
PREEMPT_RT, not of mainline.
Even when you're talking about the -rt tree, I suspect you really shouldn't be using raw spinlocks, right? I mean, if you have a timing critical operation, then you should ensure you have priorities set correctly so that you simply don't get preempted.
By using a raw_spinlock_t, you're saying that you're more important than anyone else (for the period of the critical section) including processes which the user has explicitly set to a higher priority.
If you get preempted, then you should be happy, because <standard RTOS nuclear power plant scenario> didn't occur.
Quoted 1 line
Any reason that stuff shouldn't move into mainline?
This sort of raw_spinlock_t arms race throughout drivers/ would be a huge reason not to move it into mainline.
Quoted 7 lines · Nick Piggin
Quoted 4 lines
Quoted 4 lines
Quoted 2 lines
I'd be happy if, as originally presented, it were possible to just pass a
raw_spinlock_ttospin_lock_irqsave()and friends.that's a spinlock type abstraction of
PREEMPT_RT, not of mainline.Even when you're talking about the -rt tree, I suspect you really shouldn't be using raw spinlocks, right?
That's one subject of discussion here. I got the expected knee-jerk responses (saying just that) -- right, ignore those and aim for ones that look at the actual issues.
Quoted 3 lines
I mean, if you have a
timing critical operation, then you should ensure you have priorities set correctly so that you simply don't get preempted.
Which is why bitops like <asm-generic/bitops/atomic.h> use normal spinlocks. Oh, wait, no they don't ...
Today, platform GPIO logic has no preemption points.
There are a few dozen implementations in the tree, not all using the generic calls (yet!), none radically different.
Certainly when gpio_set_value() is inlined, it works very much like set_bit()/clear_bit() on hardware with atomic ops. Not a preemption point
But it often turns into a function call that needs to map from GPIO number to GPIO bank, and then to a bitslice through that bank's registers. I know of only one platform which even has any locking on those code paths(*) ... so again those calls aren't preemption points.
Quoted 3 lines
By using a
raw_spinlock_t, you're saying that you're more important than anyone else (for the period of the critical section) including processes which the user has explicitly set to a higher priority.
Nope. Just saying that the relevant instructions (three, in the hot path I looked at, and not a case where priority inversion scenarios should be a concern) shouldn't be forcibly morphed into preemption points. Any more than other bitops were (not).
If a higher priority task needs that CPU, nothing prevents it from being immediately descheduled. Ditto handling a hardirq.
All this does is prevent constant and needless checking for "do you want to preempt me now?" "now?" "now?" in "now?" the middle "now?" of "now?" i/o "now?" loops.
Quoted 4 lines
Quoted 1 line
Any reason that stuff shouldn't move into mainline?
This sort of
raw_spinlock_tarms race throughoutdrivers/would be a huge reason not to move it into mainline.
This isn't driver code...
I think you've just presented an argument why that stuff shouldn't really exist in -rt either... :)
- Dave
(*) Exception, arch/arm/plat-omap/gpio.c uses spinlocks.
Maybe I should say "misuses"; ISTR getting various lockdep complaints about the same spinlock being used both from IRQ and non-IRQ contexts. In some common cases that lock could probably be eliminated, but if you look at the code you'll know why nobody's much wanted to clean it up.
Quoted 8 lines · David Brownell
Quoted 3 lines · Nick Piggin
I mean, if you have a
timing critical operation, then you should ensure you have priorities set correctly so that you simply don't get preempted.Which is why bitops like <
asm-generic/bitops/atomic.h> use normal spinlocks. Oh, wait, no they don't ...
No it isn't. It's nothing to do with that because upstream raw spinlocks disable preemption as well; the reason for using raw spinlocks in atomic.h is completely different.
Anyway, this whole line of argument is flawed. Even if the atomic.h code is crap, that doesn't give any license to introduce more bad code.
Quoted 8 lines
Quoted 3 lines
By using a
raw_spinlock_t, you're saying that you're more important than anyone else (for the period of the critical section) including processes which the user has explicitly set to a higher priority.Nope. Just saying that the relevant instructions (three, in the hot path I looked at, and not a case where priority inversion scenarios should be a concern) shouldn't be forcibly morphed into preemption points. Any more than other bitops were (not).
Don't raw_spinlock_t's quite explictly disallow preemption in the critical section? Eg. as opposed to spinlock_t, which does not, in -rt.
Quoted 6 lines
If a higher priority task needs that CPU, nothing prevents it from being immediately descheduled. Ditto handling a hardirq.
All this does is prevent constant and needless checking for "do you want to preempt me now?" "now?" "now?" in "now?" the middle "now?" of "now?" i/o "now?" loops.
Actually that's wrong. By disabling preemption, you have to explicitly check whether you should be preempted when you enable it again. If you don't disable preemption then you don't have to check anything -- you'll simply be preempted by asynchronous events.
Quoted 6 lines
Quoted 4 lines
Quoted 1 line
Any reason that stuff shouldn't move into mainline?
This sort of
raw_spinlock_tarms race throughoutdrivers/would be a huge reason not to move it into mainline.This isn't driver code...
Semantics. It's not something like the scheduler or interrupt handler or something that might have real reasons to use raw locks.
Quoted 2 lines
I think you've just presented an argument why that stuff shouldn't really exist in -rt either... :)
Anyway, I'm not going to argue about -rt specific stuff any longer. Whatever. It shouldn't go upstream like this though.
Quoted 5 lines · Nick Piggin
Quoted 3 lines
All this does is prevent constant and needless checking for "do you want to preempt me now?" "now?" "now?" in "now?" the middle "now?" of "now?" i/o "now?" loops.
Actually that's wrong.
Certainly it's right for the mainstream kernel. Dropping a lock (other than a raw spinlock) does that checking; when a loop needs to acquire then drop such a lock, that's exactly what's going on.
And in the RT kernel, it's got to do the same thing ... because dropping that lock may mean that a higher priority task should immediately run and grab the lock.
Quoted 11 lines · David Brownell
Quoted 5 lines · Nick Piggin
Quoted 3 lines
All this does is prevent constant and needless checking for "do you want to preempt me now?" "now?" "now?" in "now?" the middle "now?" of "now?" i/o "now?" loops.
Actually that's wrong.
Certainly it's right for the mainstream kernel. Dropping a lock (other than a raw spinlock) does that checking; when a loop needs to acquire then drop such a lock, that's exactly what's going on.
Obviously a raw spinlock is no different from a regular spinlock upstream.
Quoted 3 lines
And in the RT kernel, it's got to do the same thing ... because dropping that lock may mean that a higher priority task should immediately run and grab the lock.
No, it wakes up the highest priority waiter. If that wakeup causes a preemption, then that's fine, but it does not have to explicitly check preemption when dropping the lock.
Quoted 6 lines · Nick Piggin
Quoted 6 lines
Quoted 5 lines
Quoted 3 lines
All this does is prevent constant and needless checking for "do you want to preempt me now?" "now?" "now?" in "now?" the middle "now?" of "now?" i/o "now?" loops.
Actually that's wrong.
Quoted 7 lines
Quoted 4 lines
Certainly it's right for the mainstream kernel. Dropping a lock (other than a raw spinlock) does that checking; when a loop needs to acquire then drop such a lock, that's exactly what's going on.
Obviously a raw spinlock is no different from a regular spinlock upstream.
Erm, no. The raw ones don't have the extra logic when the lock gets dropped.
Quoted 12 lines · David Brownell
Quoted 11 lines · Nick Piggin
Quoted 10 lines
Quoted 5 lines
Quoted 3 lines
All this does is prevent constant and needless checking for "do you want to preempt me now?" "now?" "now?" in "now?" the middle "now?" of "now?" i/o "now?" loops.
Actually that's wrong.
Certainly it's right for the mainstream kernel. Dropping a lock (other than a raw spinlock) does that checking; when a loop needs to acquire then drop such a lock, that's exactly what's going on.
Quoted 5 lines
Quoted 2 lines
Obviously a raw spinlock is no different from a regular spinlock upstream.
Erm, no. The raw ones don't have the extra logic when the lock gets dropped.
If you don't have preemption disabled already, then it is a bug to use raw spinlocks. If you do have preemption disabled, then a regular spinlock isn't going to check preemption after the unlock either.
Quoted 16 lines · Nick Piggin
Quoted 15 lines · David Brownell
Quoted 13 lines · Nick Piggin
Quoted 10 lines
Quoted 5 lines
Quoted 3 lines
All this does is prevent constant and needless checking for "do you want to preempt me now?" "now?" "now?" in "now?" the middle "now?" of "now?" i/o "now?" loops.
Actually that's wrong.
Certainly it's right for the mainstream kernel. Dropping a lock (other than a raw spinlock) does that checking; when a loop needs to acquire then drop such a lock, that's exactly what's going on.
Obviously a raw spinlock is no different from a regular spinlock upstream.
Quoted 7 lines
Quoted 2 lines
Erm, no. The raw ones don't have the extra logic when the lock gets dropped.
If you don't have preemption disabled already, then it is a bug to use raw spinlocks. If you do have preemption disabled, then a regular spinlock isn't going to check preemption after the unlock either.
And I might add that this is just trying to nitpick at a weak link in the argument rather than prove anything important.
Even if you are avoiding preemption checks in upstream kernels, this is probably like several instructions to do. So if you're avoiding this for preformance reasons, then something's not right.
Quoted 4 lines · David Brownell
Quoted 1 line
Why do you want to use
raw_spinlock_t?Already answered elsewhere in this thread ... I'll highlight the point that such bitops shouldn't be preemption points.
raw_spinlock_t is a spinlock-internal implementation detail in the upstream kernel. You should _not_ be using it. The PREEMPT_RT raw_spinlock_t markings will move upstream in the future together with that feature, not piecemail wise with other patches. In other words:raw_spinlock_t in PREEMPT_RT != raw_spinlock_t in the upstream kernel. (and dont be worried, any necessary raw_spinlock_t annotations _will_ move upstream together with PREEMPT_RT, once that feature is being merged, so your code is not missing out on anything.)
The only code that should use raw_spinlock_t in the upstream kernel is the spinlock code and occasionally some debugging code. (If you still see any other strong reasons for it then please state it in simple, standalone terms without depending on context - your reasons were not clear to me so far so either me or you are confused about something and this is probably just a matter of communication.)
Ingo
Quoted 7 lines · David Brownell
Quoted 2 lines
I'm still trying to understand what you've observed here. Is it the case that a single gpio operation went from 6.4 up to 11.2 usecs?
That was a single bitbanged I2C bit transfer, with embedded udelay()s. I believe that was four gpio operations, as summarized at the end of that email above. Enabling preempt + debug increased the cost of each GPIO call from whatever it was (reasonable) by 1.2 usecs.
This raw lock change is just pampering over the design problem of the gpio lib:
There is no need to check for every single access to a GPIO pin, whether the pin has a valid number and the chip, which provides access to the pin, is still registered.
Each driver, which wants to access a pin, needs to make sure that
- the pin is available
- the pin is associated to this driver
- the chip reference count is incremented
_before_ it starts to do anything with the pin. Once this is done the access to the pin is completely lock free except for the protection of the chip hardware itself.
What you really need is a different API to request and free the access to a particular pin like:
struct gpio_desc {
struct gpio_chip *chip;
int pinoffset;
};int gpio_request_pin(int nr, struct gpio_desc *desc); void gpio_release_pin(struct gpio_desc *desc);
where the description structure is caller provided to avoid static allocation overhead for a large number of unused gpios.
The modification functions just use the description to access the pin instead of the number and the per access lookup/locking.
The protection of the chip list can be converted to a mutex and does not need to be a spinlock at all.
Thanks,
tglx
Quoted 9 lines · Thomas Gleixner
Quoted 7 lines · David Brownell
Quoted 2 lines
I'm still trying to understand what you've observed here. Is it the case that a single gpio operation went from 6.4 up to 11.2 usecs?
That was a single bitbanged I2C bit transfer, with embedded udelay()s. I believe that was four gpio operations, as summarized at the end of that email above. Enabling preempt + debug increased the cost of each GPIO call from whatever it was (reasonable) by 1.2 usecs.
Quoted 6 lines
This raw lock change is just pampering over the design problem of the gpio lib:
There is no need to check for every single access to a GPIO pin, whether the pin has a valid number and the chip, which provides access to the pin, is still registered.
As Haavard had noted. The "requested" flag is actually serving as a longterm bit-level lock, which -- assuming well-behaved callers, and no debug instrumentation -- obviates any need to grab a spinlock in hot paths.
Quoted 9 lines
Each driver, which wants to access a pin, needs to make sure that
- the pin is available
- the pin is associated to this driver
- the chip reference count is incremented_before_ it starts to do anything with the pin. Once this is done the access to the pin is completely lock free except for the protection of the chip hardware itself.
That's what the gpio_request() call does, although it's using something isomorphic to a refcount, not an actual refcount.
The key observation here is that we already *have* a bit which is serving as a per-gpio lock. It's just never been viewed as a lock before. :)
Quoted 2 lines
The protection of the chip list can be converted to a mutex and does not need to be a spinlock at all.
No, we still need to use a spinlock to protect table changes. The reason for that is briefly:
- gpio_request()/gpio_free() have so far been optional. Most platforms implement them as NOPs, not all drivers use them. (Having gpiolib in place should help change that ...)
- gpio_direction_input()/gpio_direction_output() implicitly request the pins, if they weren't already requested.
- Those input/output direction-setting calls may be called in IRQ contexts, which means (on non-RT kernels) no mutex.
So we're actually in good shape; just take out a bit of code (or turn it into debugging instrumentation) and I don't think anyone will complain about the locking any more.
- Dave
Quoted 9 lines · David Brownell
Quoted 2 lines
The protection of the chip list can be converted to a mutex and does not need to be a spinlock at all.
No, we still need to use a spinlock to protect table changes. The reason for that is briefly:
- gpio_request()/gpio_free() have so far been optional. Most platforms implement them as NOPs, not all drivers use them. (Having gpiolib in place should help change that ...)
By magically doing the request of the pin ? See below.
Quoted 2 lines
- gpio_direction_input()/gpio_direction_output() implicitly request the pins, if they weren't already requested.
Eek, that's completely wrong. Allowing to access a resource _before_ it is assigned and then doing the assignment implicit is a really bad idea.
Quoted 2 lines
- Those input/output direction-setting calls may be called in IRQ contexts, which means (on non-RT kernels) no mutex.
There is no reason to do that if you actually have a useful reference to the chip _before_ accessing the pin.
Quoted 3 lines
So we're actually in good shape; just take out a bit of code (or turn it into debugging instrumentation) and I don't think anyone will complain about the locking any more.
This still does not solve the lookup, which is done for each operation on a pin (direction setting, read, write).
tglx
Quoted 11 lines · Thomas Gleixner
Quoted 9 lines · David Brownell
Quoted 2 lines
The protection of the chip list can be converted to a mutex and does not need to be a spinlock at all.
No, we still need to use a spinlock to protect table changes. The reason for that is briefly:
- gpio_request()/gpio_free() have so far been optional. Most platforms implement them as NOPs, not all drivers use them. (Having gpiolib in place should help change that ...)
Quoted 8 lines
By magically doing the request of the pin ? See below.
Quoted 2 lines
- gpio_direction_input()/gpio_direction_output() implicitly request the pins, if they weren't already requested.
Eek, that's completely wrong. Allowing to access a resource _before_ it is assigned and then doing the assignment implicit is a really bad idea.
This is an artifact of making the GPIO interface easy to adopt, by letting all the initial adopters wrap "legacy" SOC-specific GPIO interfaces instead of creating a bunch of new platform code.
Only one of those legacy interfaces actually had a request/free model. (That was OMAP, which really needed it at one point to help sort out driver conflicts ... current drivers don't seem to trip over such conflicts, they've been fixed.)
You'll observe that a GPIO interface providing a standardized interface to the capabilities of SOC registers doesn't *need* to add a per-GPIO "is it allocated" flag ... most didn't.
However, now that we actually *have* such an interface, the merit of such flags is becoming more apparent. (As one person
observed: "Eek"!) And now gpiolib makes the costt to implement that mechanism a lot lower.
Quoted 5 lines
Quoted 2 lines
- Those input/output direction-setting calls may be called in IRQ contexts, which means (on non-RT kernels) no mutex.
There is no reason to do that if you actually have a useful reference to the chip _before_ accessing the pin.
See above ... until all platforms change and implement the gpio_request() as more than a NOP, we *do* have a reason.
Fortunately drivers are tending to do the right thing (making that call), but when they don't there are no warnings/errors.
Quoted 6 lines
Quoted 3 lines
So we're actually in good shape; just take out a bit of code (or turn it into debugging instrumentation) and I don't think anyone will complain about the locking any more.
This still does not solve the lookup, which is done for each operation on a pin (direction setting, read, write).
Are you saying that an array index is a cost to worry about here??
FYI, see the appended patch ... it's been sanity tested.
- Dave
=============
Simplify the hot-path (gpio value get/set) locking by taking advantage of the fact that gpio_request() effectively locks that GPIO in memory. This helps us avoid the belated complaints about whether this locking was one of the places raw spinlocks are OK to use.
This is a net code shrink, most of it by removing explicit locking calls from those hot paths.
--- Documentation/gpio.txt | 3 - lib/gpiolib.c | 118 ++++++++++++++----------------------------------- 2 files changed, 36 insertions(+), 85 deletions(-)
Documentation/gpio.txt +2 -1
@@ -173,7 +173,8 @@ get to the head of a queue to transmit a This requires sleeping, which can't be done from inside IRQ handlers. Platforms that support this type of GPIO distinguish them from other GPIOs -by returning nonzero from this call: +by returning nonzero from this call (which may be issued even for GPIOs +that have not been requested): int gpio_cansleep(unsigned gpio);
lib/gpiolib.c +34 -84
263 lines not on this page · raw message
Quoted 10 lines · On Thu, 15 Nov 2007 00:20:33 -0800 David Brownell <[email protected]> wrote:
Quoted 6 lines
Quoted 2 lines
- gpio_direction_input()/gpio_direction_output() implicitly request the pins, if they weren't already requested.
Eek, that's completely wrong. Allowing to access a resource _before_ it is assigned and then doing the assignment implicit is a really bad idea.
This is an artifact of making the GPIO interface easy to adopt, by letting all the initial adopters wrap "legacy" SOC-specific GPIO interfaces instead of creating a bunch of new platform code.
It's still ok for platforms that do not use gpiolib to provide NOP gpio_request() and gpio_free() functions if they don't care about tracking gpio usage. That doesn't mean we shouldn't require all drivers to use those calls -- if they are implemented as empty inlines, it won't cost anything to call them.
I'd rather speed up the gpio_direction_* functions a bit by removing the implicit gpio_request() since they may be called a lot more frequently and, as you pointed out, possibly from atomic context.
Håvard
Quoted 10 lines · Haavard Skinnemoen
Quoted 7 lines · On Thu, 15 Nov 2007 00:20:33 -0800 David Brownell <[email protected]> wrote:
Quoted 6 lines
Quoted 2 lines
- gpio_direction_input()/gpio_direction_output() implicitly request the pins, if they weren't already requested.
Eek, that's completely wrong. Allowing to access a resource _before_ it is assigned and then doing the assignment implicit is a really bad idea.
Quoted 7 lines
Quoted 3 lines
This is an artifact of making the GPIO interface easy to adopt, by letting all the initial adopters wrap "legacy" SOC-specific GPIO interfaces instead of creating a bunch of new platform code.
It's still ok for platforms that do not use gpiolib to provide NOP
gpio_request()andgpio_free()functions if they don't care about tracking gpio usage.
In fact they *must* provide some implementation of those calls, and several platforms do exactly that. (Since the legacy GPIO code didn't use such primitives ... nothing to wrap.)
Quoted 3 lines
That doesn't mean we shouldn't require all drivers
to use those calls -- if they are implemented as empty inlines, it won't cost anything to call them.
Thing is, issuing those calls is not currently mandatory ... so requiring use of them would be an incompatible API change, which would require auditing (and fixing, and testing) much platform code.
Quoted 3 lines
I'd rather speed up the gpio_direction_* functions a bit by removing the implicit
gpio_request()since they may be called a lot more frequently and, as you pointed out, possibly from atomic context.
In fact, possibly before IRQs get set up ... some platforms start using GPIOs very early in system setup.
- Dave
Quoted 4 lines · Thomas Gleixner
struct
gpio_desc{
structgpio_chip*chip;
int pinoffset;
};
Eric Miao has a different notion of "gpio_desc" more analagous to the "irq_desc", so I'll not use that name for such a structure.
Let me call yours a "struct gpio_pin" instead -- purely for some discussion here.
There's been some desire for a "gpio_pin" struct for entirely different reasons ... as the identifiers passed into a slightly different type of GPIO programming interface. So for example
static inline void gpiopin_set_value(struct gpio_pin *p, int v)
{
p->chip->set(p->chip, p->pinoffset, v);}
One reason to be interested in gpio_chip is that it it would easily support interoperation between traditional GPIO ID schemes (numbered to match the SOC at the heart of a given board) and more dynamic ones that might be needed to use curiousities like a set of USB-to-GPIO adapters.
- Dave
Quoted 20 lines · David Brownell
Quoted 4 lines · Thomas Gleixner
struct gpio_desc { struct gpio_chip *chip; int pinoffset; };Eric Miao has a different notion of "
gpio_desc" more analagous to the "irq_desc", so I'll not use that name for such a structure.Let me call yours a "struct
gpio_pin" instead -- purely for some discussion here.There's been some desire for a "
gpio_pin" struct for entirely different reasons ... as the identifiers passed into a slightly different type of GPIO programming interface. So for examplestatic inline void
gpiopin_set_value(struct gpio_pin *p, int v){ p->chip->set(p->chip, p->pinoffset, v);
Quoted 7 lines
}
One reason to be interested in
gpio_chipis that it it would easily support interoperation between traditional GPIO ID schemes (numbered to match the SOC at the heart of a given board) and more dynamic ones that might be needed to use curiousities like a set of USB-to-GPIO adapters.
So can we please do this right from the beginning instead of adding a new library first which suffers from bad design compromises.
tglx