diff mbox series

[v6,04/20] xen/asm-generic: introduce generic non-atomic test_*bit()

Message ID 48b7dfafccc7a0ed814b5dfb0f109a0473a1b4b4.1710517542.git.oleksii.kurochko@gmail.com (mailing list archive)
State Superseded
Headers show
Series Enable build of full Xen for RISC-V | expand

Commit Message

Oleksii Kurochko March 15, 2024, 6:06 p.m. UTC
The patch introduces the following generic functions:
* test_bit
* generic___test_and_set_bit
* generic___test_and_clear_bit
* generic___test_and_change_bit

Also, the patch introduces the following generics which are
used by the functions mentioned above:
* BITOP_BITS_PER_WORD
* BITOP_MASK
* BITOP_WORD
* BITOP_TYPE

These functions and macros can be useful for architectures
that don't have corresponding arch-specific instructions.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
---
 Changes in V6:
  - Nothing changed ( only rebase )
---
 Changes in V5:
   - new patch
---
 xen/include/asm-generic/bitops/bitops-bits.h  | 21 +++++
 .../asm-generic/bitops/generic-non-atomic.h   | 89 +++++++++++++++++++
 xen/include/asm-generic/bitops/test-bit.h     | 18 ++++
 3 files changed, 128 insertions(+)
 create mode 100644 xen/include/asm-generic/bitops/bitops-bits.h
 create mode 100644 xen/include/asm-generic/bitops/generic-non-atomic.h
 create mode 100644 xen/include/asm-generic/bitops/test-bit.h

Comments

Jan Beulich March 20, 2024, 3:30 p.m. UTC | #1
On 15.03.2024 19:06, Oleksii Kurochko wrote:
> The patch introduces the following generic functions:
> * test_bit
> * generic___test_and_set_bit
> * generic___test_and_clear_bit
> * generic___test_and_change_bit
> 
> Also, the patch introduces the following generics which are
> used by the functions mentioned above:
> * BITOP_BITS_PER_WORD
> * BITOP_MASK
> * BITOP_WORD
> * BITOP_TYPE
> 
> These functions and macros can be useful for architectures
> that don't have corresponding arch-specific instructions.
> 
> Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>

First of all: How much syncing has there been versus Andrew's plans?
I don't want to end up in the middle of two entirely different
approaches to the same generalization goal.

> --- /dev/null
> +++ b/xen/include/asm-generic/bitops/bitops-bits.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_GENERIC_BITOPS_BITS_H_
> +#define _ASM_GENERIC_BITOPS_BITS_H_
> +
> +#ifndef BITOP_BITS_PER_WORD
> +#define BITOP_BITS_PER_WORD     32
> +#endif
> +
> +#ifndef BITOP_MASK
> +#define BITOP_MASK(nr)          (1U << ((nr) % BITOP_BITS_PER_WORD))
> +#endif
> +
> +#ifndef BITOP_WORD
> +#define BITOP_WORD(nr)          ((nr) / BITOP_BITS_PER_WORD)
> +#endif
> +
> +#ifndef BITOP_TYPE
> +typedef uint32_t bitops_uint_t;
> +#endif

Is it of practical use to permit overriding of one of BITOP_TYPE and
BITOP_BITS_PER_WORD? I think both want tying together.

Is it further of any use to allow overriding of BITOP_{MASK,WORD}?
There's minimal generalization needed to allow an override of
BITOP_TYPE, though:

#define BITOP_MASK(nr)          ((bitop_uint_t)1 << ((nr) % BITOP_BITS_PER_WORD))

Note that I've omitted the 's' from the typedef name - the macros
all having no 'S', imo the type also shouldn't (or the other way
around).

