From: ohad@wizery.com (Ohad Ben-Cohen)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v2 1/4] drivers: hwspinlock: add generic framework
Date: Fri, 26 Nov 2010 10:53:10 +0200 [thread overview]
Message-ID: <AANLkTin5Tfcuy0WcHJ22rWC8niwZCj8SZAMKLz0p9jwc@mail.gmail.com> (raw)
In-Reply-To: <20101126045912.GC6598@lixom.net>
On Fri, Nov 26, 2010 at 6:59 AM, Olof Johansson <olof@lixom.net> wrote:
>> +#define pr_fmt(fmt) ? ?"%s: " fmt, __func__
>
> Not used.
Yes, it is, check out how the pr_* macros are implemented:
#define pr_info(fmt, ...) \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
I use it to ensure that the function name is printed with any pr_*
macro, without having to explicitly specify the __func__ param every
time.
>> +int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
>> +{
>> + ? ? int ret;
>> +
>> + ? ? if (unlikely(!hwlock)) {
>> + ? ? ? ? ? ? pr_err("invalid hwlock\n");
>
> These kind of errors can get very spammy for buggy drivers.
Yeah, but that's the purpose - I want to catch such egregious drivers
who try to crash the kernel.
> It's likely
> more useful to either do a WARN_ON(), and/or move them under a debug
> config option.
Why would you prefer to compile out reporting of such extremely buggy behavior ?
>
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> + ? ? if (unlikely(mode == HWLOCK_IRQSTATE && flags == NULL)) {
>> + ? ? ? ? ? ? pr_err("invalid flags pointer (null)\n");
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> +
>> + ? ? /*
>> + ? ? ?* This spin_lock{_irq, _irqsave} serves three purposes:
>> + ? ? ?*
>> + ? ? ?* 1. Disable preemption, in order to minimize the period of time
>> + ? ? ?* ? ?in which the hwspinlock is taken. This is important in order
>> + ? ? ?* ? ?to minimize the possible polling on the hardware interconnect
>> + ? ? ?* ? ?by a remote user of this lock.
>> + ? ? ?* 2. Make the hwspinlock SMP-safe (so we can take it from
>> + ? ? ?* ? ?additional contexts on the local host).
>> + ? ? ?* 3. Ensure that in_atomic/might_sleep checks catch potential
>> + ? ? ?* ? ?problems with hwspinlock usage (e.g. scheduler checks like
>> + ? ? ?* ? ?'scheduling while atomic' etc.)
>> + ? ? ?*/
>> + ? ? if (mode == HWLOCK_IRQSTATE)
>> + ? ? ? ? ? ? ret = spin_trylock_irqsave(&hwlock->lock, *flags);
>> + ? ? else if (mode == HWLOCK_IRQ)
>> + ? ? ? ? ? ? ret = spin_trylock_irq(&hwlock->lock);
>> + ? ? else
>> + ? ? ? ? ? ? ret = spin_trylock(&hwlock->lock);
>> +
>> + ? ? /* is lock already taken by another context on the local cpu ? */
>> + ? ? if (!ret)
>> + ? ? ? ? ? ? return -EBUSY;
>> +
>> + ? ? /* try to take the hwspinlock device */
>> + ? ? ret = hwlock->ops->trylock(hwlock);
>> +
>> + ? ? /* if hwlock is already taken, undo spin_trylock_* and exit */
>> + ? ? if (!ret) {
>> + ? ? ? ? ? ? if (mode == HWLOCK_IRQSTATE)
>> + ? ? ? ? ? ? ? ? ? ? spin_unlock_irqrestore(&hwlock->lock, *flags);
>> + ? ? ? ? ? ? else if (mode == HWLOCK_IRQ)
>> + ? ? ? ? ? ? ? ? ? ? spin_unlock_irq(&hwlock->lock);
>> + ? ? ? ? ? ? else
>> + ? ? ? ? ? ? ? ? ? ? spin_unlock(&hwlock->lock);
>> +
>> + ? ? ? ? ? ? return -EBUSY;
>> + ? ? }
>> +
>> + ? ? /*
>> + ? ? ?* We can be sure the other core's memory operations
>> + ? ? ?* are observable to us only _after_ we successfully take
>> + ? ? ?* the hwspinlock, so we must make sure that subsequent memory
>> + ? ? ?* operations will not be reordered before we actually took the
>> + ? ? ?* hwspinlock.
>> + ? ? ?*
>> + ? ? ?* Note: the implicit memory barrier of the spinlock above is too
>> + ? ? ?* early, so we need this additional explicit memory barrier.
>> + ? ? ?*/
>> + ? ? mb();
>
> rmb() should be sufficient here.
It's not.
We need to make sure that our writes, too, will not be reordered
before that barrier. Otherwise, we might end up changing protected
shared memory during the critical section of the remote processor
(before we acquire the lock we must not read from, or write to, the
protected shared memory).
I guess I need to add a comment about this.
>> +int __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
>> +{
>> + ? ? if (unlikely(!hwlock)) {
>> + ? ? ? ? ? ? pr_err("invalid hwlock\n");
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> + ? ? if (unlikely(mode == HWLOCK_IRQSTATE && flags == NULL)) {
>> + ? ? ? ? ? ? pr_err("invalid flags pointer (null)\n");
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> +
>> + ? ? /*
>> + ? ? ?* We must make sure that memory operations, done before unlocking
>> + ? ? ?* the hwspinlock, will not be reordered after the lock is released.
>> + ? ? ?*
>> + ? ? ?* That's the purpose of this explicit memory barrier.
>> + ? ? ?*
>> + ? ? ?* Note: the memory barrier induced by the spin_unlock below is too
>> + ? ? ?* late; the other core is going to access memory soon after it will
>> + ? ? ?* take the hwspinlock, and by then we want to be sure our memory
>> + ? ? ?* operations are already observable.
>> + ? ? ?*/
>> + ? ? mb();
>
> wmb() should be sufficient here.
No - here, too, we need to make sure that also our read operations
will not be reordered after the barrier. Otherwise, we might end up
reading memory that has already been changed by a remote processor
that is just about to acquire the lock.
>
>> +
>> + ? ? hwlock->ops->unlock(hwlock);
>> +
>> + ? ? /* Undo the spin_trylock{_irq, _irqsave} called while locking */
>> + ? ? if (mode == HWLOCK_IRQSTATE)
>> + ? ? ? ? ? ? spin_unlock_irqrestore(&hwlock->lock, *flags);
>> + ? ? else if (mode == HWLOCK_IRQ)
>> + ? ? ? ? ? ? spin_unlock_irq(&hwlock->lock);
>> + ? ? else
>> + ? ? ? ? ? ? spin_unlock(&hwlock->lock);
>> +
>> + ? ? return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(__hwspin_unlock);
>> +
>> + ? ? /* mark this hwspinlock as available */
>> + ? ? tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? HWSPINLOCK_UNUSED);
>> +
>> + ? ? /* this implies an unrecoverable bug. at least rant */
>> + ? ? WARN_ON(tmp != hwlock);
>
> I don't see how this could ever happen?
It can't. Unless there's a bug somewhere.
That's why:
1. It is a WARN_ON - I don't care if someone compiles this out
2. There is no error handler for this (simply because there can never
be an error handler for buggy code). It's just a rant, meant to catch
an unlikely buggy event.
The reason I did this is because I like to check return values. Even
if it's only for sanity sake.
Since it's not a hot path this shouldn't matter to anyone, but if
people are still bothered by it, it can be removed.
>> +/**
>> + * hwspinlock_unregister() - unregister an hw spinlock
>> + * @id: index of the specific hwspinlock to unregister
>> + *
>> + * This function should be called from the underlying platform-specific
>> + * implementation, to unregister an existing (and unused) hwspinlock.
>> + *
>> + * Can be called from an atomic context (will not sleep) but not from
>> + * within interrupt context.
>> + *
>> + * Returns the address of hwspinlock @id on success, or NULL on failure
>
> Why not just return int for success / fail and have the driver keep track
> of the lock pointers too?
Because it's elegant. This way drivers don't need to keep track of the pointers.
It can be changed, with an extra cost of code (duplicated for each
implementation) and memory, but why would we want to do that ?
> Or, if you for some reason is attached to the ptr-return case, please make it return
> standard ERR_PTR instead.
ERR_PTR patterns have been reasonably argued against in the previous
discussion, and I heartily agreed (see
http://www.mail-archive.com/linux-omap at vger.kernel.org/msg37453.html).
>> + ? ? /* look for an unused lock */
>> + ? ? ret = radix_tree_gang_lookup_tag(&hwspinlock_tree, (void **)&hwlock,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0, 1, HWSPINLOCK_UNUSED);
>> + ? ? if (ret == 0) {
>> + ? ? ? ? ? ? pr_warn("a free hwspinlock is not available\n");
>> + ? ? ? ? ? ? hwlock = NULL;
>> + ? ? ? ? ? ? goto out;
>> + ? ? }
>> +
>> + ? ? /* sanity check: we shouldn't get more than we requested for */
>> + ? ? WARN_ON(ret > 1);
>
> No need to check, unless you're debugging the radix code.
Again, this is me preferring to check return values for sanity sake.
The one time that this may yield true (e.g. code has erroneously
changed) is well worth it.
It shouldn't bother anyone since it's not a hot path, but as I said
earlier, if people don't like it that much, it can be removed.
>
>> + ? ? /* mark as used and power up */
>> + ? ? ret = __hwspinlock_request(hwlock);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? hwlock = NULL;
>> +
>> +out:
>> + ? ? spin_unlock(&hwspinlock_tree_lock);
>> + ? ? return hwlock;
>> +}
>> +EXPORT_SYMBOL_GPL(hwspinlock_request);
>> +
>
> [...]
>
>> +/**
>> + * hwspinlock_free() - free a specific hwspinlock
>> + * @hwlock: the specific hwspinlock to free
>> + *
>> + * This function mark @hwlock as free again.
>> + * Should only be called with an @hwlock that was retrieved from
>> + * an earlier call to omap_hwspinlock_request{_specific}.
>> + *
>> + * Can be called from an atomic context (will not sleep) but not from
>> + * within interrupt context (simply because there is no use case for
>> + * that yet).
>> + *
>> + * Returns 0 on success, or an appropriate error code on failure
>> + */
>> +int hwspinlock_free(struct hwspinlock *hwlock)
>> +{
>> + ? ? struct hwspinlock *tmp;
>> + ? ? int ret;
>> +
>> + ? ? if (!hwlock) {
>
> Some alloc/free APIs will just silently return for these cases.
True.
Some other free API will simply crash.
Does it bother so much if we stick to the safe side here ?
>
>> + ? ? ? ? ? ? pr_err("invalid hwlock\n");
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> +
>> + ? ? spin_lock(&hwspinlock_tree_lock);
>> +
>> + ? ? /* make sure the hwspinlock is used */
>> + ? ? ret = radix_tree_tag_get(&hwspinlock_tree, hwlock->id,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? HWSPINLOCK_UNUSED);
>> + ? ? if (ret == 1) {
>> + ? ? ? ? ? ? dev_err(hwlock->dev, "%s: hwlock is already free\n", __func__);
>
> Double free. WARN_ON() seems appropriate?
I don't want that this message will ever get compiled out.
>
>> + ? ? ? ? ? ? ret = -EINVAL;
>> + ? ? ? ? ? ? goto out;
>> + ? ? }
>> +
>> + ? ? /* notify the underlying device that power is not needed */
>> + ? ? ret = pm_runtime_put(hwlock->dev);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? goto out;
>> +
>> + ? ? /* mark this hwspinlock as available */
>> + ? ? tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? HWSPINLOCK_UNUSED);
>> +
>> + ? ? /* sanity check (this shouldn't happen) */
>> + ? ? WARN_ON(tmp != hwlock);
>
> No need for this.
Please see explanations above why I added those sanity checks (despite
my explicit comment saying this shouldn't happen).
Thanks!
Ohad.
>
>> +
>> + ? ? module_put(hwlock->owner);
>> +
>> +out:
>> + ? ? spin_unlock(&hwspinlock_tree_lock);
>> + ? ? return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(hwspinlock_free);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("Generic hardware spinlock interface");
>> +MODULE_AUTHOR("Ohad Ben-Cohen <ohad@wizery.com>");
>
>
>
>
next prev parent reply other threads:[~2010-11-26 8:53 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-11-23 15:38 [PATCH v2 0/4] Introduce common hardware spinlock interface Ohad Ben-Cohen
2010-11-23 15:38 ` [PATCH v2 1/4] drivers: hwspinlock: add generic framework Ohad Ben-Cohen
2010-11-24 7:44 ` Kamoolkar, Mugdha
2010-11-24 19:59 ` Ohad Ben-Cohen
2010-11-25 3:59 ` David Brownell
2010-11-25 6:40 ` Ohad Ben-Cohen
2010-11-25 20:22 ` David Brownell
2010-11-26 7:34 ` Ohad Ben-Cohen
2010-11-27 1:24 ` David Brownell
2010-11-29 9:57 ` Ohad Ben-Cohen
2010-11-25 6:05 ` Kamoolkar, Mugdha
2010-11-25 14:29 ` Ohad Ben-Cohen
2010-11-26 4:59 ` Olof Johansson
2010-11-26 7:18 ` Grant Likely
2010-11-26 21:00 ` Olof Johansson
2010-11-26 8:53 ` Ohad Ben-Cohen [this message]
2010-11-26 9:18 ` Russell King - ARM Linux
2010-11-26 10:16 ` Ohad Ben-Cohen
2010-11-26 10:45 ` Russell King - ARM Linux
2010-11-26 22:18 ` Ohad Ben-Cohen
2010-11-26 22:53 ` Russell King - ARM Linux
2010-11-29 9:46 ` Ohad Ben-Cohen
2010-11-26 22:51 ` Olof Johansson
2010-11-29 21:31 ` Ohad Ben-Cohen
2010-11-30 19:00 ` Tony Lindgren
2010-11-30 22:20 ` Ohad Ben-Cohen
2010-11-30 22:23 ` Tony Lindgren
2010-11-23 15:38 ` [PATCH v2 2/4] drivers: hwspinlock: add OMAP implementation Ohad Ben-Cohen
2010-11-23 23:23 ` Ionut Nicu
2010-11-24 10:33 ` Ohad Ben-Cohen
2010-11-23 15:38 ` [PATCH v2 3/4] OMAP4: hwmod data: Add hwspinlock Ohad Ben-Cohen
2010-11-23 15:39 ` [PATCH v2 4/4] omap: add hwspinlock device Ohad Ben-Cohen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=AANLkTin5Tfcuy0WcHJ22rWC8niwZCj8SZAMKLz0p9jwc@mail.gmail.com \
--to=ohad@wizery.com \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).