mbox series

[v3,0/7] Introduce Sequence Number Ops

Message ID cover.1612314468.git.skhan@linuxfoundation.org
Headers show
Series Introduce Sequence Number Ops | expand

Message

Shuah Khan Feb. 3, 2021, 6:11 p.m. UTC
Sequence Number api provides interfaces for unsigned atomic up counters.

There are a number of atomic_t usages in the kernel where atomic_t api
is used for counting sequence numbers and other statistical counters.
Several of these usages, convert atomic_read() and atomic_inc_return()
return values to unsigned. Introducing sequence number ops supports
these use-cases with a standard core-api.

Sequence Number ops provide interfaces to initialize, increment and get
the sequence number. These ops also check for overflow and log message to
indicate when overflow occurs. This check is intended to help catch cases
where overflow could lead to problems.

Since v2:
- Uses atomic_inc_return() for incrementing the sequence number.
- No longer uses atomic_read()

Shuah Khan (7):
  seqnum_ops: Introduce Sequence Number Ops
  selftests: lib:test_seqnum_ops: add new test for seqnum_ops
  drivers/acpi: convert seqno to use seqnum_ops
  drivers/acpi/apei: convert seqno to seqnum_ops
  drivers/staging/rtl8723bs: convert event_seq to use seqnum_ops
  drivers/staging/rtl8188eu: convert event_seq to use seqnum_ops
  kobject: convert uevent_seqnum to seqnum_ops

 Documentation/core-api/index.rst              |   1 +
 Documentation/core-api/seqnum_ops.rst         |  62 ++++++++
 MAINTAINERS                                   |   8 ++
 drivers/acpi/acpi_extlog.c                    |   8 +-
 drivers/acpi/apei/ghes.c                      |   8 +-
 drivers/staging/rtl8188eu/core/rtw_mlme_ext.c |  23 ++-
 .../staging/rtl8188eu/include/rtw_mlme_ext.h  |   3 +-
 drivers/staging/rtl8723bs/core/rtw_cmd.c      |   3 +-
 drivers/staging/rtl8723bs/core/rtw_mlme_ext.c |  33 +++--
 drivers/staging/rtl8723bs/include/rtw_cmd.h   |   3 +-
 .../staging/rtl8723bs/include/rtw_mlme_ext.h  |   3 +-
 include/linux/kobject.h                       |   3 +-
 include/linux/seqnum_ops.h                    | 131 +++++++++++++++++
 kernel/ksysfs.c                               |   3 +-
 lib/Kconfig                                   |   9 ++
 lib/Makefile                                  |   1 +
 lib/kobject_uevent.c                          |   9 +-
 lib/test_seqnum_ops.c                         | 133 ++++++++++++++++++
 tools/testing/selftests/lib/Makefile          |   1 +
 tools/testing/selftests/lib/config            |   1 +
 .../testing/selftests/lib/test_seqnum_ops.sh  |  10 ++
 21 files changed, 423 insertions(+), 33 deletions(-)
 create mode 100644 Documentation/core-api/seqnum_ops.rst
 create mode 100644 include/linux/seqnum_ops.h
 create mode 100644 lib/test_seqnum_ops.c
 create mode 100755 tools/testing/selftests/lib/test_seqnum_ops.sh

Comments

Peter Zijlstra Feb. 4, 2021, 9:20 a.m. UTC | #1
On Wed, Feb 03, 2021 at 11:11:57AM -0700, Shuah Khan wrote:
> +static inline u32 seqnum32_inc(struct seqnum32 *seq)
> +{
> +	atomic_t val = ATOMIC_INIT(seq->seqnum);
> +
> +	seq->seqnum = (u32) atomic_inc_return(&val);
> +	if (seq->seqnum >= UINT_MAX)
> +		pr_info("Sequence Number overflow %u detected\n",
> +			seq->seqnum);
> +	return seq->seqnum;
> +}

