[PATCH] locking/atomics: Clean up the atomic.h maze of #defines

From: Ingo Molnar
Date: Sat May 05 2018 - 04:13:21 EST



* Mark Rutland <mark.rutland@xxxxxxx> wrote:

> On Fri, May 04, 2018 at 08:01:05PM +0200, Peter Zijlstra wrote:
> > On Fri, May 04, 2018 at 06:39:32PM +0100, Mark Rutland wrote:
> > > Currently <asm-generic/atomic-instrumented.h> only instruments the fully
> > > ordered variants of atomic functions, ignoring the {relaxed,acquire,release}
> > > ordering variants.
> > >
> > > This patch reworks the header to instrument all ordering variants of the atomic
> > > functions, so that architectures implementing these are instrumented
> > > appropriately.
> > >
> > > To minimise repetition, a macro is used to generate each variant from a common
> > > template. The {full,relaxed,acquire,release} order variants respectively are
> > > then built using this template, where the architecture provides an
> > > implementation.
>
> > > include/asm-generic/atomic-instrumented.h | 1195 ++++++++++++++++++++++++-----
> > > 1 file changed, 1008 insertions(+), 187 deletions(-)
> >
> > Is there really no way to either generate or further macro compress this?
>
> I can definitely macro compress this somewhat, but the bulk of the
> repetition will be the ifdeffery, which can't be macro'd away IIUC.

The thing is, the existing #ifdeffery is suboptimal to begin with.

I just did the following cleanups (patch attached):

include/linux/atomic.h | 1275 +++++++++++++++++++++---------------------------
1 file changed, 543 insertions(+), 732 deletions(-)

The gist of the changes is the following simplification of the main construct:

Before:

#ifndef atomic_fetch_dec_relaxed

#ifndef atomic_fetch_dec
#define atomic_fetch_dec(v) atomic_fetch_sub(1, (v))
#define atomic_fetch_dec_relaxed(v) atomic_fetch_sub_relaxed(1, (v))
#define atomic_fetch_dec_acquire(v) atomic_fetch_sub_acquire(1, (v))
#define atomic_fetch_dec_release(v) atomic_fetch_sub_release(1, (v))
#else /* atomic_fetch_dec */
#define atomic_fetch_dec_relaxed atomic_fetch_dec
#define atomic_fetch_dec_acquire atomic_fetch_dec
#define atomic_fetch_dec_release atomic_fetch_dec
#endif /* atomic_fetch_dec */

#else /* atomic_fetch_dec_relaxed */

#ifndef atomic_fetch_dec_acquire
#define atomic_fetch_dec_acquire(...) \
__atomic_op_acquire(atomic_fetch_dec, __VA_ARGS__)
#endif

#ifndef atomic_fetch_dec_release
#define atomic_fetch_dec_release(...) \
__atomic_op_release(atomic_fetch_dec, __VA_ARGS__)
#endif

#ifndef atomic_fetch_dec
#define atomic_fetch_dec(...) \
__atomic_op_fence(atomic_fetch_dec, __VA_ARGS__)
#endif
#endif /* atomic_fetch_dec_relaxed */

After:

#ifndef atomic_fetch_dec_relaxed
# ifndef atomic_fetch_dec
# define atomic_fetch_dec(v) atomic_fetch_sub(1, (v))
# define atomic_fetch_dec_relaxed(v) atomic_fetch_sub_relaxed(1, (v))
# define atomic_fetch_dec_acquire(v) atomic_fetch_sub_acquire(1, (v))
# define atomic_fetch_dec_release(v) atomic_fetch_sub_release(1, (v))
# else
# define atomic_fetch_dec_relaxed atomic_fetch_dec
# define atomic_fetch_dec_acquire atomic_fetch_dec
# define atomic_fetch_dec_release atomic_fetch_dec
# endif
#else
# ifndef atomic_fetch_dec_acquire
# define atomic_fetch_dec_acquire(...) __atomic_op_acquire(atomic_fetch_dec, __VA_ARGS__)
# endif
# ifndef atomic_fetch_dec_release
# define atomic_fetch_dec_release(...) __atomic_op_release(atomic_fetch_dec, __VA_ARGS__)
# endif
# ifndef atomic_fetch_dec
# define atomic_fetch_dec(...) __atomic_op_fence(atomic_fetch_dec, __VA_ARGS__)
# endif
#endif

The new variant is readable at a glance, and the hierarchy of defines is very
obvious as well.

And I think we could do even better - there's absolutely no reason why _every_
operation has to be made conditional on a finegrained level - they are overriden
in API groups. In fact allowing individual override is arguably a fragility.

So we could do the following simplification on top of that:

#ifndef atomic_fetch_dec_relaxed
# ifndef atomic_fetch_dec
# define atomic_fetch_dec(v) atomic_fetch_sub(1, (v))
# define atomic_fetch_dec_relaxed(v) atomic_fetch_sub_relaxed(1, (v))
# define atomic_fetch_dec_acquire(v) atomic_fetch_sub_acquire(1, (v))
# define atomic_fetch_dec_release(v) atomic_fetch_sub_release(1, (v))
# else
# define atomic_fetch_dec_relaxed atomic_fetch_dec
# define atomic_fetch_dec_acquire atomic_fetch_dec
# define atomic_fetch_dec_release atomic_fetch_dec
# endif
#else
# ifndef atomic_fetch_dec
# define atomic_fetch_dec(...) __atomic_op_fence(atomic_fetch_dec, __VA_ARGS__)
# define atomic_fetch_dec_acquire(...) __atomic_op_acquire(atomic_fetch_dec, __VA_ARGS__)
# define atomic_fetch_dec_release(...) __atomic_op_release(atomic_fetch_dec, __VA_ARGS__)
# endif
#endif

Note how the grouping of APIs based on 'atomic_fetch_dec' is already an assumption
in the primary !atomic_fetch_dec_relaxed branch.

I much prefer such clear constructs of API mapping versus magic macros.

Thanks,

Ingo

=============================>