From: Waiman Long <waiman.long@hpe.com>
To: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>,
Ingo Molnar <mingo@redhat.com>, "H. Peter Anvin" <hpa@zytor.com>,
Alexander Viro <viro@zeniv.linux.org.uk>,
linux-fsdevel@vger.kernel.org, x86@kernel.org,
linux-kernel@vger.kernel.org,
Scott J Norton <scott.norton@hp.com>,
Douglas Hatch <doug.hatch@hp.com>
Subject: Re: [RFC PATCH 1/3] lib/list_batch: A simple list insertion/deletion batching facility
Date: Wed, 27 Jan 2016 15:22:19 -0500 [thread overview]
Message-ID: <56A926FB.80609@hpe.com> (raw)
In-Reply-To: <20160127163450.GU6357@twins.programming.kicks-ass.net>
On 01/27/2016 11:34 AM, Peter Zijlstra wrote:
> On Tue, Jan 26, 2016 at 11:03:37AM -0500, Waiman Long wrote:
>> +static __always_inline void _list_batch_cmd(enum list_batch_cmd cmd,
>> + struct list_head *head,
>> + struct list_head *entry)
>> +{
>> + if (cmd == lb_cmd_add)
>> + list_add(entry, head);
>> + else if (cmd == lb_cmd_del)
>> + list_del(entry);
>> + else /* cmd == lb_cmd_del_init */
>> + list_del_init(entry);
> Maybe use switch(), GCC has fancy warns with enums and switch().
OK, I will look at the generated code to see if there is any difference.
>
>> +}
>> +static inline void do_list_batch(enum list_batch_cmd cmd, spinlock_t *lock,
>> + struct list_batch *batch,
>> + struct list_head *entry)
>> +{
>> + /*
>> + * Fast path
>> + */
>> + if (spin_trylock(lock)) {
>> + _list_batch_cmd(cmd, batch->list, entry);
>> + spin_unlock(lock);
>> _list_batch_cmd
> This is still quite a lot of code for an inline function
I expect the callers will call it with a constant cmd, thus optimizing
out all the if conditional checks in _list_batch_cmd(). Taking the
inline out will probably stop that optimization.
>> + return;
>> + }
>> + do_list_batch_slowpath(cmd, lock, batch, entry);
>> +}
>
>
>> +void do_list_batch_slowpath(enum list_batch_cmd cmd, spinlock_t *lock,
>> + struct list_batch *batch, struct list_head *entry)
>> +{
>> + struct list_batch_qnode node, *prev, *next, *nptr;
>> + int loop;
>> +
>> + /*
>> + * Put itself into the list_batch queue
>> + */
>> + node.next = NULL;
>> + node.entry = entry;
>> + node.cmd = cmd;
>> + node.state = lb_state_waiting;
>> +
> Here we rely on the release barrier implied by xchg() to ensure the node
> initialization is complete before the xchg() publishes the thing.
>
> But do we also need the acquire part of this barrier? From what I could
> tell, the primitive as a whole does not imply any ordering.
I think we probably won't need the acquire part, but I don't have a
non-x86 machine that can really test out the more relaxed versions of
the atomic ops. That is why I use the strict versions. We can always
relax it later on with additional patches.
>
>> + prev = xchg(&batch->tail,&node);
>> +
>> + if (prev) {
>> + WRITE_ONCE(prev->next,&node);
>> + while (READ_ONCE(node.state) == lb_state_waiting)
>> + cpu_relax();
>> + if (node.state == lb_state_done)
>> + return;
>> + WARN_ON(node.state != lb_state_batch);
>> + }
>> +
>> + /*
>> + * We are now the queue head, we shold now acquire the lock and
>> + * process a batch of qnodes.
>> + */
>> + loop = LB_BATCH_SIZE;
> Have you tried different sizes?
I have tried 64 and 128. Using 128 seems to give a bit better
performance number.
>> + next =&node;
>> + spin_lock(lock);
>> +
>> +do_list_again:
>> + do {
>> + nptr = next;
>> + _list_batch_cmd(nptr->cmd, batch->list, nptr->entry);
>> + next = READ_ONCE(nptr->next);
>> + /*
>> + * As soon as the state is marked lb_state_done, we
>> + * can no longer assume the content of *nptr as valid.
>> + * So we have to hold off marking it done until we no
>> + * longer need its content.
>> + *
>> + * The release barrier here is to make sure that we
>> + * won't access its content after marking it done.
>> + */
>> + if (next)
>> + smp_store_release(&nptr->state, lb_state_done);
>> + } while (--loop&& next);
>> + if (!next) {
>> + /*
>> + * The queue tail should equal to nptr, so clear it to
>> + * mark the queue as empty.
>> + */
>> + if (cmpxchg(&batch->tail, nptr, NULL) != nptr) {
>> + /*
>> + * Queue not empty, wait until the next pointer is
>> + * initialized.
>> + */
>> + while (!(next = READ_ONCE(nptr->next)))
>> + cpu_relax();
>> + }
>> + /* The above cmpxchg acts as a memory barrier */
> for what? :-)
>
> Also, if that cmpxchg() fails, it very much does _not_ act as one.
>
> I suspect you want smp_store_release() setting the state_done, just as
> above, and then use cmpxchg_relaxed().
You are right. I did forgot about there was no memory barrier guarantee
when cmpxchg() fails. However, in that case, the READ_ONCE() and
WRITE_ONCE() macros should still provide the necessary ordering, IMO. I
can certainly change it to use cmpxchg_relaxed() and smp_store_release()
instead.
>
>> + WRITE_ONCE(nptr->state, lb_state_done);
>> + }
>> + if (next) {
>> + if (loop)
>> + goto do_list_again; /* More qnodes to process */
>> + /*
>> + * Mark the next qnode as head to process the next batch
>> + * of qnodes. The new queue head cannot proceed until we
>> + * release the lock.
>> + */
>> + WRITE_ONCE(next->state, lb_state_batch);
>> + }
>> + spin_unlock(lock);
>> +}
>> +EXPORT_SYMBOL_GPL(do_list_batch_slowpath);
Cheers,
Longman
next prev parent reply other threads:[~2016-01-27 20:22 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-01-26 16:03 [RFC PATCH 0/3] lib/list_batch: A simple list insertion/deletion batching facility Waiman Long
2016-01-26 16:03 ` [RFC PATCH 1/3] " Waiman Long
2016-01-27 16:34 ` Peter Zijlstra
2016-01-27 20:22 ` Waiman Long [this message]
2016-01-27 20:54 ` Peter Zijlstra
2016-01-28 16:45 ` Waiman Long
2016-01-28 18:35 ` Peter Zijlstra
2016-01-26 16:03 ` [RFC PATCH 2/3] lib/list_batch, x86: Enable list insertion/deletion batching in x86-64 Waiman Long
2016-01-26 21:44 ` Andi Kleen
2016-01-27 16:38 ` Peter Zijlstra
2016-01-27 20:34 ` Waiman Long
2016-01-26 16:03 ` [RFC PATCH 3/3] vfs: Enable list batching for the superblock's inode list Waiman Long
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=56A926FB.80609@hpe.com \
--to=waiman.long@hpe.com \
--cc=doug.hatch@hp.com \
--cc=hpa@zytor.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=scott.norton@hp.com \
--cc=tglx@linutronix.de \
--cc=viro@zeniv.linux.org.uk \
--cc=x86@kernel.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.