What kind of broken garbage is that?
Greg KH Feb. 5, 2021, 9:58 a.m. UTC | #2
On Wed, Feb 03, 2021 at 11:11:57AM -0700, Shuah Khan wrote:
> +static inline u32 seqnum32_inc(struct seqnum32 *seq)
> +{
> +	atomic_t val = ATOMIC_INIT(seq->seqnum);
> +
> +	seq->seqnum = (u32) atomic_inc_return(&val);
> +	if (seq->seqnum >= UINT_MAX)
> +		pr_info("Sequence Number overflow %u detected\n",
> +			seq->seqnum);
> +	return seq->seqnum;

As Peter points out, this is doing doing what you think it is doing :(

Why do you not just have seq->seqnum be a real atomic variable?  Trying
to switch to/from one like this does not work as there is no
"atomic-ness" happening here at all.

Oh, and checkpatch should have complained about the extra ' ' in your
cast :)

thanks,

greg k-h
Shuah Khan Feb. 5, 2021, 8:03 p.m. UTC | #3
On 2/5/21 2:58 AM, Greg KH wrote:
> On Wed, Feb 03, 2021 at 11:11:57AM -0700, Shuah Khan wrote:

>> +static inline u32 seqnum32_inc(struct seqnum32 *seq)

>> +{

>> +	atomic_t val = ATOMIC_INIT(seq->seqnum);

>> +

>> +	seq->seqnum = (u32) atomic_inc_return(&val);

>> +	if (seq->seqnum >= UINT_MAX)

>> +		pr_info("Sequence Number overflow %u detected\n",

>> +			seq->seqnum);

>> +	return seq->seqnum;

> 

> As Peter points out, this is doing doing what you think it is doing :(

> 

> Why do you not just have seq->seqnum be a real atomic variable?  Trying

> to switch to/from one like this does not work as there is no

> "atomic-ness" happening here at all.

> 


Yes. This is sloppy on my part. As Peter and Rafael also pointed. I have
to start paying more attention to my inner voice.

thanks,
-- Shuah
Tony Luck Feb. 5, 2021, 10:16 p.m. UTC | #4
On Fri, Feb 05, 2021 at 01:03:18PM -0700, Shuah Khan wrote:
> On 2/5/21 2:58 AM, Greg KH wrote:
> > On Wed, Feb 03, 2021 at 11:11:57AM -0700, Shuah Khan wrote:
> > > +static inline u32 seqnum32_inc(struct seqnum32 *seq)
> > > +{
> > > +	atomic_t val = ATOMIC_INIT(seq->seqnum);
> > > +
> > > +	seq->seqnum = (u32) atomic_inc_return(&val);
> > > +	if (seq->seqnum >= UINT_MAX)
> > > +		pr_info("Sequence Number overflow %u detected\n",
> > > +			seq->seqnum);
> > > +	return seq->seqnum;
> > 
> > As Peter points out, this is doing doing what you think it is doing :(
> > 
> > Why do you not just have seq->seqnum be a real atomic variable?  Trying
> > to switch to/from one like this does not work as there is no
> > "atomic-ness" happening here at all.
> > 
> 
> Yes. This is sloppy on my part. As Peter and Rafael also pointed. I have
> to start paying more attention to my inner voice.

What's the end goal with this sequence number type?

Apart from the functional issues with this code already pointed
out, it doesn't seem overly useful to just print the *value* of
the sequence number when it hits the max value.  It might be
more helpful if each instance had a seq->name field that is
used here to tell the user *which* user of sequence numbers
had seen the wrap arounnd.

But that just begs the question of what should users of sequence
numbers do when wrap around occurs? Any user that can run through
sequence numbers at greater than 10 Hz is going to cause a problem
for systems that stay running for years.

Maybe you should force users to code for wraparound by initializing
to something like 0xffffff00 so that they all get a wraparound event
quickly, rather than slowly, (same as was done with jiffies)?
Randy Dunlap Feb. 8, 2021, 3:55 a.m. UTC | #5
Hi--
Comments are inline.

On 2/3/21 10:11 AM, Shuah Khan wrote:
> Sequence Number api provides interfaces for unsigned atomic up counters.

> 

> There are a number of atomic_t usages in the kernel where atomic_t api

> is used for counting sequence numbers and other statistical counters.

> Several of these usages, convert atomic_read() and atomic_inc_return()

> return values to unsigned. Introducing sequence number ops supports

> these use-cases with a standard core-api.

> 

> Sequence Number ops provide interfaces to initialize, increment and get

> the sequence number. These ops also check for overflow and log message to

> indicate when overflow occurs.

> 

> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>

> ---

>  Documentation/core-api/index.rst      |   1 +

>  Documentation/core-api/seqnum_ops.rst |  53 ++++++++++

>  MAINTAINERS                           |   7 ++

>  include/linux/seqnum_ops.h            | 129 +++++++++++++++++++++++++

>  lib/Kconfig                           |   9 ++

>  lib/Makefile                          |   1 +

>  lib/test_seqnum_ops.c                 | 133 ++++++++++++++++++++++++++

>  7 files changed, 333 insertions(+)

>  create mode 100644 Documentation/core-api/seqnum_ops.rst

>  create mode 100644 include/linux/seqnum_ops.h

>  create mode 100644 lib/test_seqnum_ops.c



> diff --git a/Documentation/core-api/seqnum_ops.rst b/Documentation/core-api/seqnum_ops.rst

> new file mode 100644

> index 000000000000..ed4eba394799

> --- /dev/null

> +++ b/Documentation/core-api/seqnum_ops.rst

> @@ -0,0 +1,53 @@

> +.. SPDX-License-Identifier: GPL-2.0

> +

> +.. include:: <isonum.txt>

> +

> +.. _seqnum_ops:

> +

> +==========================

> +Sequence Number Operations

> +==========================

> +

> +:Author: Shuah Khan

> +:Copyright: |copy| 2021, The Linux Foundation

> +:Copyright: |copy| 2021, Shuah Khan <skhan@linuxfoundation.org>

> +

> +Sequence Number api provides interfaces for unsigned up counters.


                   API

> +

> +Sequence Number Ops

> +===================

> +

> +seqnum32 and seqnum64 types support implementing unsigned up counters. ::

> +

> +        struct seqnum32 { u32 seqnum; };

> +        struct seqnum64 { u64 seqnum; };

> +

> +Initializers

> +------------

> +

> +Interfaces for initializing sequence numbers. ::

> +

> +        #define SEQNUM_INIT(i)    { .seqnum = i }

> +        seqnum32_init(seqnum, val)

> +        seqnum64_init(seqnum, val)

> +

> +Increment interface

> +-------------------

> +

> +Increments sequence number and returns the new value. Checks for overflow

> +conditions and logs message when overflow occurs. This check is intended

> +to help catch cases where overflow could lead to problems. ::

> +

> +        seqnum32_inc(seqnum): Calls atomic_inc_return(seqnum).

> +        seqnum64_inc(seqnum): Calls atomic64_inc_return(seqnum).

> +

> +Return/get value interface

> +--------------------------

> +

> +Returns sequence number value. ::

> +

> +        seqnum32_get() - return seqnum value.

> +        seqnum64_get() - return seqnum value.

> +

> +.. warning::

> +        seqnum32 wraps around to INT_MIN when it overflows.

> diff --git a/MAINTAINERS b/MAINTAINERS

> index cc1e6a5ee6e6..f9fe1438a8cd 100644

> --- a/MAINTAINERS

> +++ b/MAINTAINERS

> @@ -16235,6 +16235,13 @@ S:	Maintained

>  F:	Documentation/fb/sm712fb.rst

>  F:	drivers/video/fbdev/sm712*

>  

> +SEQNUM OPS

> +M:	Shuah Khan <skhan@linuxfoundation.org>

> +L:	linux-kernel@vger.kernel.org

> +S:	Maintained

> +F:	include/linux/seqnum_ops.h

> +F:	lib/test_seqnum_ops.c

> +

>  SIMPLE FIRMWARE INTERFACE (SFI)

>  S:	Obsolete

>  W:	http://simplefirmware.org/

> diff --git a/include/linux/seqnum_ops.h b/include/linux/seqnum_ops.h

> new file mode 100644

> index 000000000000..e8d8481445d3

> --- /dev/null

> +++ b/include/linux/seqnum_ops.h

> @@ -0,0 +1,129 @@

> +/* SPDX-License-Identifier: GPL-2.0 */

> +/*

> + * seqnum_ops.h - Interfaces for unsigned atomic sequential up counters.

> + *

> + * Copyright (c) 2021 Shuah Khan <skhan@linuxfoundation.org>

> + * Copyright (c) 2021 The Linux Foundation

> + *

> + * Sequence Number functions provide support for unsgined atomic up


                                                    unsigned

> + * counters.

> + *

> + * The interface provides:

> + * seqnumu32 & seqnumu64 functions:

> + *	initialization

> + *	increment and return

> + *

> + * seqnumu32 and seqnumu64 functions leverage/use atomic*_t ops to

> + * implement support for unsigned atomic up counters.

> + *

> + * Reference and API guide:

> + *	Documentation/core-api/seqnum_ops.rst for more information.

> + */

> +

> +#ifndef __LINUX_SEQNUM_OPS_H

> +#define __LINUX_SEQNUM_OPS_H

> +

> +#include <linux/atomic.h>

> +

> +/**

> + * struct seqnum32 - Sequence number atomic counter

> + * @seqnum: atomic_t

> + *

> + **/

> +struct seqnum32 {

> +	u32 seqnum;

> +};

> +

> +#define SEQNUM_INIT(i)		{ .seqnum = i }

> +

> +/*

> + * seqnum32_init() - initialize seqnum value

> + * @seq: struct seqnum32 pointer

> + *

> + */

> +static inline void seqnum32_init(struct seqnum32 *seq, u32 val)

> +{

> +	seq->seqnum = val;

> +}

> +

> +/*

> + * seqnum32_inc() - increment seqnum value and return the new value

> + * @seq: struct seqnum32 pointer

> + *

> + * Return u32


It would be good to convert that to kernel-doc notation.

> + */

> +static inline u32 seqnum32_inc(struct seqnum32 *seq)

> +{

> +	atomic_t val = ATOMIC_INIT(seq->seqnum);

> +

> +	seq->seqnum = (u32) atomic_inc_return(&val);

> +	if (seq->seqnum >= UINT_MAX)

> +		pr_info("Sequence Number overflow %u detected\n",

> +			seq->seqnum);

> +	return seq->seqnum;

> +}

> +

> +/*

> + * seqnum32_get() - get seqnum value

> + * @seq: struct seqnum32 pointer

> + *

> + * Return u32

> + */

> +static inline u32 seqnum32_get(struct seqnum32 *seq)

> +{

> +	return seq->seqnum;

> +}

> +

> +/*

> + * struct seqnum64 - Sequential/Statistical atomic counter

> + * @seq: atomic64_t

> + *

> + */

> +struct seqnum64 {

> +	u64 seqnum;

> +};

> +

> +/* Add to a global include/vdso/limits.h and fix all other UINT64_MAX

> + * duplicate defines?

> + */

> +#define SEQ_UINT64_MAX	((u64)(~((u64) 0)))	/* 0xFFFFFFFFFFFFFFFF */

> +

> +/*

> + * seqnum64_init() - initialize seqnum value

> + * @seq: struct seqnum64 pointer

> + *

> + */


and kernel-doc there also.

> +static inline void seqnum64_init(struct seqnum64 *seq, u64 val)

> +{

> +	seq->seqnum = val;

> +}

> +

> +/*

> + * seqnum64_inc() - increment seqnum value and return the new value

> + * @seq: struct seqnum64 pointer

> + *

> + * Return u64

> + */

> +static inline u64 seqnum64_inc(struct seqnum64 *seq)

> +{

> +	atomic64_t val = ATOMIC_INIT(seq->seqnum);

> +

> +	seq->seqnum = (u64) atomic64_inc_return(&val);

> +	if (seq->seqnum >= SEQ_UINT64_MAX)

> +		pr_info("Sequence Number overflow %llu detected\n",

> +			seq->seqnum);

> +	return seq->seqnum;

> +}

> +

> +/*

> + * seqnum64_get() - get seqnum value

> + * @seq: struct seqnum64 pointer

> + *

> + * Return u64

> + */

> +static inline u64 seqnum64_get(struct seqnum64 *seq)

> +{

> +	return (u64) seq->seqnum;

> +}

> +

> +#endif /* __LINUX_SEQNUM_OPS_H */



> diff --git a/lib/test_seqnum_ops.c b/lib/test_seqnum_ops.c

> new file mode 100644

> index 000000000000..173278314f26

> --- /dev/null

> +++ b/lib/test_seqnum_ops.c

> @@ -0,0 +1,133 @@

> +// SPDX-License-Identifier: GPL-2.0-only

> +/*

> + * test_seqnum_ops.c - Kernel module for testing Seqnum API

> + *

> + * Copyright (c) 2021 Shuah Khan <skhan@linuxfoundation.org>

> + * Copyright (c) 2021 The Linux Foundation

> + *

> + */

> +

> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

> +

> +#include <linux/module.h>

> +#include <linux/seqnum_ops.h>

> +

>

...

> +static void test_seqnum64(void)

> +{

> +	u64 start_val = 0;

> +	struct seqnum64 seq = SEQNUM_INIT(start_val);

> +	u64 end_val;

> +

> +	end_val = seqnum64_inc(&seq);

> +	test_seqnum64_result("Test increment",

> +			     start_val, end_val, start_val+1);

> +

> +	/* Initialize sequence number to 0 */

> +	seqnum64_init(&seq, start_val);

> +	end_val = seqnum64_inc(&seq);

> +

> +	/* if seqnum642_init() works correctly end_val should be 1 */


	      seqnum64_init()
AFAICT.

> +	test_seqnum64_result("Test init", start_val, end_val, 1);

> +	/* seqnum64_get() test for seqnum value == 1 */

> +	start_val = end_val = seqnum64_get(&seq);

> +	test_seqnum64_result("Test get", start_val, end_val, 1);

> +}

> +



-- 
~Randy