> --- /dev/null
> +++ b/xen/include/asm-generic/bitops/generic-non-atomic.h
> @@ -0,0 +1,89 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * The file is based on Linux ( 6.4.0 ) header:
> + *   include/asm-generic/bitops/generic-non-atomic.h
> + * 
> + * Only functions that can be reused in Xen were left; others were removed.
> + * 
> + * Also, the following changes were done:
> + *  - it was updated the message inside #ifndef ... #endif.
> + *  - __always_inline -> always_inline to be align with definition in
> + *    xen/compiler.h.
> + *  - update function prototypes from
> + *    generic___test_and_*(unsigned long nr nr, volatile unsigned long *addr) to
> + *    generic___test_and_*(unsigned long nr, volatile void *addr) to be

What's the point of having a whopping 3 successive inner underscores?
Which btw ...

> + *    consistent with other related macros/defines.
> + *  - convert identations from tabs to spaces.
> + *  - inside generic__test_and_* use 'bitops_uint_t' instead of 'unsigned long'
> + *    to be generic.

... shrink to just 2 here.

Oh, and there's no generic__test_bit(), but just test_bit() in a separate
header.

> + */
> +
> +#ifndef __ASM_GENERIC_BITOPS_GENERIC_NON_ATOMIC_H
> +#define __ASM_GENERIC_BITOPS_GENERIC_NON_ATOMIC_H
> +
> +#include <xen/compiler.h>
> +
> +#include <asm-generic/bitops/bitops-bits.h>
> +
> +#ifndef XEN_BITOPS_H
> +#error only <xen/bitops.h> can be included directly
> +#endif
> +
> +/*
> + * Generic definitions for bit operations, should not be used in regular code
> + * directly.
> + */
> +
> +/**
> + * generic___test_and_set_bit - Set a bit and return its old value
> + * @nr: Bit to set
> + * @addr: Address to count from
> + *
> + * This operation is non-atomic and can be reordered.
> + * If two examples of this operation race, one can appear to succeed
> + * but actually fail.  You must protect multiple accesses with a lock.
> + */
> +static always_inline bool
> +generic___test_and_set_bit(unsigned long nr, volatile void *addr)
> +{
> +    bitops_uint_t mask = BITOP_MASK(nr);
> +    bitops_uint_t *p = ((bitops_uint_t *)addr) + BITOP_WORD(nr);

If you cast away the volatile here, what's the point of having it in
the parameter declaration? Also such a cast doesn't need an outer
pair of parentheses.

> +    bitops_uint_t old = *p;
> +
> +    *p = old | mask;
> +    return (old & mask) != 0;

Minor: The function returning bool, the "!= 0" (and then also the
parentheses) could be omitted.

> --- /dev/null
> +++ b/xen/include/asm-generic/bitops/test-bit.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_GENERIC_BITOPS_TESTBIT_H_
> +#define _ASM_GENERIC_BITOPS_TESTBIT_H_
> +
> +#include <asm-generic/bitops/bitops-bits.h>
> +
> +/**
> + * test_bit - Determine whether a bit is set
> + * @nr: bit number to test
> + * @addr: Address to start counting from
> + */
> +static inline int test_bit(int nr, const volatile void *addr)
> +{
> +    const volatile bitops_uint_t *p = addr;
> +    return 1 & (p[BITOP_WORD(nr)] >> (nr & (BITOP_BITS_PER_WORD - 1)));
> +}

Interestingly you don't lose the volatile here. Overall the way this
is written would likely benefit the other functions too. There's no
cast needed here and thus also not there.

Jan
Oleksii Kurochko March 21, 2024, 9:27 a.m. UTC | #2
On Wed, 2024-03-20 at 16:30 +0100, Jan Beulich wrote:
> On 15.03.2024 19:06, Oleksii Kurochko wrote:
> > The patch introduces the following generic functions:
> > * test_bit
> > * generic___test_and_set_bit
> > * generic___test_and_clear_bit
> > * generic___test_and_change_bit
> > 
> > Also, the patch introduces the following generics which are
> > used by the functions mentioned above:
> > * BITOP_BITS_PER_WORD
> > * BITOP_MASK
> > * BITOP_WORD
> > * BITOP_TYPE
> > 
> > These functions and macros can be useful for architectures
> > that don't have corresponding arch-specific instructions.
> > 
> > Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
> 
> First of all: How much syncing has there been versus Andrew's plans?
> I don't want to end up in the middle of two entirely different
> approaches to the same generalization goal.
We don't have synced about bit operations mentioned in this patch, so I
decided to follow an approach I have for them. But if Andrew's approach
is better, or it would be better to follow one approach instead of
introducing two approaches, I will be happy to rework this patch.

Andrew, what do you think about this bit ops?

> 
> > --- /dev/null
> > +++ b/xen/include/asm-generic/bitops/bitops-bits.h
> > @@ -0,0 +1,21 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _ASM_GENERIC_BITOPS_BITS_H_
> > +#define _ASM_GENERIC_BITOPS_BITS_H_
> > +
> > +#ifndef BITOP_BITS_PER_WORD
> > +#define BITOP_BITS_PER_WORD     32
> > +#endif
> > +
> > +#ifndef BITOP_MASK
> > +#define BITOP_MASK(nr)          (1U << ((nr) %
> > BITOP_BITS_PER_WORD))
> > +#endif
> > +
> > +#ifndef BITOP_WORD
> > +#define BITOP_WORD(nr)          ((nr) / BITOP_BITS_PER_WORD)
> > +#endif
> > +
> > +#ifndef BITOP_TYPE
> > +typedef uint32_t bitops_uint_t;
> > +#endif
> 
> Is it of practical use to permit overriding of one of BITOP_TYPE and
> BITOP_BITS_PER_WORD? I think both want tying together.
> 
> Is it further of any use to allow overriding of BITOP_{MASK,WORD}?
> There's minimal generalization needed to allow an override of
> BITOP_TYPE, though:
> 
> #define BITOP_MASK(nr)          ((bitop_uint_t)1 << ((nr) %
> BITOP_BITS_PER_WORD))
> 
> Note that I've omitted the 's' from the typedef name - the macros
> all having no 'S', imo the type also shouldn't (or the other way
> around).
If to generalize BITOP_MASK in way you suggest, then there is no need
to allow overriding of BITOP_{MASK,WORD}. I'll update this part.

> 
> > --- /dev/null
> > +++ b/xen/include/asm-generic/bitops/generic-non-atomic.h
> > @@ -0,0 +1,89 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * The file is based on Linux ( 6.4.0 ) header:
> > + *   include/asm-generic/bitops/generic-non-atomic.h
> > + * 
> > + * Only functions that can be reused in Xen were left; others were
> > removed.
> > + * 
> > + * Also, the following changes were done:
> > + *  - it was updated the message inside #ifndef ... #endif.
> > + *  - __always_inline -> always_inline to be align with definition
> > in
> > + *    xen/compiler.h.
> > + *  - update function prototypes from
> > + *    generic___test_and_*(unsigned long nr nr, volatile unsigned
> > long *addr) to
> > + *    generic___test_and_*(unsigned long nr, volatile void *addr)
> > to be
> 
> What's the point of having a whopping 3 successive inner underscores?
> Which btw ...
> 
> > + *    consistent with other related macros/defines.
> > + *  - convert identations from tabs to spaces.
> > + *  - inside generic__test_and_* use 'bitops_uint_t' instead of
> > 'unsigned long'
> > + *    to be generic.
> 
> ... shrink to just 2 here.
> 
> Oh, and there's no generic__test_bit(), but just test_bit() in a
> separate
> header.
I just wanted to show that it is a generic version of __test_and_*
operations. If there is no sense in that, I am okay to update function
names.

~ Oleksii
> 
> > + */
> > +
> > +#ifndef __ASM_GENERIC_BITOPS_GENERIC_NON_ATOMIC_H
> > +#define __ASM_GENERIC_BITOPS_GENERIC_NON_ATOMIC_H
> > +
> > +#include <xen/compiler.h>
> > +
> > +#include <asm-generic/bitops/bitops-bits.h>
> > +
> > +#ifndef XEN_BITOPS_H
> > +#error only <xen/bitops.h> can be included directly
> > +#endif
> > +
> > +/*
> > + * Generic definitions for bit operations, should not be used in
> > regular code
> > + * directly.
> > + */
> > +
> > +/**
> > + * generic___test_and_set_bit - Set a bit and return its old value
> > + * @nr: Bit to set
> > + * @addr: Address to count from
> > + *
> > + * This operation is non-atomic and can be reordered.
> > + * If two examples of this operation race, one can appear to
> > succeed
> > + * but actually fail.  You must protect multiple accesses with a
> > lock.
> > + */
> > +static always_inline bool
> > +generic___test_and_set_bit(unsigned long nr, volatile void *addr)
> > +{
> > +    bitops_uint_t mask = BITOP_MASK(nr);
> > +    bitops_uint_t *p = ((bitops_uint_t *)addr) + BITOP_WORD(nr);
> 
> If you cast away the volatile here, what's the point of having it in
> the parameter declaration? Also such a cast doesn't need an outer
> pair of parentheses.
> 
> > +    bitops_uint_t old = *p;
> > +
> > +    *p = old | mask;
> > +    return (old & mask) != 0;
> 
> Minor: The function returning bool, the "!= 0" (and then also the
> parentheses) could be omitted.
> 
> > --- /dev/null
> > +++ b/xen/include/asm-generic/bitops/test-bit.h
> > @@ -0,0 +1,18 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _ASM_GENERIC_BITOPS_TESTBIT_H_
> > +#define _ASM_GENERIC_BITOPS_TESTBIT_H_
> > +
> > +#include <asm-generic/bitops/bitops-bits.h>
> > +
> > +/**
> > + * test_bit - Determine whether a bit is set
> > + * @nr: bit number to test
> > + * @addr: Address to start counting from
> > + */
> > +static inline int test_bit(int nr, const volatile void *addr)
> > +{
> > +    const volatile bitops_uint_t *p = addr;
> > +    return 1 & (p[BITOP_WORD(nr)] >> (nr & (BITOP_BITS_PER_WORD -
> > 1)));
> > +}
> 
> Interestingly you don't lose the volatile here. Overall the way this
> is written would likely benefit the other functions too. There's no
> cast needed here and thus also not there.
> 
> Jan
diff mbox series

Patch

diff --git a/xen/include/asm-generic/bitops/bitops-bits.h b/xen/include/asm-generic/bitops/bitops-bits.h
new file mode 100644
index 0000000000..4ece2affd6
--- /dev/null
+++ b/xen/include/asm-generic/bitops/bitops-bits.h
@@ -0,0 +1,21 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_GENERIC_BITOPS_BITS_H_
+#define _ASM_GENERIC_BITOPS_BITS_H_
+
+#ifndef BITOP_BITS_PER_WORD
+#define BITOP_BITS_PER_WORD     32
+#endif
+
+#ifndef BITOP_MASK
+#define BITOP_MASK(nr)          (1U << ((nr) % BITOP_BITS_PER_WORD))
+#endif
+
+#ifndef BITOP_WORD
+#define BITOP_WORD(nr)          ((nr) / BITOP_BITS_PER_WORD)
+#endif
+
+#ifndef BITOP_TYPE
+typedef uint32_t bitops_uint_t;
+#endif
+
+#endif /* _ASM_GENERIC_BITOPS_BITS_H_ */
diff --git a/xen/include/asm-generic/bitops/generic-non-atomic.h b/xen/include/asm-generic/bitops/generic-non-atomic.h
new file mode 100644
index 0000000000..02d5721bfe
--- /dev/null
+++ b/xen/include/asm-generic/bitops/generic-non-atomic.h
@@ -0,0 +1,89 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * The file is based on Linux ( 6.4.0 ) header:
+ *   include/asm-generic/bitops/generic-non-atomic.h
+ * 
+ * Only functions that can be reused in Xen were left; others were removed.
+ * 
+ * Also, the following changes were done:
+ *  - it was updated the message inside #ifndef ... #endif.
+ *  - __always_inline -> always_inline to be align with definition in
+ *    xen/compiler.h.
+ *  - update function prototypes from
+ *    generic___test_and_*(unsigned long nr nr, volatile unsigned long *addr) to
+ *    generic___test_and_*(unsigned long nr, volatile void *addr) to be
+ *    consistent with other related macros/defines.
+ *  - convert identations from tabs to spaces.
+ *  - inside generic__test_and_* use 'bitops_uint_t' instead of 'unsigned long'
+ *    to be generic.
+ */
+
+#ifndef __ASM_GENERIC_BITOPS_GENERIC_NON_ATOMIC_H
+#define __ASM_GENERIC_BITOPS_GENERIC_NON_ATOMIC_H
+
+#include <xen/compiler.h>
+
+#include <asm-generic/bitops/bitops-bits.h>
+
+#ifndef XEN_BITOPS_H
+#error only <xen/bitops.h> can be included directly
+#endif
+
+/*
+ * Generic definitions for bit operations, should not be used in regular code
+ * directly.
+ */
+
+/**
+ * generic___test_and_set_bit - Set a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic and can be reordered.
+ * If two examples of this operation race, one can appear to succeed
+ * but actually fail.  You must protect multiple accesses with a lock.
+ */
+static always_inline bool
+generic___test_and_set_bit(unsigned long nr, volatile void *addr)
+{
+    bitops_uint_t mask = BITOP_MASK(nr);
+    bitops_uint_t *p = ((bitops_uint_t *)addr) + BITOP_WORD(nr);
+    bitops_uint_t old = *p;
+
+    *p = old | mask;
+    return (old & mask) != 0;
+}
+
+/**
+ * generic___test_and_clear_bit - Clear a bit and return its old value
+ * @nr: Bit to clear
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic and can be reordered.
+ * If two examples of this operation race, one can appear to succeed
+ * but actually fail.  You must protect multiple accesses with a lock.
+ */
+static always_inline bool
+generic___test_and_clear_bit(bitops_uint_t nr, volatile void *addr)
+{
+    bitops_uint_t mask = BITOP_MASK(nr);
+    bitops_uint_t *p = ((bitops_uint_t *)addr) + BITOP_WORD(nr);
+    bitops_uint_t old = *p;
+
+    *p = old & ~mask;
+    return (old & mask) != 0;
+}
+
+/* WARNING: non atomic and it can be reordered! */
+static always_inline bool
+generic___test_and_change_bit(unsigned long nr, volatile void *addr)
+{
+    bitops_uint_t mask = BITOP_MASK(nr);
+    bitops_uint_t *p = ((bitops_uint_t *)addr) + BITOP_WORD(nr);
+    bitops_uint_t old = *p;
+
+    *p = old ^ mask;
+    return (old & mask) != 0;
+}
+
+#endif /* __ASM_GENERIC_BITOPS_GENERIC_NON_ATOMIC_H */
diff --git a/xen/include/asm-generic/bitops/test-bit.h b/xen/include/asm-generic/bitops/test-bit.h
new file mode 100644
index 0000000000..6fb414d808
--- /dev/null
+++ b/xen/include/asm-generic/bitops/test-bit.h
@@ -0,0 +1,18 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_GENERIC_BITOPS_TESTBIT_H_
+#define _ASM_GENERIC_BITOPS_TESTBIT_H_
+
+#include <asm-generic/bitops/bitops-bits.h>
+
+/**
+ * test_bit - Determine whether a bit is set
+ * @nr: bit number to test
+ * @addr: Address to start counting from
+ */
+static inline int test_bit(int nr, const volatile void *addr)
+{
+    const volatile bitops_uint_t *p = addr;
+    return 1 & (p[BITOP_WORD(nr)] >> (nr & (BITOP_BITS_PER_WORD - 1)));
+}
+
+#endif /* _ASM_GENERIC_BITOPS_TESTBIT_H_ */