DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH v1] ticketlock: ticket based to improve fairness
@ 2019-01-13 14:46 Gavin Hu
  2019-01-14  7:59 ` [dpdk-dev] [EXT] " Jerin Jacob Kollanukkaran
                   ` (3 more replies)
  0 siblings, 4 replies; 74+ messages in thread
From: Gavin Hu @ 2019-01-13 14:46 UTC (permalink / raw)
  To: dev
  Cc: thomas, jerinj, hemant.agrawal, stephen, nd,
	Honnappa.Nagarahalli, gavin.hu, Joyce Kong

From: Joyce Kong <joyce.kong@arm.com>

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time. As
shown in the following test, within same period of time, there are
threads taking locks much more times than the others.

The ticketlock gives each waiting thread a ticket and they can take the
lock one by one, first come, first serviced, this avoids starvation for
too long time and is more predictable.

*** spinlock_autotest without this patch ***
Core [0] count = 89
Core [1] count = 84
Core [2] count = 94
...
Core [208] count = 171
Core [209] count = 152
Core [210] count = 161
Core [211] count = 187

*** spinlock_autotest with this patch ***
Core [0] count = 534
Core [1] count = 533
Core [2] count = 542
...
Core [208] count = 554
Core [209] count = 556
Core [210] count = 555
Core [211] count = 551

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Signed-off-by: Gavin Hu <gavin.hu@arm.com>
---
 .../common/include/generic/rte_ticketlock.h        | 203 +++++++++++++++++++++
 lib/librte_eal/rte_eal_version.map                 |   8 +
 2 files changed, 211 insertions(+)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 000000000..9d044bb31
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Corporation
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticketlocks
+ *
+ * This file defines an API for read-write locks, which are implemented
+ * in an architecture-specific way. This kind of lock simply waits in
+ * a loop repeatedly checking until the lock becomes available.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef struct {
+	uint16_t current;
+	uint16_t next;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl);
+
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl);
+
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	i++;
+	__atomic_store_n(&tl->current, i, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->current, __ATOMIC_RELAXED) != me) {
+		__atomic_sub_fetch(&tl->next, 1, __ATOMIC_RELAXED);
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock icurrently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	return (__atomic_load_n(&tl->current, __ATOMIC_RELAXED) !=
+			__atomic_load_n(&tl->next, __ATOMIC_RELAXED));
+}
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	volatile int user; /**< core id using lock, -1 for unused */
+	volatile int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, -1, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void rte_ticketlock_recursive_init(
+					rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	tlr->user = -1;
+	tlr->count = 0;
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void rte_ticketlock_recursive_lock(
+					rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (tlr->user != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		tlr->user = id;
+	}
+	tlr->count++;
+}
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void rte_ticketlock_recursive_unlock(
+					rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		tlr->user = -1;
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int rte_ticketlock_recursive_trylock(
+					rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (tlr->user != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		tlr->user = id;
+	}
+	tlr->count++;
+	return 1;
+}
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index eb5f7b9cb..f1841effa 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -364,4 +364,12 @@ EXPERIMENTAL {
 	rte_service_may_be_active;
 	rte_socket_count;
 	rte_socket_id_by_idx;
+	rte_ticketlock_lock;
+	rte_ticketlock_unlock;
+	rte_ticketlock_islocked;
+	rte_ticketlock_trylock;
+	rte_ticketlock_recurrsive_lock;
+	rte_ticketlock_recurrsive_unlock;
+	rte_ticketlock_recurrsive_islocked;
+	rte_ticketlock_recurrsive_trylock;
 };
-- 
2.11.0

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [EXT] [PATCH v1] ticketlock: ticket based to improve fairness
  2019-01-13 14:46 [dpdk-dev] [PATCH v1] ticketlock: ticket based to improve fairness Gavin Hu
@ 2019-01-14  7:59 ` Jerin Jacob Kollanukkaran
  2019-01-14 16:57   ` Gavin Hu (Arm Technology China)
  2019-01-14 23:36 ` [dpdk-dev] " Honnappa Nagarahalli
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 74+ messages in thread
From: Jerin Jacob Kollanukkaran @ 2019-01-14  7:59 UTC (permalink / raw)
  To: gavin.hu, dev
  Cc: stephen, Honnappa.Nagarahalli, thomas, nd, joyce.kong, hemant.agrawal

On Sun, 2019-01-13 at 22:46 +0800, Gavin Hu wrote:
> -------------------------------------------------------------------
> ---
> From: Joyce Kong <joyce.kong@arm.com>
> 
> The spinlock implementation is unfair, some threads may take locks
> aggressively while leaving the other threads starving for long time.
> As
> shown in the following test, within same period of time, there are
> threads taking locks much more times than the others.
> 
> The ticketlock gives each waiting thread a ticket and they can take
> the
> lock one by one, first come, first serviced, this avoids starvation
> for
> too long time and is more predictable.
> 
> *** spinlock_autotest without this patch ***
> Core [0] count = 89
> Core [1] count = 84
> Core [2] count = 94
> ...
> Core [208] count = 171
> Core [209] count = 152
> Core [210] count = 161
> Core [211] count = 187
> 
> *** spinlock_autotest with this patch ***

This comment needs to be updated as it is new API now
and we need add test case for the same. I think,
making modification to exiting spinlock test case would be fine.


> Core [0] count = 534
> Core [1] count = 533
> Core [2] count = 542
> ...
> Core [208] count = 554
> Core [209] count = 556
> Core [210] count = 555
> Core [211] count = 551
> 
> Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> Signed-off-by: Gavin Hu <gavin.hu@arm.com>
> ---
> 

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [EXT] [PATCH v1] ticketlock: ticket based to improve fairness
  2019-01-14  7:59 ` [dpdk-dev] [EXT] " Jerin Jacob Kollanukkaran
@ 2019-01-14 16:57   ` Gavin Hu (Arm Technology China)
  0 siblings, 0 replies; 74+ messages in thread
From: Gavin Hu (Arm Technology China) @ 2019-01-14 16:57 UTC (permalink / raw)
  To: jerinj, dev
  Cc: stephen, Honnappa Nagarahalli, thomas, nd,
	Joyce Kong (Arm Technology China),
	hemant.agrawal



> -----Original Message-----
> From: Jerin Jacob Kollanukkaran <jerinj@marvell.com>
> Sent: Monday, January 14, 2019 4:00 PM
> To: Gavin Hu (Arm Technology China) <Gavin.Hu@arm.com>;
> dev@dpdk.org
> Cc: stephen@networkplumber.org; Honnappa Nagarahalli
> <Honnappa.Nagarahalli@arm.com>; thomas@monjalon.net; nd
> <nd@arm.com>; Joyce Kong (Arm Technology China)
> <Joyce.Kong@arm.com>; hemant.agrawal@nxp.com
> Subject: Re: [EXT] [PATCH v1] ticketlock: ticket based to improve fairness
> 
> On Sun, 2019-01-13 at 22:46 +0800, Gavin Hu wrote:
> > -------------------------------------------------------------------
> > ---
> > From: Joyce Kong <joyce.kong@arm.com>
> >
> > The spinlock implementation is unfair, some threads may take locks
> > aggressively while leaving the other threads starving for long time.
> > As
> > shown in the following test, within same period of time, there are
> > threads taking locks much more times than the others.
> >
> > The ticketlock gives each waiting thread a ticket and they can take
> > the
> > lock one by one, first come, first serviced, this avoids starvation
> > for
> > too long time and is more predictable.
> >
> > *** spinlock_autotest without this patch ***
> > Core [0] count = 89
> > Core [1] count = 84
> > Core [2] count = 94
> > ...
> > Core [208] count = 171
> > Core [209] count = 152
> > Core [210] count = 161
> > Core [211] count = 187
> >
> > *** spinlock_autotest with this patch ***
> 
> This comment needs to be updated as it is new API now
> and we need add test case for the same. I think,
> making modification to exiting spinlock test case would be fine.

Thanks for you comments, we will do it and send a v2.

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v1] ticketlock: ticket based to improve fairness
  2019-01-13 14:46 [dpdk-dev] [PATCH v1] ticketlock: ticket based to improve fairness Gavin Hu
  2019-01-14  7:59 ` [dpdk-dev] [EXT] " Jerin Jacob Kollanukkaran
@ 2019-01-14 23:36 ` Honnappa Nagarahalli
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 2/2] " Joyce Kong
  3 siblings, 0 replies; 74+ messages in thread
From: Honnappa Nagarahalli @ 2019-01-14 23:36 UTC (permalink / raw)
  To: Gavin Hu (Arm Technology China), dev
  Cc: thomas, jerinj, hemant.agrawal, stephen, nd,
	Gavin Hu (Arm Technology China),
	Joyce Kong (Arm Technology China),
	nd

> Subject: [PATCH v1] ticketlock: ticket based to improve fairness
> 
> From: Joyce Kong <joyce.kong@arm.com>
> 
> The spinlock implementation is unfair, some threads may take locks
> aggressively while leaving the other threads starving for long time. As shown
> in the following test, within same period of time, there are threads taking
> locks much more times than the others.
> 
> The ticketlock gives each waiting thread a ticket and they can take the lock
> one by one, first come, first serviced, this avoids starvation for too long time
> and is more predictable.
> 
> *** spinlock_autotest without this patch *** Core [0] count = 89 Core [1]
> count = 84 Core [2] count = 94 ...
> Core [208] count = 171
> Core [209] count = 152
> Core [210] count = 161
> Core [211] count = 187
> 
> *** spinlock_autotest with this patch *** Core [0] count = 534 Core [1] count
> = 533 Core [2] count = 542 ...
> Core [208] count = 554
> Core [209] count = 556
> Core [210] count = 555
> Core [211] count = 551
> 
> Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> Signed-off-by: Gavin Hu <gavin.hu@arm.com>
> ---
>  .../common/include/generic/rte_ticketlock.h        | 203
> +++++++++++++++++++++
>  lib/librte_eal/rte_eal_version.map                 |   8 +
>  2 files changed, 211 insertions(+)
>  create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h
> 
> diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> new file mode 100644
> index 000000000..9d044bb31
> --- /dev/null
> +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> @@ -0,0 +1,203 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2018-2019 Arm Corporation  */
                                                              ^^^^^^^^^^
Should be 'Limited'

> +
> +#ifndef _RTE_TICKETLOCK_H_
> +#define _RTE_TICKETLOCK_H_
> +
> +/**
> + * @file
> + *
> + * RTE ticketlocks
> + *
> + * This file defines an API for read-write locks, which are implemented
> + * in an architecture-specific way. This kind of lock simply waits in
> + * a loop repeatedly checking until the lock becomes available.
Needs update for ticket lock

> + *
> + * All locks must be initialised before use, and only initialised once.
> + *
> + */
> +
> +#include <rte_lcore.h>
> +#include <rte_common.h>
> +#include <rte_pause.h>
> +
> +/**
> + * The rte_ticketlock_t type.
> + */
> +typedef struct {
> +	uint16_t current;
> +	uint16_t next;
Any reason these are 16b?
Use 'volatile' for consistency with rte_spinlock_t? (though I do not prefer to use 'volatile' in general)

> +} rte_ticketlock_t;
> +
> +/**
> + * A static ticketlock initializer.
> + */
> +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> +
> +/**
> + * Initialize the ticketlock to an unlocked state.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_init(rte_ticketlock_t *tl) {
> +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED); }
> +
> +/**
> + * Take the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_lock(rte_ticketlock_t *tl);
> +
> +static inline __rte_experimental void
> +rte_ticketlock_lock(rte_ticketlock_t *tl) {
> +	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
> +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> +		rte_pause();
> +}
Do we need to place the APIs under RTE_FORCE_INTRINSICS flag? I see that x86 has implementation which does not use intrinsics (lib/librte_eal/common/include/arch/x86/rte_spinlock.h).

> +
> +/**
> + * Release the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_unlock(rte_ticketlock_t *tl);
> +
> +static inline __rte_experimental void
> +rte_ticketlock_unlock(rte_ticketlock_t *tl) {
> +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> +	i++;
> +	__atomic_store_n(&tl->current, i, __ATOMIC_RELEASE); }
> +
> +/**
> + * Try to take the lock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + * @return
> + *   1 if the lock is successfully taken; 0 otherwise.
> + */
> +static inline __rte_experimental int
> +rte_ticketlock_trylock(rte_ticketlock_t *tl) {
> +	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
> +	while (__atomic_load_n(&tl->current, __ATOMIC_RELAXED) != me) {
What happens if other threads incremented 'tl->next' here?

> +		__atomic_sub_fetch(&tl->next, 1, __ATOMIC_RELAXED);
> +		return 0;
> +	}
> +
> +	return 1;
> +}
IMO, this implementation needs to be changed. We should try to take the lock if tl->next and tl->current are equal. Then tl->next should be incremented using a CAS (which makes sure no one else has taken the lock in the meanwhile)

> +
> +/**
> + * Test if the lock is taken.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + * @return
> + *   1 if the lock icurrently taken; 0 otherwise.
> + */
> +static inline __rte_experimental int
> +rte_ticketlock_is_locked(rte_ticketlock_t *tl) {
> +	return (__atomic_load_n(&tl->current, __ATOMIC_RELAXED) !=
Load of tl->current should be __ATOMIC_ACQUIRE

> +			__atomic_load_n(&tl->next, __ATOMIC_RELAXED)); }
> +
> +/**
> + * The rte_ticketlock_recursive_t type.
> + */
> +typedef struct {
> +	rte_ticketlock_t tl; /**< the actual ticketlock */
> +	volatile int user; /**< core id using lock, -1 for unused */
> +	volatile int count; /**< count of time this lock has been called */ }
'count' can be 'unsigned int'?

> +rte_ticketlock_recursive_t;
> +
> +/**
> + * A static recursive ticketlock initializer.
> + */
> +#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER
> +{RTE_TICKETLOCK_INITIALIZER, -1, 0}
> +
> +/**
> + * Initialize the recursive ticketlock to an unlocked state.
> + *
> + * @param tlr
> + *   A pointer to the recursive ticketlock.
> + */
> +static inline __rte_experimental void rte_ticketlock_recursive_init(
> +					rte_ticketlock_recursive_t *tlr)
> +{
> +	rte_ticketlock_init(&tlr->tl);
> +	tlr->user = -1;
> +	tlr->count = 0;
> +}
> +
> +/**
> + * Take the recursive ticketlock.
> + *
> + * @param tlr
> + *   A pointer to the recursive ticketlock.
> + */
> +static inline __rte_experimental void rte_ticketlock_recursive_lock(
> +					rte_ticketlock_recursive_t *tlr)
> +{
> +	int id = rte_gettid();
> +
> +	if (tlr->user != id) {
> +		rte_ticketlock_lock(&tlr->tl);
> +		tlr->user = id;
> +	}
> +	tlr->count++;
> +}
> +/**
> + * Release the recursive ticketlock.
> + *
> + * @param tlr
> + *   A pointer to the recursive ticketlock.
> + */
> +static inline __rte_experimental void rte_ticketlock_recursive_unlock(
> +					rte_ticketlock_recursive_t *tlr)
> +{
> +	if (--(tlr->count) == 0) {
> +		tlr->user = -1;
> +		rte_ticketlock_unlock(&tlr->tl);
> +	}
> +
Minor comment. Extra line.

> +}
> +
> +/**
> + * Try to take the recursive lock.
> + *
> + * @param tlr
> + *   A pointer to the recursive ticketlock.
> + * @return
> + *   1 if the lock is successfully taken; 0 otherwise.
> + */
> +static inline __rte_experimental int rte_ticketlock_recursive_trylock(
> +					rte_ticketlock_recursive_t *tlr)
> +{
> +	int id = rte_gettid();
> +
> +	if (tlr->user != id) {
> +		if (rte_ticketlock_trylock(&tlr->tl) == 0)
> +			return 0;
> +		tlr->user = id;
> +	}
> +	tlr->count++;
> +	return 1;
> +}
> +
> +#endif /* _RTE_TICKETLOCK_H_ */
> diff --git a/lib/librte_eal/rte_eal_version.map
> b/lib/librte_eal/rte_eal_version.map
> index eb5f7b9cb..f1841effa 100644
> --- a/lib/librte_eal/rte_eal_version.map
> +++ b/lib/librte_eal/rte_eal_version.map
> @@ -364,4 +364,12 @@ EXPERIMENTAL {
>  	rte_service_may_be_active;
>  	rte_socket_count;
>  	rte_socket_id_by_idx;
> +	rte_ticketlock_lock;
> +	rte_ticketlock_unlock;
> +	rte_ticketlock_islocked;
> +	rte_ticketlock_trylock;
> +	rte_ticketlock_recurrsive_lock;
> +	rte_ticketlock_recurrsive_unlock;
> +	rte_ticketlock_recurrsive_islocked;
> +	rte_ticketlock_recurrsive_trylock;
These are all 'inline' functions, they do not need to be versioned.

>  };
> --
> 2.11.0

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v2 1/2] ticketlock: ticket based to improve fairness
  2019-01-13 14:46 [dpdk-dev] [PATCH v1] ticketlock: ticket based to improve fairness Gavin Hu
  2019-01-14  7:59 ` [dpdk-dev] [EXT] " Jerin Jacob Kollanukkaran
  2019-01-14 23:36 ` [dpdk-dev] " Honnappa Nagarahalli
@ 2019-01-18  9:15 ` Joyce Kong
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 0/2] ticketlock: implement ticketlock and add test case Joyce Kong
                     ` (13 more replies)
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 2/2] " Joyce Kong
  3 siblings, 14 replies; 74+ messages in thread
From: Joyce Kong @ 2019-01-18  9:15 UTC (permalink / raw)
  To: dev
  Cc: OSS-DPDK-dev, nd, thomas, jerin.jacob, stephen,
	honnappa.nagarahalli, gavin.hu, stable, Joyce kong

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.
As shown in the following test, within same period of time, there
are threads taking locks much more times than the others.

The ticketlock gives each waiting thread a ticket and they can take
the lock one by one, first come, first serviced, this avoids starvation
for too long time and is more predictable.

*** ticketlock_autotest with this patch ***
    Core [0] count = 496
    Core [1] count = 495
    Core [2] count = 498
    ...
    Core [209] count = 488
    Core [210] count = 490
    Core [211] count = 474

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Phil Yang <phil.yang@arm.com>
---
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 198 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 4 files changed, 201 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 87d8c45..99e948b 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..2979528
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticketlocks
+ *
+ * This file defines an API for read-write locks, which are implemented
+ * in an architecture-specific way. This kind of lock simply waits in
+ * a loop repeatedly checking until the lock becomes available.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef struct {
+	uint16_t current;
+	uint16_t next;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	i++;
+	__atomic_store_n(&tl->current, i, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->current, __ATOMIC_RELAXED) != me) {
+		__atomic_sub_fetch(&tl->next, 1, __ATOMIC_RELAXED);
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock icurrently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	return (__atomic_load_n(&tl->current, __ATOMIC_RELAXED) !=
+			__atomic_load_n(&tl->next, __ATOMIC_RELAXED));
+}
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	volatile int user; /**< core id using lock, -1 for unused */
+	volatile int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, -1, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void rte_ticketlock_recursive_init(
+					rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	tlr->user = -1;
+	tlr->count = 0;
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void rte_ticketlock_recursive_lock(
+					rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (tlr->user != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		tlr->user = id;
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void rte_ticketlock_recursive_unlock(
+					rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		tlr->user = -1;
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int rte_ticketlock_recursive_trylock(
+					rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (tlr->user != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		tlr->user = id;
+	}
+	tlr->count++;
+	return 1;
+}
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 2a10d57..23f9416 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -98,6 +98,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v2 2/2] test/ticketlock: add ticket lock test case
  2019-01-13 14:46 [dpdk-dev] [PATCH v1] ticketlock: ticket based to improve fairness Gavin Hu
                   ` (2 preceding siblings ...)
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
@ 2019-01-18  9:15 ` Joyce Kong
  3 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-01-18  9:15 UTC (permalink / raw)
  To: dev
  Cc: OSS-DPDK-dev, nd, thomas, jerin.jacob, stephen,
	honnappa.nagarahalli, gavin.hu, stable

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Phil Yang <phil.yang@arm.com>
---
 test/test/Makefile          |   1 +
 test/test/autotest_data.py  |   6 +
 test/test/meson.build       |   1 +
 test/test/test_ticketlock.c | 311 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 319 insertions(+)
 create mode 100644 test/test/test_ticketlock.c

diff --git a/test/test/Makefile b/test/test/Makefile
index e7c8108..fa50aaf 100644
--- a/test/test/Makefile
+++ b/test/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/test/test/autotest_data.py b/test/test/autotest_data.py
index 0fb7866..f18b9b9 100644
--- a/test/test/autotest_data.py
+++ b/test/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock_autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report": None,
+    },
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/test/test/meson.build b/test/test/meson.build
index 9e45baf..d473f2b 100644
--- a/test/test/meson.build
+++ b/test/test/meson.build
@@ -103,6 +103,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/test/test/test_ticketlock.c b/test/test/test_ticketlock.c
new file mode 100644
index 0000000..10294f6
--- /dev/null
+++ b/test/test/test_ticketlock.c
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_eal.h>
+#include <rte_lcore.h>
+#include <rte_cycles.h>
+#include <generic/rte_ticketlock.h>
+#include <rte_atomic.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t sl, sl_try;
+static rte_ticketlock_t sl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t slr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&sl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&sl);
+
+	rte_ticketlock_lock(&sl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&sl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lock_count[RTE_MAX_LCORE] = {0};
+
+#define TIME_MS 100
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	uint64_t lcount = 0;
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_get_timer_cycles();
+	while (time_diff < hz * TIME_MS / 1000) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+		/* delay to make lock duty cycle slighlty realistic */
+		rte_delay_us(1);
+		time_diff = rte_get_timer_cycles() - begin;
+	}
+	lock_count[lcore] = lcount;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t total = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
+		total += lock_count[i];
+	}
+
+	printf("Total count = %"PRIu64"\n", total);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&sl_try) == 0) {
+		rte_ticketlock_lock(&sl);
+		count++;
+		rte_ticketlock_unlock(&sl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&sl);
+	rte_ticketlock_init(&sl_try);
+	rte_ticketlock_recursive_init(&slr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&sl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&sl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&sl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&sl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&sl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&slr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&slr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&slr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+							  NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&slr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&sl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&sl_try);
+	if (rte_ticketlock_is_locked(&sl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&sl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&sl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&slr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&slr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&slr);
+	rte_ticketlock_recursive_unlock(&slr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v3 0/2] ticketlock: implement ticketlock and add test case
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
@ 2019-01-25  8:37   ` Joyce Kong
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 " Joyce Kong
                       ` (2 more replies)
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 1/2] ticketlock: ticket based to improve fairness Joyce Kong
                     ` (12 subsequent siblings)
  13 siblings, 3 replies; 74+ messages in thread
From: Joyce Kong @ 2019-01-25  8:37 UTC (permalink / raw)
  To: dev; +Cc: nd, thomas, jerin.jacob, stephen, honnappa.nagarahalli, gavin.hu

V3:
	1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
    2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).

V2:
	1.Update commit message(suggested by Jerin Jacob).
	2.Add ticketlock test cases(suggested by Jerin Jacob).

V1:
    Implement ticket lock to improve lock fairness and prdictability.

    As shown on thundex-2 platform:
    *** ticketlock_autotest with this patch ***
        Core [0] count = 496
        Core [1] count = 495
        Core [2] count = 498
        ...
        Core [209] count = 488
        Core [210] count = 490
        Core [211] count = 474

Joyce Kong (2):
  ticketlock: ticket based to improve fairness
  test/ticketlock: add ticket lock test case

 MAINTAINERS                                        |   5 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 201 +++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 test/test/Makefile                                 |   1 +
 test/test/autotest_data.py                         |   6 +
 test/test/meson.build                              |   1 +
 test/test/test_ticketlock.c                        | 311 +++++++++++++++++++++
 9 files changed, 528 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h
 create mode 100644 test/test/test_ticketlock.c

-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v3 1/2] ticketlock: ticket based to improve fairness
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 0/2] ticketlock: implement ticketlock and add test case Joyce Kong
@ 2019-01-25  8:37   ` Joyce Kong
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 2/2] test/ticketlock: add ticket lock test case Joyce Kong
                     ` (11 subsequent siblings)
  13 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-01-25  8:37 UTC (permalink / raw)
  To: dev
  Cc: nd, thomas, jerin.jacob, stephen, honnappa.nagarahalli, gavin.hu,
	Joyce kong

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.

This patch introduces ticketlock which gives each waiting thread a
ticket and they can take the lock one by one. First come, first serviced.
This avoids starvation for too long time and is more predictable.

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
---
 MAINTAINERS                                        |   4 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 201 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 5 files changed, 208 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 6610440..2ba5663 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -212,6 +212,10 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 F: lib/librte_eal/common/include/rte_bitmap.h
 F: test/test/test_bitmap.c
 
+Ticketlock
+M: Joyce Kong <joyce.kong@arm.com>
+F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
 M: Gavin Hu <gavin.hu@arm.com>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 87d8c45..99e948b 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..6b416ac
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticket locks
+ *
+ * This file defines an API for ticket locks, which give each waiting
+ * thread a ticket and take the lock one by one, first come, first
+ * serviced.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef struct {
+	unsigned int current;
+	unsigned int next;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	unsigned int me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	unsigned int i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	i++;
+	__atomic_store_n(&tl->current, i, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	unsigned int next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
+	unsigned int cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	if (next == cur) {
+		if (__atomic_compare_exchange_n(&tl->next, &next, next+1,
+			0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock icurrently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
+			__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
+}
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+#define INVALID_THREAD_ID -1
+
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	int user; /**< core id using lock, INVALID_THREAD_ID for unused */
+	unsigned int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, -1, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void rte_ticketlock_recursive_init(
+					rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	__atomic_store_n(&tlr->user, INVALID_THREAD_ID, __ATOMIC_RELAXED);
+	__atomic_store_n(&tlr->count, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void rte_ticketlock_recursive_lock(
+					rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		tlr->user = id;
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void rte_ticketlock_recursive_unlock(
+					rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		tlr->user = INVALID_THREAD_ID;
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int rte_ticketlock_recursive_trylock(
+					rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		tlr->user = id;
+	}
+	tlr->count++;
+	return 1;
+}
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 2a10d57..23f9416 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -98,6 +98,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v3 2/2] test/ticketlock: add ticket lock test case
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 0/2] ticketlock: implement ticketlock and add test case Joyce Kong
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 1/2] ticketlock: ticket based to improve fairness Joyce Kong
@ 2019-01-25  8:37   ` Joyce Kong
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 0/2] ticketlock: implement ticketlock and add " Joyce Kong
                     ` (10 subsequent siblings)
  13 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-01-25  8:37 UTC (permalink / raw)
  To: dev; +Cc: nd, thomas, jerin.jacob, stephen, honnappa.nagarahalli, gavin.hu

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Phil Yang <phil.yang@arm.com>
---
 MAINTAINERS                 |   1 +
 test/test/Makefile          |   1 +
 test/test/autotest_data.py  |   6 +
 test/test/meson.build       |   1 +
 test/test/test_ticketlock.c | 311 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 320 insertions(+)
 create mode 100644 test/test/test_ticketlock.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 2ba5663..4da01ab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -215,6 +215,7 @@ F: test/test/test_bitmap.c
 Ticketlock
 M: Joyce Kong <joyce.kong@arm.com>
 F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+F: test/test/test_ticketlock.c
 
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
diff --git a/test/test/Makefile b/test/test/Makefile
index 89949c2..d6aa28b 100644
--- a/test/test/Makefile
+++ b/test/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/test/test/autotest_data.py b/test/test/autotest_data.py
index 948a625..23be062 100644
--- a/test/test/autotest_data.py
+++ b/test/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock_autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report": None,
+    },
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/test/test/meson.build b/test/test/meson.build
index 05e5dde..ddb4d09 100644
--- a/test/test/meson.build
+++ b/test/test/meson.build
@@ -107,6 +107,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/test/test/test_ticketlock.c b/test/test/test_ticketlock.c
new file mode 100644
index 0000000..10294f6
--- /dev/null
+++ b/test/test/test_ticketlock.c
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_eal.h>
+#include <rte_lcore.h>
+#include <rte_cycles.h>
+#include <generic/rte_ticketlock.h>
+#include <rte_atomic.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t sl, sl_try;
+static rte_ticketlock_t sl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t slr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&sl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&sl);
+
+	rte_ticketlock_lock(&sl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&sl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lock_count[RTE_MAX_LCORE] = {0};
+
+#define TIME_MS 100
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	uint64_t lcount = 0;
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_get_timer_cycles();
+	while (time_diff < hz * TIME_MS / 1000) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+		/* delay to make lock duty cycle slighlty realistic */
+		rte_delay_us(1);
+		time_diff = rte_get_timer_cycles() - begin;
+	}
+	lock_count[lcore] = lcount;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t total = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
+		total += lock_count[i];
+	}
+
+	printf("Total count = %"PRIu64"\n", total);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&sl_try) == 0) {
+		rte_ticketlock_lock(&sl);
+		count++;
+		rte_ticketlock_unlock(&sl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&sl);
+	rte_ticketlock_init(&sl_try);
+	rte_ticketlock_recursive_init(&slr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&sl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&sl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&sl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&sl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&sl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&slr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&slr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&slr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+							  NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&slr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&sl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&sl_try);
+	if (rte_ticketlock_is_locked(&sl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&sl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&sl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&slr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&slr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&slr);
+	rte_ticketlock_recursive_unlock(&slr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v4 0/2] ticketlock: implement ticketlock and add test case
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 0/2] ticketlock: implement ticketlock and add test case Joyce Kong
@ 2019-02-19 10:48     ` Joyce Kong
  2019-03-11  5:52       ` [dpdk-dev] [PATCH v5 " Joyce Kong
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 1/2] ticketlock: ticket based to improve fairness Joyce Kong
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 2/2] test/ticketlock: add ticket lock test case Joyce Kong
  2 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-02-19 10:48 UTC (permalink / raw)
  To: dev; +Cc: nd, thomas, jerin.jacob, stephen, honnappa.nagarahalli, gavin.hu

v4:
	Change some assignment operation in recursive ticket lock to __atomic.

V3:
    1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
    2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).

V2:
    1.Update commit message(suggested by Jerin Jacob).
    2.Add ticketlock test cases(suggested by Jerin Jacob).

V1:
    Implement ticket lock to improve lock fairness and prdictability.

    As shown on thundex-2 platform:
    *** ticketlock_autotest with this patch ***
        Core [0] count = 496
        Core [1] count = 495
        Core [2] count = 498
        ...
        Core [209] count = 488
        Core [210] count = 490
        Core [211] count = 474

Joyce Kong (2):
  ticketlock: ticket based to improve fairness
  test/ticketlock: add ticket lock test case

 MAINTAINERS                                        |   4 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 201 +++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 test/test/Makefile                                 |   1 +
 test/test/autotest_data.py                         |   6 +
 test/test/meson.build                              |   1 +
 test/test/test_ticketlock.c                        | 311 +++++++++++++++++++++
 9 files changed, 527 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h
 create mode 100644 test/test/test_ticketlock.c

-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v4 1/2] ticketlock: ticket based to improve fairness
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 0/2] ticketlock: implement ticketlock and add test case Joyce Kong
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 " Joyce Kong
@ 2019-02-19 10:48     ` Joyce Kong
  2019-03-11  5:52       ` [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: " Joyce Kong
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 2/2] test/ticketlock: add ticket lock test case Joyce Kong
  2 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-02-19 10:48 UTC (permalink / raw)
  To: dev
  Cc: nd, thomas, jerin.jacob, stephen, honnappa.nagarahalli, gavin.hu,
	Joyce kong

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.

This patch introduces ticketlock which gives each waiting thread a
ticket and they can take the lock one by one. First come, first serviced.
This avoids starvation for too long time and is more predictable.

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
---
 MAINTAINERS                                        |   4 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 201 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 5 files changed, 208 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index eef480a..022e6bb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -212,6 +212,10 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 F: lib/librte_eal/common/include/rte_bitmap.h
 F: test/test/test_bitmap.c
 
+Ticketlock
+M: Joyce Kong <joyce.kong@arm.com>
+F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
 M: Gavin Hu <gavin.hu@arm.com>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index 87d8c45..99e948b 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..4846962
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticket locks
+ *
+ * This file defines an API for ticket locks, which give each waiting
+ * thread a ticket and take the lock one by one, first come, first
+ * serviced.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef struct {
+	unsigned int current;
+	unsigned int next;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	unsigned int me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	unsigned int i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	i++;
+	__atomic_store_n(&tl->current, i, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	unsigned int next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
+	unsigned int cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	if (next == cur) {
+		if (__atomic_compare_exchange_n(&tl->next, &next, next+1,
+			0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock icurrently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
+			__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
+}
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+#define TICKET_LOCK_INVALID_ID -1
+
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	int user; /**< core id using lock, TICKET_LOCK_INVALID_ID for unused */
+	unsigned int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, -1, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_init(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED);
+	__atomic_store_n(&tlr->count, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_lock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_unlock(rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED);
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_recursive_trylock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+	return 1;
+}
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 2a10d57..23f9416 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -98,6 +98,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v4 2/2] test/ticketlock: add ticket lock test case
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 0/2] ticketlock: implement ticketlock and add test case Joyce Kong
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 " Joyce Kong
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 1/2] ticketlock: ticket based to improve fairness Joyce Kong
@ 2019-02-19 10:48     ` Joyce Kong
  2019-03-11  5:52       ` [dpdk-dev] [PATCH v5 " Joyce Kong
  2 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-02-19 10:48 UTC (permalink / raw)
  To: dev; +Cc: nd, thomas, jerin.jacob, stephen, honnappa.nagarahalli, gavin.hu

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Phil Yang <phil.yang@arm.com>
---
 test/test/Makefile          |   1 +
 test/test/autotest_data.py  |   6 +
 test/test/meson.build       |   1 +
 test/test/test_ticketlock.c | 311 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 319 insertions(+)
 create mode 100644 test/test/test_ticketlock.c

diff --git a/test/test/Makefile b/test/test/Makefile
index 89949c2..d6aa28b 100644
--- a/test/test/Makefile
+++ b/test/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/test/test/autotest_data.py b/test/test/autotest_data.py
index 5f87bb9..0f80df5 100644
--- a/test/test/autotest_data.py
+++ b/test/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock_autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report": None,
+    },
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/test/test/meson.build b/test/test/meson.build
index 05e5dde..ddb4d09 100644
--- a/test/test/meson.build
+++ b/test/test/meson.build
@@ -107,6 +107,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/test/test/test_ticketlock.c b/test/test/test_ticketlock.c
new file mode 100644
index 0000000..dfa818c
--- /dev/null
+++ b/test/test/test_ticketlock.c
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_eal.h>
+#include <rte_lcore.h>
+#include <rte_cycles.h>
+#include <generic/rte_ticketlock.h>
+#include <rte_atomic.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t sl, sl_try;
+static rte_ticketlock_t sl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t slr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&sl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&sl);
+
+	rte_ticketlock_lock(&sl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&sl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lock_count[RTE_MAX_LCORE] = {0};
+
+#define TIME_MS 100
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	uint64_t lcount = 0;
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_get_timer_cycles();
+	while (time_diff < hz * TIME_MS / 1000) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+		/* delay to make lock duty cycle slighlty realistic */
+		rte_delay_us(1);
+		time_diff = rte_get_timer_cycles() - begin;
+	}
+	lock_count[lcore] = lcount;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t total = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
+		total += lock_count[i];
+	}
+
+	printf("Total count = %"PRIu64"\n", total);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&sl_try) == 0) {
+		rte_ticketlock_lock(&sl);
+		count++;
+		rte_ticketlock_unlock(&sl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&sl);
+	rte_ticketlock_init(&sl_try);
+	rte_ticketlock_recursive_init(&slr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&sl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&sl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&sl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&sl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&sl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&slr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&slr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&slr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+								NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&slr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&sl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&sl_try);
+	if (rte_ticketlock_is_locked(&sl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&sl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&sl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&slr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&slr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&slr);
+	rte_ticketlock_recursive_unlock(&slr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v5 0/2] ticketlock: implement ticketlock and add test case
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 " Joyce Kong
@ 2019-03-11  5:52       ` Joyce Kong
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-11  5:52 UTC (permalink / raw)
  To: dev; +Cc: nd, thomas, jerin.jacob, stephen, honnappa.nagarahalli, gavin.hu

v5:
    Change the variants inside rte_ticket_lock from unint to uint16_t for
    binary compatibility with the plain spin lock.

v4:
    Change some assignment operation in recursive ticket lock to __atomic.

V3:
    1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
    2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).

V2:
    1.Update commit message(suggested by Jerin Jacob).
    2.Add ticketlock test cases(suggested by Jerin Jacob).

V1:
    Implement ticket lock to improve lock fairness and prdictability.

    As shown on thundex-2 platform:
    *** ticketlock_autotest with this patch ***
        Core [0] count = 496
        Core [1] count = 495
        Core [2] count = 498
        ...
        Core [209] count = 488
        Core [210] count = 490
        Core [211] count = 474

Joyce Kong (2):
  eal/ticketlock: ticket based to improve fairness
  test/ticketlock: add ticket lock test case

 MAINTAINERS                                        |   4 +
 app/test/Makefile                                  |   1 +
 app/test/autotest_data.py                          |   6 +
 app/test/meson.build                               |   1 +
 app/test/test_ticketlock.c                         | 311 +++++++++++++++++++++
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 203 ++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 9 files changed, 529 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_ticketlock.c
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve fairness
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 1/2] ticketlock: ticket based to improve fairness Joyce Kong
@ 2019-03-11  5:52       ` Joyce Kong
  2019-03-13  9:41         ` Jerin Jacob Kollanukkaran
  2019-03-13 15:36         ` Jerin Jacob Kollanukkaran
  0 siblings, 2 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-11  5:52 UTC (permalink / raw)
  To: dev
  Cc: nd, thomas, jerin.jacob, stephen, honnappa.nagarahalli, gavin.hu,
	Joyce kong

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.

This patch introduces ticketlock which gives each waiting thread a
ticket and they can take the lock one by one. First come, first serviced.
This avoids starvation for too long time and is more predictable.

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
---
 MAINTAINERS                                        |   4 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 203 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 5 files changed, 210 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 097cfb4..12a091f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -210,6 +210,10 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 F: lib/librte_eal/common/include/rte_bitmap.h
 F: app/test/test_bitmap.c
 
+Ticketlock
+M: Joyce Kong <joyce.kong@arm.com>
+F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
 M: Gavin Hu <gavin.hu@arm.com>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index c487201..ac3305c 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..b99737a
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticket locks
+ *
+ * This file defines an API for ticket locks, which give each waiting
+ * thread a ticket and take the lock one by one, first come, first
+ * serviced.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#include <rte_lcore.h>
+#include <rte_common.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef struct {
+	uint16_t current;
+	uint16_t next;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	unsigned int me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	unsigned int i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	i++;
+	__atomic_store_n(&tl->current, i, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	unsigned int next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
+	unsigned int cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	if (next == cur) {
+		if (__atomic_compare_exchange_n(&tl->next, &next, next+1,
+		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock icurrently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
+		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
+}
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+#define TICKET_LOCK_INVALID_ID -1
+
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	int user; /**< core id using lock, TICKET_LOCK_INVALID_ID for unused */
+	unsigned int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, \
+				TICKET_LOCK_INVALID_ID, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_init(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED);
+	tlr->count = 0;
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_lock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_unlock(rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID,
+				 __ATOMIC_RELAXED);
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_recursive_trylock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+	return 1;
+}
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 5ecae0b..0670e41 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -99,6 +99,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v5 2/2] test/ticketlock: add ticket lock test case
  2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 2/2] test/ticketlock: add ticket lock test case Joyce Kong
@ 2019-03-11  5:52       ` Joyce Kong
  2019-03-13 13:31         ` Jerin Jacob Kollanukkaran
  0 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-03-11  5:52 UTC (permalink / raw)
  To: dev; +Cc: nd, thomas, jerin.jacob, stephen, honnappa.nagarahalli, gavin.hu

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Phil Yang <phil.yang@arm.com>
---
 app/test/Makefile          |   1 +
 app/test/autotest_data.py  |   6 +
 app/test/meson.build       |   1 +
 app/test/test_ticketlock.c | 311 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 319 insertions(+)
 create mode 100644 app/test/test_ticketlock.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 89949c2..d6aa28b 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 5f87bb9..db25274 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report":  None,
+    }
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 05e5dde..ddb4d09 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -107,6 +107,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
new file mode 100644
index 0000000..dfa818c
--- /dev/null
+++ b/app/test/test_ticketlock.c
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_eal.h>
+#include <rte_lcore.h>
+#include <rte_cycles.h>
+#include <generic/rte_ticketlock.h>
+#include <rte_atomic.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t sl, sl_try;
+static rte_ticketlock_t sl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t slr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&sl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&sl);
+
+	rte_ticketlock_lock(&sl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&sl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_lock(&slr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, slr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+	rte_ticketlock_recursive_unlock(&slr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, slr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lock_count[RTE_MAX_LCORE] = {0};
+
+#define TIME_MS 100
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	uint64_t lcount = 0;
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_get_timer_cycles();
+	while (time_diff < hz * TIME_MS / 1000) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+		/* delay to make lock duty cycle slighlty realistic */
+		rte_delay_us(1);
+		time_diff = rte_get_timer_cycles() - begin;
+	}
+	lock_count[lcore] = lcount;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t total = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
+		total += lock_count[i];
+	}
+
+	printf("Total count = %"PRIu64"\n", total);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&sl_try) == 0) {
+		rte_ticketlock_lock(&sl);
+		count++;
+		rte_ticketlock_unlock(&sl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&sl);
+	rte_ticketlock_init(&sl_try);
+	rte_ticketlock_recursive_init(&slr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&sl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&sl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&sl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&sl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&sl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&slr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&slr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&slr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+								NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&slr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&sl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&sl_try);
+	if (rte_ticketlock_is_locked(&sl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&sl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&sl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&slr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&slr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&slr);
+	rte_ticketlock_recursive_unlock(&slr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-11  5:52       ` [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: " Joyce Kong
@ 2019-03-13  9:41         ` Jerin Jacob Kollanukkaran
  2019-03-15  6:57           ` Joyce Kong (Arm Technology China)
  2019-03-13 15:36         ` Jerin Jacob Kollanukkaran
  1 sibling, 1 reply; 74+ messages in thread
From: Jerin Jacob Kollanukkaran @ 2019-03-13  9:41 UTC (permalink / raw)
  To: joyce.kong, dev
  Cc: stephen, honnappa.nagarahalli, thomas, nd, jerin.jacob, gavin.hu

On Mon, 2019-03-11 at 13:52 +0800, Joyce Kong wrote:
> The spinlock implementation is unfair, some threads may take locks
> aggressively while leaving the other threads starving for long time.
> 
> This patch introduces ticketlock which gives each waiting thread a
> ticket and they can take the lock one by one. First come, first
> serviced.
> This avoids starvation for too long time and is more predictable.
> 
> Suggested-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Joyce kong <joyce.kong@arm.com>
> Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
> Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
> ---
> +static inline __rte_experimental int
> +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> +{
> +	unsigned int next = __atomic_load_n(&tl->next,
> __ATOMIC_RELAXED);
> +	unsigned int cur = __atomic_load_n(&tl->current,
> __ATOMIC_RELAXED);
> +	if (next == cur) {
> +		if (__atomic_compare_exchange_n(&tl->next, &next,
> next+1,
> +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))

gcc 8.2 emits the following compilation error.

/export/dpdk.org/build/include/generic/rte_ticketlock.h:93:46: error:
incompatible pointer types passing 'unsigned int *' to parameter of
type 'uint16_t *' (aka 'unsigned short *') [-Werror,-Wincompatible-
pointer-types]
                if (__atomic_compare_exchange_n(&tl->next, &next,
next+1,





> +			return 1;
> +	}
> +
> +	return 0;
> +}
> +
> 

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v5 2/2] test/ticketlock: add ticket lock test case
  2019-03-11  5:52       ` [dpdk-dev] [PATCH v5 " Joyce Kong
@ 2019-03-13 13:31         ` Jerin Jacob Kollanukkaran
  2019-03-15  6:57           ` Joyce Kong (Arm Technology China)
  0 siblings, 1 reply; 74+ messages in thread
From: Jerin Jacob Kollanukkaran @ 2019-03-13 13:31 UTC (permalink / raw)
  To: joyce.kong, dev
  Cc: stephen, honnappa.nagarahalli, thomas, nd, jerin.jacob, gavin.hu

On Mon, 2019-03-11 at 13:52 +0800, Joyce Kong wrote:
> Add test cases for ticket lock, recursive ticket lock,
> and ticket lock performance.
> 
> Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> Reviewed-by: Phil Yang <phil.yang@arm.com>
> ---
> index 0000000..dfa818c
> --- /dev/null
> +++ b/app/test/test_ticketlock.c
> @@ -0,0 +1,311 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2018-2019 Arm Limited
> + */
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <inttypes.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/queue.h>
> +
> +#include <rte_common.h>
> +#include <rte_memory.h>
> +#include <rte_per_lcore.h>
> +#include <rte_launch.h>
> +#include <rte_eal.h>
> +#include <rte_lcore.h>
> +#include <rte_cycles.h>
> +#include <generic/rte_ticketlock.h>

it should be just <rte_ticketlock.h>

> +#include <rte_atomic.h>

Please sort this in alphabetical order.


> 

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-11  5:52       ` [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: " Joyce Kong
  2019-03-13  9:41         ` Jerin Jacob Kollanukkaran
@ 2019-03-13 15:36         ` Jerin Jacob Kollanukkaran
  2019-03-15  6:58           ` Joyce Kong (Arm Technology China)
  1 sibling, 1 reply; 74+ messages in thread
From: Jerin Jacob Kollanukkaran @ 2019-03-13 15:36 UTC (permalink / raw)
  To: joyce.kong, dev
  Cc: stephen, honnappa.nagarahalli, thomas, nd, jerin.jacob, gavin.hu

On Mon, 2019-03-11 at 13:52 +0800, Joyce Kong wrote:
> The spinlock implementation is unfair, some threads may take locks
> aggressively while leaving the other threads starving for long time.
> 
> This patch introduces ticketlock which gives each waiting thread a
> ticket and they can take the lock one by one. First come, first
> serviced.
> This avoids starvation for too long time and is more predictable.
> 
> Suggested-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Joyce kong <joyce.kong@arm.com>
> Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
> Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
> ---
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 097cfb4..12a091f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -210,6 +210,10 @@ M: Cristian Dumitrescu <
> cristian.dumitrescu@intel.com>
>  F: lib/librte_eal/common/include/rte_bitmap.h
>  F: app/test/test_bitmap.c
>  
> +Ticketlock
> +M: Joyce Kong <joyce.kong@arm.com>
> +F: lib/librte_eal/common/include/generic/rte_ticketlock.h


Add F: app/test/test_ticketlock.c in the next patch



> 
> +#include <rte_lcore.h>
> +#include <rte_common.h>
> +#include <rte_pause.h>

Sort the header in alphabetical order.


> +
> +/**
> + * The rte_ticketlock_t type.
> + */
> +typedef struct {
> +	uint16_t current;
> +	uint16_t next;
> +} rte_ticketlock_t;
> +
> 
> +
> +/**
> + * Take the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_lock(rte_ticketlock_t *tl)
> +{
> +	unsigned int me = __atomic_fetch_add(&tl->next, 1, 

If current, next is uint16_t why "me" as unsigned int.


> __ATOMIC_RELAXED);
> +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> +		rte_pause();
> +}
> +
> +/**
> + * Release the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> +{
> +	unsigned int i = __atomic_load_n(&tl->current,
> __ATOMIC_RELAXED);
> +	i++;

You can save this line by making
__atomic_store_n(&tl->current, i + 1, __ATOMIC_RELEASE);


The code looks good. Please check the above comments and earlier
reported compilation issue with clang 7.1



^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v6 0/2] ticketlock: implement ticketlock and add test case
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (2 preceding siblings ...)
  2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 2/2] test/ticketlock: add ticket lock test case Joyce Kong
@ 2019-03-15  6:56   ` Joyce Kong
  2019-03-15  6:56     ` Joyce Kong
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness Joyce Kong
                     ` (9 subsequent siblings)
  13 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-03-15  6:56 UTC (permalink / raw)
  To: dev; +Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu

v6:
    Add rte_ticketlock.h in lib/librte_eal/common/include/arch/arm/.
    Sort header inside ticketlock files by alphabetical order.

v5:
    Change the variants inside rte_ticket_lock from unint to uint16_t for binary
    compatibility with the plain spin lock(suggested by Honnappa Nagarahalli)).

v4:
    Change some assignment operation in recursive ticket lock to __atomic.

V3:
    1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
    2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).

V2:
    1.Update commit message(suggested by Jerin Jacob).
    2.Add ticketlock test cases(suggested by Jerin Jacob).

V1:
    Implement ticket lock to improve lock fairness and prdictability.

    As shown on thundex-2 platform:
    *** ticketlock_autotest with this patch ***
        Core [0] count = 496
        Core [1] count = 495
        Core [2] count = 498
        ...
        Core [209] count = 488
        Core [210] count = 490
        Core [211] count = 474


Joyce Kong (2):
  eal/ticketlock: ticket based to improve fairness
  test/ticketlock: add ticket lock test case

 MAINTAINERS                                        |   6 +
 app/test/Makefile                                  |   1 +
 app/test/autotest_data.py                          |   6 +
 app/test/meson.build                               |   1 +
 app/test/test_ticketlock.c                         | 311 +++++++++++++++++++++
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/arch/arm/rte_ticketlock.h       |  64 +++++
 .../common/include/generic/rte_ticketlock.h        | 308 ++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 10 files changed, 700 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_ticketlock.c
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v6 0/2] ticketlock: implement ticketlock and add test case
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 0/2] ticketlock: implement ticketlock and add " Joyce Kong
@ 2019-03-15  6:56     ` Joyce Kong
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-15  6:56 UTC (permalink / raw)
  To: dev; +Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu

v6:
    Add rte_ticketlock.h in lib/librte_eal/common/include/arch/arm/.
    Sort header inside ticketlock files by alphabetical order.

v5:
    Change the variants inside rte_ticket_lock from unint to uint16_t for binary
    compatibility with the plain spin lock(suggested by Honnappa Nagarahalli)).

v4:
    Change some assignment operation in recursive ticket lock to __atomic.

V3:
    1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
    2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).

V2:
    1.Update commit message(suggested by Jerin Jacob).
    2.Add ticketlock test cases(suggested by Jerin Jacob).

V1:
    Implement ticket lock to improve lock fairness and prdictability.

    As shown on thundex-2 platform:
    *** ticketlock_autotest with this patch ***
        Core [0] count = 496
        Core [1] count = 495
        Core [2] count = 498
        ...
        Core [209] count = 488
        Core [210] count = 490
        Core [211] count = 474


Joyce Kong (2):
  eal/ticketlock: ticket based to improve fairness
  test/ticketlock: add ticket lock test case

 MAINTAINERS                                        |   6 +
 app/test/Makefile                                  |   1 +
 app/test/autotest_data.py                          |   6 +
 app/test/meson.build                               |   1 +
 app/test/test_ticketlock.c                         | 311 +++++++++++++++++++++
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/arch/arm/rte_ticketlock.h       |  64 +++++
 .../common/include/generic/rte_ticketlock.h        | 308 ++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 10 files changed, 700 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_ticketlock.c
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (3 preceding siblings ...)
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 0/2] ticketlock: implement ticketlock and add " Joyce Kong
@ 2019-03-15  6:56   ` Joyce Kong
  2019-03-15  6:56     ` Joyce Kong
  2019-03-15 12:55     ` Ananyev, Konstantin
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 2/2] test/ticketlock: add ticket lock test case Joyce Kong
                     ` (8 subsequent siblings)
  13 siblings, 2 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-15  6:56 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu,
	Joyce kong

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.

This patch introduces ticketlock which gives each waiting thread a
ticket and they can take the lock one by one. First come, first serviced.
This avoids starvation for too long time and is more predictable.

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
---
 MAINTAINERS                                        |   5 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/arch/arm/rte_ticketlock.h       |  64 +++++
 .../common/include/generic/rte_ticketlock.h        | 308 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 6 files changed, 380 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 452b8eb..7d87e25 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -210,6 +210,11 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 F: lib/librte_eal/common/include/rte_bitmap.h
 F: app/test/test_bitmap.c
 
+Ticketlock
+M: Joyce Kong <joyce.kong@arm.com>
+F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+F: lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
+
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
 M: Gavin Hu <gavin.hu@arm.com>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index c487201..ac3305c 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
new file mode 100644
index 0000000..57deb0b
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_ARM_H_
+#define _RTE_TICKETLOCK_ARM_H_
+
+#ifndef RTE_FORCE_INTRINSICS
+#  error Platform must be built with CONFIG_RTE_FORCE_INTRINSICS
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include "generic/rte_ticketlock.h"
+
+static inline int rte_tm_supported(void)
+{
+	return 0;
+}
+
+static inline void
+rte_ticketlock_lock_tm(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_lock(tl); /* fall-back */
+}
+
+static inline int
+rte_ticketlock_trylock_tm(rte_ticketlock_t *tl)
+{
+	return rte_ticketlock_trylock(tl);
+}
+
+static inline void
+rte_ticketlock_unlock_tm(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_unlock(tl);
+}
+
+static inline void
+rte_ticketlock_recursive_lock_tm(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_recursive_lock(tlr); /* fall-back */
+}
+
+static inline void
+rte_ticketlock_recursive_unlock_tm(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_recursive_unlock(tlr);
+}
+
+static inline int
+rte_ticketlock_recursive_trylock_tm(rte_ticketlock_recursive_t *tlr)
+{
+	return rte_ticketlock_recursive_trylock(tlr);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_ARM_H_ */
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..d63aaaa
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,308 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticket locks
+ *
+ * This file defines an API for ticket locks, which give each waiting
+ * thread a ticket and take the lock one by one, first come, first
+ * serviced.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_lcore.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef struct {
+	uint16_t current;
+	uint16_t next;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
+	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	if (next == cur) {
+		if (__atomic_compare_exchange_n(&tl->next, &next, next+1,
+		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock icurrently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
+		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
+}
+
+/**
+ * Test if hardware transactional memory (lock elision) is supported
+ *
+ * @return
+ *   1 if the hardware transactional memory is supported; 0 otherwise.
+ */
+static inline int rte_tm_supported(void);
+
+/**
+ * Try to execute critical section in a hardware memory transaction,
+ * if it fails or not available take the ticketlock.
+ *
+ * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
+ * transaction always aborts the transaction since the CPU is not able to
+ * roll-back should the transaction fail. Therefore, hardware transactional
+ * locks are not advised to be used around rte_eth_rx_burst() and
+ * rte_eth_tx_burst() calls.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline void
+rte_ticketlock_lock_tm(rte_ticketlock_t *tl);
+
+/**
+ * Commit hardware memory transaction or release the ticketlock if
+ * the ticketlock is used as a fall-back
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline void
+rte_ticketlock_unlock_tm(rte_ticketlock_t *tl);
+
+/**
+ * Try to execute critical section in a hardware memory transaction,
+ * if it fails or not available try to take the lock.
+ *
+ * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
+ * transaction always aborts the transaction since the CPU is not able to
+ * roll-back should the transaction fail. Therefore, hardware transactional
+ * locks are not advised to be used around rte_eth_rx_burst() and
+ * rte_eth_tx_burst() calls.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the hardware memory transaction is successfully started
+ *   or lock is successfully taken; 0 otherwise.
+ */
+static inline int
+rte_ticketlock_trylock_tm(rte_ticketlock_t *tl);
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+#define TICKET_LOCK_INVALID_ID -1
+
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	int user; /**< core id using lock, TICKET_LOCK_INVALID_ID for unused */
+	unsigned int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, \
+				TICKET_LOCK_INVALID_ID, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_init(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED);
+	tlr->count = 0;
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_lock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_unlock(rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID,
+				 __ATOMIC_RELAXED);
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_recursive_trylock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+	return 1;
+}
+
+/**
+ * Try to execute critical section in a hardware memory transaction,
+ * if it fails or not available take the recursive ticketlocks
+ *
+ * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
+ * transaction always aborts the transaction since the CPU is not able to
+ * roll-back should the transaction fail. Therefore, hardware transactional
+ * locks are not advised to be used around rte_eth_rx_burst() and
+ * rte_eth_tx_burst() calls.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline void
+rte_ticketlock_recursive_lock_tm(rte_ticketlock_recursive_t *tlr);
+
+/**
+ * Commit hardware memory transaction or release the recursive ticketlock
+ * if the recursive ticketlock is used as a fall-back
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline void
+rte_ticketlock_recursive_unlock_tm(rte_ticketlock_recursive_t *tlr);
+
+/**
+ * Try to execute critical section in a hardware memory transaction,
+ * if it fails or not available try to take the recursive lock
+ *
+ * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
+ * transaction always aborts the transaction since the CPU is not able to
+ * roll-back should the transaction fail. Therefore, hardware transactional
+ * locks are not advised to be used around rte_eth_rx_burst() and
+ * rte_eth_tx_burst() calls.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the hardware memory transaction is successfully started
+ *   or lock is successfully taken; 0 otherwise.
+ */
+static inline int
+rte_ticketlock_recursive_trylock_tm(rte_ticketlock_recursive_t *tlr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 5ecae0b..0670e41 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -99,6 +99,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness Joyce Kong
@ 2019-03-15  6:56     ` Joyce Kong
  2019-03-15 12:55     ` Ananyev, Konstantin
  1 sibling, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-15  6:56 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu,
	Joyce kong

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.

This patch introduces ticketlock which gives each waiting thread a
ticket and they can take the lock one by one. First come, first serviced.
This avoids starvation for too long time and is more predictable.

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
---
 MAINTAINERS                                        |   5 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/arch/arm/rte_ticketlock.h       |  64 +++++
 .../common/include/generic/rte_ticketlock.h        | 308 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 6 files changed, 380 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 452b8eb..7d87e25 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -210,6 +210,11 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 F: lib/librte_eal/common/include/rte_bitmap.h
 F: app/test/test_bitmap.c
 
+Ticketlock
+M: Joyce Kong <joyce.kong@arm.com>
+F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+F: lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
+
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
 M: Gavin Hu <gavin.hu@arm.com>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index c487201..ac3305c 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
new file mode 100644
index 0000000..57deb0b
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_ARM_H_
+#define _RTE_TICKETLOCK_ARM_H_
+
+#ifndef RTE_FORCE_INTRINSICS
+#  error Platform must be built with CONFIG_RTE_FORCE_INTRINSICS
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include "generic/rte_ticketlock.h"
+
+static inline int rte_tm_supported(void)
+{
+	return 0;
+}
+
+static inline void
+rte_ticketlock_lock_tm(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_lock(tl); /* fall-back */
+}
+
+static inline int
+rte_ticketlock_trylock_tm(rte_ticketlock_t *tl)
+{
+	return rte_ticketlock_trylock(tl);
+}
+
+static inline void
+rte_ticketlock_unlock_tm(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_unlock(tl);
+}
+
+static inline void
+rte_ticketlock_recursive_lock_tm(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_recursive_lock(tlr); /* fall-back */
+}
+
+static inline void
+rte_ticketlock_recursive_unlock_tm(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_recursive_unlock(tlr);
+}
+
+static inline int
+rte_ticketlock_recursive_trylock_tm(rte_ticketlock_recursive_t *tlr)
+{
+	return rte_ticketlock_recursive_trylock(tlr);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_ARM_H_ */
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..d63aaaa
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,308 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticket locks
+ *
+ * This file defines an API for ticket locks, which give each waiting
+ * thread a ticket and take the lock one by one, first come, first
+ * serviced.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_lcore.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef struct {
+	uint16_t current;
+	uint16_t next;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
+	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
+	if (next == cur) {
+		if (__atomic_compare_exchange_n(&tl->next, &next, next+1,
+		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock icurrently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
+		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
+}
+
+/**
+ * Test if hardware transactional memory (lock elision) is supported
+ *
+ * @return
+ *   1 if the hardware transactional memory is supported; 0 otherwise.
+ */
+static inline int rte_tm_supported(void);
+
+/**
+ * Try to execute critical section in a hardware memory transaction,
+ * if it fails or not available take the ticketlock.
+ *
+ * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
+ * transaction always aborts the transaction since the CPU is not able to
+ * roll-back should the transaction fail. Therefore, hardware transactional
+ * locks are not advised to be used around rte_eth_rx_burst() and
+ * rte_eth_tx_burst() calls.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline void
+rte_ticketlock_lock_tm(rte_ticketlock_t *tl);
+
+/**
+ * Commit hardware memory transaction or release the ticketlock if
+ * the ticketlock is used as a fall-back
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline void
+rte_ticketlock_unlock_tm(rte_ticketlock_t *tl);
+
+/**
+ * Try to execute critical section in a hardware memory transaction,
+ * if it fails or not available try to take the lock.
+ *
+ * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
+ * transaction always aborts the transaction since the CPU is not able to
+ * roll-back should the transaction fail. Therefore, hardware transactional
+ * locks are not advised to be used around rte_eth_rx_burst() and
+ * rte_eth_tx_burst() calls.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the hardware memory transaction is successfully started
+ *   or lock is successfully taken; 0 otherwise.
+ */
+static inline int
+rte_ticketlock_trylock_tm(rte_ticketlock_t *tl);
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+#define TICKET_LOCK_INVALID_ID -1
+
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	int user; /**< core id using lock, TICKET_LOCK_INVALID_ID for unused */
+	unsigned int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, \
+				TICKET_LOCK_INVALID_ID, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_init(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED);
+	tlr->count = 0;
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_lock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_unlock(rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID,
+				 __ATOMIC_RELAXED);
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_recursive_trylock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+	return 1;
+}
+
+/**
+ * Try to execute critical section in a hardware memory transaction,
+ * if it fails or not available take the recursive ticketlocks
+ *
+ * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
+ * transaction always aborts the transaction since the CPU is not able to
+ * roll-back should the transaction fail. Therefore, hardware transactional
+ * locks are not advised to be used around rte_eth_rx_burst() and
+ * rte_eth_tx_burst() calls.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline void
+rte_ticketlock_recursive_lock_tm(rte_ticketlock_recursive_t *tlr);
+
+/**
+ * Commit hardware memory transaction or release the recursive ticketlock
+ * if the recursive ticketlock is used as a fall-back
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline void
+rte_ticketlock_recursive_unlock_tm(rte_ticketlock_recursive_t *tlr);
+
+/**
+ * Try to execute critical section in a hardware memory transaction,
+ * if it fails or not available try to take the recursive lock
+ *
+ * NOTE: An attempt to perform a HW I/O operation inside a hardware memory
+ * transaction always aborts the transaction since the CPU is not able to
+ * roll-back should the transaction fail. Therefore, hardware transactional
+ * locks are not advised to be used around rte_eth_rx_burst() and
+ * rte_eth_tx_burst() calls.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the hardware memory transaction is successfully started
+ *   or lock is successfully taken; 0 otherwise.
+ */
+static inline int
+rte_ticketlock_recursive_trylock_tm(rte_ticketlock_recursive_t *tlr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 5ecae0b..0670e41 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -99,6 +99,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v6 2/2] test/ticketlock: add ticket lock test case
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (4 preceding siblings ...)
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness Joyce Kong
@ 2019-03-15  6:56   ` Joyce Kong
  2019-03-15  6:56     ` Joyce Kong
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 0/3] ticketlock: implement ticketlock and add " Joyce Kong
                     ` (7 subsequent siblings)
  13 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-03-15  6:56 UTC (permalink / raw)
  To: dev; +Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Phil Yang <phil.yang@arm.com>
Signed-off-by: Joyce Kong <joyce.kong@arm.com>
---
 MAINTAINERS                |   1 +
 app/test/Makefile          |   1 +
 app/test/autotest_data.py  |   6 +
 app/test/meson.build       |   1 +
 app/test/test_ticketlock.c | 311 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 320 insertions(+)
 create mode 100644 app/test/test_ticketlock.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 7d87e25..b9ffd76 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -214,6 +214,7 @@ Ticketlock
 M: Joyce Kong <joyce.kong@arm.com>
 F: lib/librte_eal/common/include/generic/rte_ticketlock.h
 F: lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
+F: app/test/test_ticketlock.c
 
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
diff --git a/app/test/Makefile b/app/test/Makefile
index 89949c2..d6aa28b 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 5f87bb9..db25274 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report":  None,
+    }
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 05e5dde..ddb4d09 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -107,6 +107,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
new file mode 100644
index 0000000..67281ce
--- /dev/null
+++ b/app/test/test_ticketlock.c
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_atomic.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_ticketlock.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t tl, tl_try;
+static rte_ticketlock_t tl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t tlr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&tl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl);
+
+	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lock_count[RTE_MAX_LCORE] = {0};
+
+#define TIME_MS 100
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	uint64_t lcount = 0;
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_get_timer_cycles();
+	while (time_diff < hz * TIME_MS / 1000) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+		/* delay to make lock duty cycle slighlty realistic */
+		rte_delay_us(1);
+		time_diff = rte_get_timer_cycles() - begin;
+	}
+	lock_count[lcore] = lcount;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t total = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
+		total += lock_count[i];
+	}
+
+	printf("Total count = %"PRIu64"\n", total);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&tl_try) == 0) {
+		rte_ticketlock_lock(&tl);
+		count++;
+		rte_ticketlock_unlock(&tl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&tl);
+	rte_ticketlock_init(&tl_try);
+	rte_ticketlock_recursive_init(&tlr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&tl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&tl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&tl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&tlr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&tlr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&tlr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+					NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&tl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&tl_try);
+	if (rte_ticketlock_is_locked(&tl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&tl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&tl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_ticketlock_recursive_unlock(&tlr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v6 2/2] test/ticketlock: add ticket lock test case
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 2/2] test/ticketlock: add ticket lock test case Joyce Kong
@ 2019-03-15  6:56     ` Joyce Kong
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-15  6:56 UTC (permalink / raw)
  To: dev; +Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Phil Yang <phil.yang@arm.com>
Signed-off-by: Joyce Kong <joyce.kong@arm.com>
---
 MAINTAINERS                |   1 +
 app/test/Makefile          |   1 +
 app/test/autotest_data.py  |   6 +
 app/test/meson.build       |   1 +
 app/test/test_ticketlock.c | 311 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 320 insertions(+)
 create mode 100644 app/test/test_ticketlock.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 7d87e25..b9ffd76 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -214,6 +214,7 @@ Ticketlock
 M: Joyce Kong <joyce.kong@arm.com>
 F: lib/librte_eal/common/include/generic/rte_ticketlock.h
 F: lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
+F: app/test/test_ticketlock.c
 
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
diff --git a/app/test/Makefile b/app/test/Makefile
index 89949c2..d6aa28b 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 5f87bb9..db25274 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report":  None,
+    }
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 05e5dde..ddb4d09 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -107,6 +107,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
new file mode 100644
index 0000000..67281ce
--- /dev/null
+++ b/app/test/test_ticketlock.c
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_atomic.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_ticketlock.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t tl, tl_try;
+static rte_ticketlock_t tl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t tlr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&tl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl);
+
+	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lock_count[RTE_MAX_LCORE] = {0};
+
+#define TIME_MS 100
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	uint64_t lcount = 0;
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_get_timer_cycles();
+	while (time_diff < hz * TIME_MS / 1000) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+		/* delay to make lock duty cycle slighlty realistic */
+		rte_delay_us(1);
+		time_diff = rte_get_timer_cycles() - begin;
+	}
+	lock_count[lcore] = lcount;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t total = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
+		total += lock_count[i];
+	}
+
+	printf("Total count = %"PRIu64"\n", total);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&tl_try) == 0) {
+		rte_ticketlock_lock(&tl);
+		count++;
+		rte_ticketlock_unlock(&tl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&tl);
+	rte_ticketlock_init(&tl_try);
+	rte_ticketlock_recursive_init(&tlr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&tl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&tl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&tl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&tlr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&tlr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&tlr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+					NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&tl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&tl_try);
+	if (rte_ticketlock_is_locked(&tl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&tl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&tl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_ticketlock_recursive_unlock(&tlr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v5 2/2] test/ticketlock: add ticket lock test case
  2019-03-13 13:31         ` Jerin Jacob Kollanukkaran
@ 2019-03-15  6:57           ` Joyce Kong (Arm Technology China)
  2019-03-15  6:57             ` Joyce Kong (Arm Technology China)
  0 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-03-15  6:57 UTC (permalink / raw)
  To: jerinj, dev
  Cc: stephen, Honnappa Nagarahalli, thomas, nd, jerin.jacob,
	Gavin Hu (Arm Technology China)

> -----Original Message-----
> From: Jerin Jacob Kollanukkaran <jerinj@marvell.com>
> Sent: Wednesday, March 13, 2019 9:31 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: stephen@networkplumber.org; Honnappa Nagarahalli
> <Honnappa.Nagarahalli@arm.com>; thomas@monjalon.net; nd
> <nd@arm.com>; jerin.jacob@caviumnetworks.com; Gavin Hu (Arm
> Technology China) <Gavin.Hu@arm.com>
> Subject: Re: [dpdk-dev] [PATCH v5 2/2] test/ticketlock: add ticket lock test
> case
> 
> On Mon, 2019-03-11 at 13:52 +0800, Joyce Kong wrote:
> > Add test cases for ticket lock, recursive ticket lock, and ticket lock
> > performance.
> >
> > Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> > Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> > Reviewed-by: Phil Yang <phil.yang@arm.com>
> > ---
> > index 0000000..dfa818c
> > --- /dev/null
> > +++ b/app/test/test_ticketlock.c
> > @@ -0,0 +1,311 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2018-2019 Arm Limited  */
> > +
> > +#include <stdio.h>
> > +#include <stdint.h>
> > +#include <inttypes.h>
> > +#include <string.h>
> > +#include <unistd.h>
> > +#include <sys/queue.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_memory.h>
> > +#include <rte_per_lcore.h>
> > +#include <rte_launch.h>
> > +#include <rte_eal.h>
> > +#include <rte_lcore.h>
> > +#include <rte_cycles.h>
> > +#include <generic/rte_ticketlock.h>
> 
> it should be just <rte_ticketlock.h>
> 

Change this to <rte_ticketlock.h> by adding a new rte_ticketlock.h file in 
lib/librte_eal/common/include/arch/arm in V6.


> > +#include <rte_atomic.h>
> 
> Please sort this in alphabetical order.
> 

Do this in v6.


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v5 2/2] test/ticketlock: add ticket lock test case
  2019-03-15  6:57           ` Joyce Kong (Arm Technology China)
@ 2019-03-15  6:57             ` Joyce Kong (Arm Technology China)
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-03-15  6:57 UTC (permalink / raw)
  To: jerinj, dev
  Cc: stephen, Honnappa Nagarahalli, thomas, nd, jerin.jacob,
	Gavin Hu (Arm Technology China)

> -----Original Message-----
> From: Jerin Jacob Kollanukkaran <jerinj@marvell.com>
> Sent: Wednesday, March 13, 2019 9:31 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: stephen@networkplumber.org; Honnappa Nagarahalli
> <Honnappa.Nagarahalli@arm.com>; thomas@monjalon.net; nd
> <nd@arm.com>; jerin.jacob@caviumnetworks.com; Gavin Hu (Arm
> Technology China) <Gavin.Hu@arm.com>
> Subject: Re: [dpdk-dev] [PATCH v5 2/2] test/ticketlock: add ticket lock test
> case
> 
> On Mon, 2019-03-11 at 13:52 +0800, Joyce Kong wrote:
> > Add test cases for ticket lock, recursive ticket lock, and ticket lock
> > performance.
> >
> > Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> > Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> > Reviewed-by: Phil Yang <phil.yang@arm.com>
> > ---
> > index 0000000..dfa818c
> > --- /dev/null
> > +++ b/app/test/test_ticketlock.c
> > @@ -0,0 +1,311 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2018-2019 Arm Limited  */
> > +
> > +#include <stdio.h>
> > +#include <stdint.h>
> > +#include <inttypes.h>
> > +#include <string.h>
> > +#include <unistd.h>
> > +#include <sys/queue.h>
> > +
> > +#include <rte_common.h>
> > +#include <rte_memory.h>
> > +#include <rte_per_lcore.h>
> > +#include <rte_launch.h>
> > +#include <rte_eal.h>
> > +#include <rte_lcore.h>
> > +#include <rte_cycles.h>
> > +#include <generic/rte_ticketlock.h>
> 
> it should be just <rte_ticketlock.h>
> 

Change this to <rte_ticketlock.h> by adding a new rte_ticketlock.h file in 
lib/librte_eal/common/include/arch/arm in V6.


> > +#include <rte_atomic.h>
> 
> Please sort this in alphabetical order.
> 

Do this in v6.


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-13  9:41         ` Jerin Jacob Kollanukkaran
@ 2019-03-15  6:57           ` Joyce Kong (Arm Technology China)
  2019-03-15  6:57             ` Joyce Kong (Arm Technology China)
  0 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-03-15  6:57 UTC (permalink / raw)
  To: jerinj, dev
  Cc: stephen, Honnappa Nagarahalli, thomas, nd, jerin.jacob,
	Gavin Hu (Arm Technology China)

> -----Original Message-----
> From: Jerin Jacob Kollanukkaran <jerinj@marvell.com>
> Sent: Wednesday, March 13, 2019 5:41 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: stephen@networkplumber.org; Honnappa Nagarahalli
> <Honnappa.Nagarahalli@arm.com>; thomas@monjalon.net; nd
> <nd@arm.com>; jerin.jacob@caviumnetworks.com; Gavin Hu (Arm
> Technology China) <Gavin.Hu@arm.com>
> Subject: Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve
> fairness
> 
> On Mon, 2019-03-11 at 13:52 +0800, Joyce Kong wrote:
> > The spinlock implementation is unfair, some threads may take locks
> > aggressively while leaving the other threads starving for long time.
> >
> > This patch introduces ticketlock which gives each waiting thread a
> > ticket and they can take the lock one by one. First come, first
> > serviced.
> > This avoids starvation for too long time and is more predictable.
> >
> > Suggested-by: Jerin Jacob <jerinj@marvell.com>
> > Signed-off-by: Joyce kong <joyce.kong@arm.com>
> > Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> > Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
> > Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
> > ---
> > +static inline __rte_experimental int
> > +rte_ticketlock_trylock(rte_ticketlock_t *tl) {
> > +	unsigned int next = __atomic_load_n(&tl->next,
> > __ATOMIC_RELAXED);
> > +	unsigned int cur = __atomic_load_n(&tl->current,
> > __ATOMIC_RELAXED);
> > +	if (next == cur) {
> > +		if (__atomic_compare_exchange_n(&tl->next, &next,
> > next+1,
> > +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> 
> gcc 8.2 emits the following compilation error.
> 
> /export/dpdk.org/build/include/generic/rte_ticketlock.h:93:46: error:
> incompatible pointer types passing 'unsigned int *' to parameter of type
> 'uint16_t *' (aka 'unsigned short *') [-Werror,-Wincompatible- pointer-types]
>                 if (__atomic_compare_exchange_n(&tl->next, &next,
> next+1,
> 

Fix the error by changing next and cur from unsigned int to unint16_t in V6.


> 
> > +			return 1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-15  6:57           ` Joyce Kong (Arm Technology China)
@ 2019-03-15  6:57             ` Joyce Kong (Arm Technology China)
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-03-15  6:57 UTC (permalink / raw)
  To: jerinj, dev
  Cc: stephen, Honnappa Nagarahalli, thomas, nd, jerin.jacob,
	Gavin Hu (Arm Technology China)

> -----Original Message-----
> From: Jerin Jacob Kollanukkaran <jerinj@marvell.com>
> Sent: Wednesday, March 13, 2019 5:41 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: stephen@networkplumber.org; Honnappa Nagarahalli
> <Honnappa.Nagarahalli@arm.com>; thomas@monjalon.net; nd
> <nd@arm.com>; jerin.jacob@caviumnetworks.com; Gavin Hu (Arm
> Technology China) <Gavin.Hu@arm.com>
> Subject: Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve
> fairness
> 
> On Mon, 2019-03-11 at 13:52 +0800, Joyce Kong wrote:
> > The spinlock implementation is unfair, some threads may take locks
> > aggressively while leaving the other threads starving for long time.
> >
> > This patch introduces ticketlock which gives each waiting thread a
> > ticket and they can take the lock one by one. First come, first
> > serviced.
> > This avoids starvation for too long time and is more predictable.
> >
> > Suggested-by: Jerin Jacob <jerinj@marvell.com>
> > Signed-off-by: Joyce kong <joyce.kong@arm.com>
> > Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> > Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
> > Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
> > ---
> > +static inline __rte_experimental int
> > +rte_ticketlock_trylock(rte_ticketlock_t *tl) {
> > +	unsigned int next = __atomic_load_n(&tl->next,
> > __ATOMIC_RELAXED);
> > +	unsigned int cur = __atomic_load_n(&tl->current,
> > __ATOMIC_RELAXED);
> > +	if (next == cur) {
> > +		if (__atomic_compare_exchange_n(&tl->next, &next,
> > next+1,
> > +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> 
> gcc 8.2 emits the following compilation error.
> 
> /export/dpdk.org/build/include/generic/rte_ticketlock.h:93:46: error:
> incompatible pointer types passing 'unsigned int *' to parameter of type
> 'uint16_t *' (aka 'unsigned short *') [-Werror,-Wincompatible- pointer-types]
>                 if (__atomic_compare_exchange_n(&tl->next, &next,
> next+1,
> 

Fix the error by changing next and cur from unsigned int to unint16_t in V6.


> 
> > +			return 1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-13 15:36         ` Jerin Jacob Kollanukkaran
@ 2019-03-15  6:58           ` Joyce Kong (Arm Technology China)
  2019-03-15  6:58             ` Joyce Kong (Arm Technology China)
  0 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-03-15  6:58 UTC (permalink / raw)
  To: jerinj, dev
  Cc: stephen, Honnappa Nagarahalli, thomas, nd, jerin.jacob,
	Gavin Hu (Arm Technology China)

> -----Original Message-----
> From: Jerin Jacob Kollanukkaran <jerinj@marvell.com>
> Sent: Wednesday, March 13, 2019 11:36 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: stephen@networkplumber.org; Honnappa Nagarahalli
> <Honnappa.Nagarahalli@arm.com>; thomas@monjalon.net; nd
> <nd@arm.com>; jerin.jacob@caviumnetworks.com; Gavin Hu (Arm
> Technology China) <Gavin.Hu@arm.com>
> Subject: Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve
> fairness
> 
> On Mon, 2019-03-11 at 13:52 +0800, Joyce Kong wrote:
> > The spinlock implementation is unfair, some threads may take locks
> > aggressively while leaving the other threads starving for long time.
> >
> > This patch introduces ticketlock which gives each waiting thread a
> > ticket and they can take the lock one by one. First come, first
> > serviced.
> > This avoids starvation for too long time and is more predictable.
> >
> > Suggested-by: Jerin Jacob <jerinj@marvell.com>
> > Signed-off-by: Joyce kong <joyce.kong@arm.com>
> > Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> > Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
> > Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
> > ---
> > diff --git a/MAINTAINERS b/MAINTAINERS index 097cfb4..12a091f 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -210,6 +210,10 @@ M: Cristian Dumitrescu <
> > cristian.dumitrescu@intel.com>
> >  F: lib/librte_eal/common/include/rte_bitmap.h
> >  F: app/test/test_bitmap.c
> >
> > +Ticketlock
> > +M: Joyce Kong <joyce.kong@arm.com>
> > +F: lib/librte_eal/common/include/generic/rte_ticketlock.h
> 
> 
> Add F: app/test/test_ticketlock.c in the next patch
> 

Done in V6.


> >
> > +#include <rte_lcore.h>
> > +#include <rte_common.h>
> > +#include <rte_pause.h>
> 
> Sort the header in alphabetical order.
> 

Done in v6.


> > +
> > +/**
> > + * The rte_ticketlock_t type.
> > + */
> > +typedef struct {
> > +	uint16_t current;
> > +	uint16_t next;
> > +} rte_ticketlock_t;
> > +
> >
> > +
> > +/**
> > + * Take the ticketlock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_lock(rte_ticketlock_t *tl) {
> > +	unsigned int me = __atomic_fetch_add(&tl->next, 1,
> 
> If current, next is uint16_t why "me" as unsigned int.
> 
 
Change "me" to uint16_t to match current and next in v6.


> > __ATOMIC_RELAXED);
> > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > +		rte_pause();
> > +}
> > +
> > +/**
> > + * Release the ticketlock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_unlock(rte_ticketlock_t *tl) {
> > +	unsigned int i = __atomic_load_n(&tl->current,
> > __ATOMIC_RELAXED);
> > +	i++;
> 
> You can save this line by making
> __atomic_store_n(&tl->current, i + 1, __ATOMIC_RELEASE);
> 

Done in V6.


> The code looks good. Please check the above comments and earlier reported
> compilation issue with clang 7.1
> 


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-15  6:58           ` Joyce Kong (Arm Technology China)
@ 2019-03-15  6:58             ` Joyce Kong (Arm Technology China)
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-03-15  6:58 UTC (permalink / raw)
  To: jerinj, dev
  Cc: stephen, Honnappa Nagarahalli, thomas, nd, jerin.jacob,
	Gavin Hu (Arm Technology China)

> -----Original Message-----
> From: Jerin Jacob Kollanukkaran <jerinj@marvell.com>
> Sent: Wednesday, March 13, 2019 11:36 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: stephen@networkplumber.org; Honnappa Nagarahalli
> <Honnappa.Nagarahalli@arm.com>; thomas@monjalon.net; nd
> <nd@arm.com>; jerin.jacob@caviumnetworks.com; Gavin Hu (Arm
> Technology China) <Gavin.Hu@arm.com>
> Subject: Re: [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: ticket based to improve
> fairness
> 
> On Mon, 2019-03-11 at 13:52 +0800, Joyce Kong wrote:
> > The spinlock implementation is unfair, some threads may take locks
> > aggressively while leaving the other threads starving for long time.
> >
> > This patch introduces ticketlock which gives each waiting thread a
> > ticket and they can take the lock one by one. First come, first
> > serviced.
> > This avoids starvation for too long time and is more predictable.
> >
> > Suggested-by: Jerin Jacob <jerinj@marvell.com>
> > Signed-off-by: Joyce kong <joyce.kong@arm.com>
> > Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> > Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
> > Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
> > ---
> > diff --git a/MAINTAINERS b/MAINTAINERS index 097cfb4..12a091f 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -210,6 +210,10 @@ M: Cristian Dumitrescu <
> > cristian.dumitrescu@intel.com>
> >  F: lib/librte_eal/common/include/rte_bitmap.h
> >  F: app/test/test_bitmap.c
> >
> > +Ticketlock
> > +M: Joyce Kong <joyce.kong@arm.com>
> > +F: lib/librte_eal/common/include/generic/rte_ticketlock.h
> 
> 
> Add F: app/test/test_ticketlock.c in the next patch
> 

Done in V6.


> >
> > +#include <rte_lcore.h>
> > +#include <rte_common.h>
> > +#include <rte_pause.h>
> 
> Sort the header in alphabetical order.
> 

Done in v6.


> > +
> > +/**
> > + * The rte_ticketlock_t type.
> > + */
> > +typedef struct {
> > +	uint16_t current;
> > +	uint16_t next;
> > +} rte_ticketlock_t;
> > +
> >
> > +
> > +/**
> > + * Take the ticketlock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_lock(rte_ticketlock_t *tl) {
> > +	unsigned int me = __atomic_fetch_add(&tl->next, 1,
> 
> If current, next is uint16_t why "me" as unsigned int.
> 
 
Change "me" to uint16_t to match current and next in v6.


> > __ATOMIC_RELAXED);
> > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > +		rte_pause();
> > +}
> > +
> > +/**
> > + * Release the ticketlock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_unlock(rte_ticketlock_t *tl) {
> > +	unsigned int i = __atomic_load_n(&tl->current,
> > __ATOMIC_RELAXED);
> > +	i++;
> 
> You can save this line by making
> __atomic_store_n(&tl->current, i + 1, __ATOMIC_RELEASE);
> 

Done in V6.


> The code looks good. Please check the above comments and earlier reported
> compilation issue with clang 7.1
> 


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness Joyce Kong
  2019-03-15  6:56     ` Joyce Kong
@ 2019-03-15 12:55     ` Ananyev, Konstantin
  2019-03-15 12:55       ` Ananyev, Konstantin
  2019-03-19  9:44       ` Gavin Hu (Arm Technology China)
  1 sibling, 2 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-15 12:55 UTC (permalink / raw)
  To: Joyce Kong, dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu

Hi,

> diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> new file mode 100644
> index 0000000..d63aaaa
> --- /dev/null
> +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> @@ -0,0 +1,308 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2019 Arm Limited
> + */
> +
> +#ifndef _RTE_TICKETLOCK_H_
> +#define _RTE_TICKETLOCK_H_
> +
> +/**
> + * @file
> + *
> + * RTE ticket locks
> + *
> + * This file defines an API for ticket locks, which give each waiting
> + * thread a ticket and take the lock one by one, first come, first
> + * serviced.
> + *
> + * All locks must be initialised before use, and only initialised once.
> + *
> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#include <rte_common.h>
> +#include <rte_lcore.h>
> +#include <rte_pause.h>
> +
> +/**
> + * The rte_ticketlock_t type.
> + */
> +typedef struct {
> +	uint16_t current;
> +	uint16_t next;
> +} rte_ticketlock_t;
> +
> +/**
> + * A static ticketlock initializer.
> + */
> +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> +
> +/**
> + * Initialize the ticketlock to an unlocked state.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_init(rte_ticketlock_t *tl)
> +{
> +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> +}
> +
> +/**
> + * Take the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_lock(rte_ticketlock_t *tl)
> +{
> +	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
> +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> +		rte_pause();
> +}
> +
> +/**
> + * Release the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> +{
> +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> +}
> +
> +/**
> + * Try to take the lock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + * @return
> + *   1 if the lock is successfully taken; 0 otherwise.
> + */
> +static inline __rte_experimental int
> +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> +{
> +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> +	if (next == cur) {

Probably a naïve one:
Suppose next==cur==1 here, then this thread will experience really long context switch,
so next time it continues its execution tl->next value will wrap-up and will be 1 again, and tl->current==0 (lock held).
I suppose this function will set tl->next=2 and will return a success?
Wouldn't be better here and in _is_locked_ to do load/store for next/current values in one go
(using 32bit op)? 
Konstantin

> +		if (__atomic_compare_exchange_n(&tl->next, &next, next+1,
> +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> +			return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * Test if the lock is taken.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + * @return
> + *   1 if the lock icurrently taken; 0 otherwise.
> + */
> +static inline __rte_experimental int
> +rte_ticketlock_is_locked(rte_ticketlock_t *tl)
> +{
> +	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
> +		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
> +}
> +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-15 12:55     ` Ananyev, Konstantin
@ 2019-03-15 12:55       ` Ananyev, Konstantin
  2019-03-19  9:44       ` Gavin Hu (Arm Technology China)
  1 sibling, 0 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-15 12:55 UTC (permalink / raw)
  To: Joyce Kong, dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="UTF-8", Size: 3677 bytes --]

Hi,

> diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> new file mode 100644
> index 0000000..d63aaaa
> --- /dev/null
> +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> @@ -0,0 +1,308 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2019 Arm Limited
> + */
> +
> +#ifndef _RTE_TICKETLOCK_H_
> +#define _RTE_TICKETLOCK_H_
> +
> +/**
> + * @file
> + *
> + * RTE ticket locks
> + *
> + * This file defines an API for ticket locks, which give each waiting
> + * thread a ticket and take the lock one by one, first come, first
> + * serviced.
> + *
> + * All locks must be initialised before use, and only initialised once.
> + *
> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#include <rte_common.h>
> +#include <rte_lcore.h>
> +#include <rte_pause.h>
> +
> +/**
> + * The rte_ticketlock_t type.
> + */
> +typedef struct {
> +	uint16_t current;
> +	uint16_t next;
> +} rte_ticketlock_t;
> +
> +/**
> + * A static ticketlock initializer.
> + */
> +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> +
> +/**
> + * Initialize the ticketlock to an unlocked state.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_init(rte_ticketlock_t *tl)
> +{
> +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> +}
> +
> +/**
> + * Take the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_lock(rte_ticketlock_t *tl)
> +{
> +	uint16_t me = __atomic_fetch_add(&tl->next, 1, __ATOMIC_RELAXED);
> +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> +		rte_pause();
> +}
> +
> +/**
> + * Release the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> +{
> +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> +}
> +
> +/**
> + * Try to take the lock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + * @return
> + *   1 if the lock is successfully taken; 0 otherwise.
> + */
> +static inline __rte_experimental int
> +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> +{
> +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> +	if (next == cur) {

Probably a naïve one:
Suppose next==cur==1 here, then this thread will experience really long context switch,
so next time it continues its execution tl->next value will wrap-up and will be 1 again, and tl->current==0 (lock held).
I suppose this function will set tl->next=2 and will return a success?
Wouldn't be better here and in _is_locked_ to do load/store for next/current values in one go
(using 32bit op)? 
Konstantin

> +		if (__atomic_compare_exchange_n(&tl->next, &next, next+1,
> +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> +			return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * Test if the lock is taken.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + * @return
> + *   1 if the lock icurrently taken; 0 otherwise.
> + */
> +static inline __rte_experimental int
> +rte_ticketlock_is_locked(rte_ticketlock_t *tl)
> +{
> +	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
> +		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
> +}
> +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-15 12:55     ` Ananyev, Konstantin
  2019-03-15 12:55       ` Ananyev, Konstantin
@ 2019-03-19  9:44       ` Gavin Hu (Arm Technology China)
  2019-03-19  9:44         ` Gavin Hu (Arm Technology China)
  2019-03-19 10:15         ` Ananyev, Konstantin
  1 sibling, 2 replies; 74+ messages in thread
From: Gavin Hu (Arm Technology China) @ 2019-03-19  9:44 UTC (permalink / raw)
  To: Ananyev, Konstantin, dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China),
	Gavin Hu (Arm Technology China)

Hi Konstantin, 

> -----Original Message-----
> From: Ananyev, Konstantin <konstantin.ananyev@intel.com>
> Sent: Friday, March 15, 2019 8:56 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: nd <nd@arm.com>; stephen@networkplumber.org;
> jerin.jacob@caviumnetworks.com; thomas@monjalon.net; Honnappa
> Nagarahalli <Honnappa.Nagarahalli@arm.com>; Gavin Hu (Arm
> Technology China) <Gavin.Hu@arm.com>
> Subject: RE: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to
> improve fairness
> 
> Hi,
> 
> > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > new file mode 100644
> > index 0000000..d63aaaa
> > --- /dev/null
> > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > @@ -0,0 +1,308 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2019 Arm Limited
> > + */
> > +
> > +#ifndef _RTE_TICKETLOCK_H_
> > +#define _RTE_TICKETLOCK_H_
> > +
> > +/**
> > + * @file
> > + *
> > + * RTE ticket locks
> > + *
> > + * This file defines an API for ticket locks, which give each waiting
> > + * thread a ticket and take the lock one by one, first come, first
> > + * serviced.
> > + *
> > + * All locks must be initialised before use, and only initialised once.
> > + *
> > + */
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +#include <rte_common.h>
> > +#include <rte_lcore.h>
> > +#include <rte_pause.h>
> > +
> > +/**
> > + * The rte_ticketlock_t type.
> > + */
> > +typedef struct {
> > +	uint16_t current;
> > +	uint16_t next;
> > +} rte_ticketlock_t;
> > +
> > +/**
> > + * A static ticketlock initializer.
> > + */
> > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > +
> > +/**
> > + * Initialize the ticketlock to an unlocked state.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > +{
> > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > +}
> > +
> > +/**
> > + * Take the ticketlock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > +{
> > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> __ATOMIC_RELAXED);
> > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > +		rte_pause();
> > +}
> > +
> > +/**
> > + * Release the ticketlock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > +{
> > +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > +}
> > +
> > +/**
> > + * Try to take the lock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + * @return
> > + *   1 if the lock is successfully taken; 0 otherwise.
> > + */
> > +static inline __rte_experimental int
> > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > +{
> > +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> > +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > +	if (next == cur) {
> 
> Probably a naïve one:
> Suppose next==cur==1 here, then this thread will experience really long
> context switch,

By saying context switch, do you mean running to here, it is out of CPU time and starving for CPU? 

> so next time it continues its execution tl->next value will wrap-up and will
> be 1 again, and tl->current==0 (lock held).
> I suppose this function will set tl->next=2 and will return a success?

If this thread was swapped out and another thread took/attempted to take the lock, yes, tl->next == 2 here,
But as next == 1 unchanged, so it would not return a success. 

> Wouldn't be better here and in _is_locked_ to do load/store for
> next/current values in one go
> (using 32bit op)?
> Konstantin

To load both in one go is feasible, but no necessary as we need to compare them. 
We don't need store both as in this function tl->current is read only.
tl->next is read-update-store, I ever thought of combining the two if-statements to one __atomic_compare_exchange_n(&(&tl->next,&tl->current, tl->next+1, ...),
but tl->next+1 is out of atomicity and may be the old value and corrupt the ticket lock waiting chain. 

The current code works ok except it may fail spuriously(in case during context switch, the lock was taken and released by other threads, moving tl->next forward, in this case
The lock is available but not obtained by this trylock).  Anyway, as the name suggests, it is a try/attempt, a spurious fail is not a big deal? And in most cases, dpdk running on dedicated cores,
the context switch will not happen at all. 

Any more comments are welcome!
> 
> > +		if (__atomic_compare_exchange_n(&tl->next, &next,
> next+1,
> > +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> > +			return 1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * Test if the lock is taken.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + * @return
> > + *   1 if the lock icurrently taken; 0 otherwise.
> > + */
> > +static inline __rte_experimental int
> > +rte_ticketlock_is_locked(rte_ticketlock_t *tl)
> > +{
> > +	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
> > +		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
> > +}
> > +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-19  9:44       ` Gavin Hu (Arm Technology China)
@ 2019-03-19  9:44         ` Gavin Hu (Arm Technology China)
  2019-03-19 10:15         ` Ananyev, Konstantin
  1 sibling, 0 replies; 74+ messages in thread
From: Gavin Hu (Arm Technology China) @ 2019-03-19  9:44 UTC (permalink / raw)
  To: Ananyev, Konstantin, dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China),
	Gavin Hu (Arm Technology China)

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="UTF-8", Size: 5667 bytes --]

Hi Konstantin, 

> -----Original Message-----
> From: Ananyev, Konstantin <konstantin.ananyev@intel.com>
> Sent: Friday, March 15, 2019 8:56 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: nd <nd@arm.com>; stephen@networkplumber.org;
> jerin.jacob@caviumnetworks.com; thomas@monjalon.net; Honnappa
> Nagarahalli <Honnappa.Nagarahalli@arm.com>; Gavin Hu (Arm
> Technology China) <Gavin.Hu@arm.com>
> Subject: RE: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to
> improve fairness
> 
> Hi,
> 
> > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > new file mode 100644
> > index 0000000..d63aaaa
> > --- /dev/null
> > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > @@ -0,0 +1,308 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2019 Arm Limited
> > + */
> > +
> > +#ifndef _RTE_TICKETLOCK_H_
> > +#define _RTE_TICKETLOCK_H_
> > +
> > +/**
> > + * @file
> > + *
> > + * RTE ticket locks
> > + *
> > + * This file defines an API for ticket locks, which give each waiting
> > + * thread a ticket and take the lock one by one, first come, first
> > + * serviced.
> > + *
> > + * All locks must be initialised before use, and only initialised once.
> > + *
> > + */
> > +
> > +#ifdef __cplusplus
> > +extern "C" {
> > +#endif
> > +
> > +#include <rte_common.h>
> > +#include <rte_lcore.h>
> > +#include <rte_pause.h>
> > +
> > +/**
> > + * The rte_ticketlock_t type.
> > + */
> > +typedef struct {
> > +	uint16_t current;
> > +	uint16_t next;
> > +} rte_ticketlock_t;
> > +
> > +/**
> > + * A static ticketlock initializer.
> > + */
> > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > +
> > +/**
> > + * Initialize the ticketlock to an unlocked state.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > +{
> > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > +}
> > +
> > +/**
> > + * Take the ticketlock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > +{
> > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> __ATOMIC_RELAXED);
> > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > +		rte_pause();
> > +}
> > +
> > +/**
> > + * Release the ticketlock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + */
> > +static inline __rte_experimental void
> > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > +{
> > +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > +}
> > +
> > +/**
> > + * Try to take the lock.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + * @return
> > + *   1 if the lock is successfully taken; 0 otherwise.
> > + */
> > +static inline __rte_experimental int
> > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > +{
> > +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> > +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > +	if (next == cur) {
> 
> Probably a naïve one:
> Suppose next==cur==1 here, then this thread will experience really long
> context switch,

By saying context switch, do you mean running to here, it is out of CPU time and starving for CPU? 

> so next time it continues its execution tl->next value will wrap-up and will
> be 1 again, and tl->current==0 (lock held).
> I suppose this function will set tl->next=2 and will return a success?

If this thread was swapped out and another thread took/attempted to take the lock, yes, tl->next == 2 here,
But as next == 1 unchanged, so it would not return a success. 

> Wouldn't be better here and in _is_locked_ to do load/store for
> next/current values in one go
> (using 32bit op)?
> Konstantin

To load both in one go is feasible, but no necessary as we need to compare them. 
We don't need store both as in this function tl->current is read only.
tl->next is read-update-store, I ever thought of combining the two if-statements to one __atomic_compare_exchange_n(&(&tl->next,&tl->current, tl->next+1, ...),
but tl->next+1 is out of atomicity and may be the old value and corrupt the ticket lock waiting chain. 

The current code works ok except it may fail spuriously(in case during context switch, the lock was taken and released by other threads, moving tl->next forward, in this case
The lock is available but not obtained by this trylock).  Anyway, as the name suggests, it is a try/attempt, a spurious fail is not a big deal? And in most cases, dpdk running on dedicated cores,
the context switch will not happen at all. 

Any more comments are welcome!
> 
> > +		if (__atomic_compare_exchange_n(&tl->next, &next,
> next+1,
> > +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> > +			return 1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * Test if the lock is taken.
> > + *
> > + * @param tl
> > + *   A pointer to the ticketlock.
> > + * @return
> > + *   1 if the lock icurrently taken; 0 otherwise.
> > + */
> > +static inline __rte_experimental int
> > +rte_ticketlock_is_locked(rte_ticketlock_t *tl)
> > +{
> > +	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
> > +		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
> > +}
> > +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-19  9:44       ` Gavin Hu (Arm Technology China)
  2019-03-19  9:44         ` Gavin Hu (Arm Technology China)
@ 2019-03-19 10:15         ` Ananyev, Konstantin
  2019-03-19 10:15           ` Ananyev, Konstantin
  2019-03-20  5:11           ` Gavin Hu (Arm Technology China)
  1 sibling, 2 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-19 10:15 UTC (permalink / raw)
  To: Gavin Hu (Arm Technology China), dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China)


Hi Gavin,

> >
> > > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > new file mode 100644
> > > index 0000000..d63aaaa
> > > --- /dev/null
> > > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > @@ -0,0 +1,308 @@
> > > +/* SPDX-License-Identifier: BSD-3-Clause
> > > + * Copyright(c) 2019 Arm Limited
> > > + */
> > > +
> > > +#ifndef _RTE_TICKETLOCK_H_
> > > +#define _RTE_TICKETLOCK_H_
> > > +
> > > +/**
> > > + * @file
> > > + *
> > > + * RTE ticket locks
> > > + *
> > > + * This file defines an API for ticket locks, which give each waiting
> > > + * thread a ticket and take the lock one by one, first come, first
> > > + * serviced.
> > > + *
> > > + * All locks must be initialised before use, and only initialised once.
> > > + *
> > > + */
> > > +
> > > +#ifdef __cplusplus
> > > +extern "C" {
> > > +#endif
> > > +
> > > +#include <rte_common.h>
> > > +#include <rte_lcore.h>
> > > +#include <rte_pause.h>
> > > +
> > > +/**
> > > + * The rte_ticketlock_t type.
> > > + */
> > > +typedef struct {
> > > +	uint16_t current;
> > > +	uint16_t next;
> > > +} rte_ticketlock_t;
> > > +
> > > +/**
> > > + * A static ticketlock initializer.
> > > + */
> > > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > > +
> > > +/**
> > > + * Initialize the ticketlock to an unlocked state.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + */
> > > +static inline __rte_experimental void
> > > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > > +{
> > > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > > +}
> > > +
> > > +/**
> > > + * Take the ticketlock.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + */
> > > +static inline __rte_experimental void
> > > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > > +{
> > > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> > __ATOMIC_RELAXED);
> > > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > > +		rte_pause();
> > > +}
> > > +
> > > +/**
> > > + * Release the ticketlock.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + */
> > > +static inline __rte_experimental void
> > > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > > +{
> > > +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > > +}
> > > +
> > > +/**
> > > + * Try to take the lock.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + * @return
> > > + *   1 if the lock is successfully taken; 0 otherwise.
> > > + */
> > > +static inline __rte_experimental int
> > > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > > +{
> > > +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> > > +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > +	if (next == cur) {
> >
> > Probably a naïve one:
> > Suppose next==cur==1 here, then this thread will experience really long
> > context switch,
> 
> By saying context switch, do you mean running to here, it is out of CPU time and starving for CPU?

Yes.

> 
> > so next time it continues its execution tl->next value will wrap-up and will
> > be 1 again, and tl->current==0 (lock held).
> > I suppose this function will set tl->next=2 and will return a success?
> 
> If this thread was swapped out and another thread took/attempted to take the lock, yes, tl->next == 2 here,
> But as next == 1 unchanged, so it would not return a success.

I am not talking about situation when tl->next == 2,tl->current==1 (just one lock() was executed by different thread).
I am talking about situation when this thread was out of cpu for significant amount of cycles,
and in that period tl->next and tl->current were wrapped around (they both reached UINT16_MAX, then 0).
i.e. UINT16_MAX lock/unlock were executed while this thread was away from cpu.
After that another thread just did successful lock(), so tl->next==1 and tl->current==0.
Now this thread wakeups and continues with:
__atomic_compare_exchange_n(&tl->next, &next, next+1, ...)
As both tl->next==1 and next==1, it will succeed.
So we have 2 threads assuming they grabbed the lock successfully. 
Konstantin

> 
> > Wouldn't be better here and in _is_locked_ to do load/store for
> > next/current values in one go
> > (using 32bit op)?
> > Konstantin
> 
> To load both in one go is feasible, but no necessary as we need to compare them.
> We don't need store both as in this function tl->current is read only.
> tl->next is read-update-store, I ever thought of combining the two if-statements to one __atomic_compare_exchange_n(&(&tl->next,&tl-
> >current, tl->next+1, ...),
> but tl->next+1 is out of atomicity and may be the old value and corrupt the ticket lock waiting chain.
> 
> The current code works ok except it may fail spuriously(in case during context switch, the lock was taken and released by other threads,
> moving tl->next forward, in this case
> The lock is available but not obtained by this trylock).  Anyway, as the name suggests, it is a try/attempt, a spurious fail is not a big deal?
> And in most cases, dpdk running on dedicated cores,
> the context switch will not happen at all.
> 
> Any more comments are welcome!
> >
> > > +		if (__atomic_compare_exchange_n(&tl->next, &next,
> > next+1,
> > > +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> > > +			return 1;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +/**
> > > + * Test if the lock is taken.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + * @return
> > > + *   1 if the lock icurrently taken; 0 otherwise.
> > > + */
> > > +static inline __rte_experimental int
> > > +rte_ticketlock_is_locked(rte_ticketlock_t *tl)
> > > +{
> > > +	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
> > > +		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
> > > +}
> > > +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-19 10:15         ` Ananyev, Konstantin
@ 2019-03-19 10:15           ` Ananyev, Konstantin
  2019-03-20  5:11           ` Gavin Hu (Arm Technology China)
  1 sibling, 0 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-19 10:15 UTC (permalink / raw)
  To: Gavin Hu (Arm Technology China), dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China)

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="UTF-8", Size: 6190 bytes --]


Hi Gavin,

> >
> > > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > new file mode 100644
> > > index 0000000..d63aaaa
> > > --- /dev/null
> > > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > @@ -0,0 +1,308 @@
> > > +/* SPDX-License-Identifier: BSD-3-Clause
> > > + * Copyright(c) 2019 Arm Limited
> > > + */
> > > +
> > > +#ifndef _RTE_TICKETLOCK_H_
> > > +#define _RTE_TICKETLOCK_H_
> > > +
> > > +/**
> > > + * @file
> > > + *
> > > + * RTE ticket locks
> > > + *
> > > + * This file defines an API for ticket locks, which give each waiting
> > > + * thread a ticket and take the lock one by one, first come, first
> > > + * serviced.
> > > + *
> > > + * All locks must be initialised before use, and only initialised once.
> > > + *
> > > + */
> > > +
> > > +#ifdef __cplusplus
> > > +extern "C" {
> > > +#endif
> > > +
> > > +#include <rte_common.h>
> > > +#include <rte_lcore.h>
> > > +#include <rte_pause.h>
> > > +
> > > +/**
> > > + * The rte_ticketlock_t type.
> > > + */
> > > +typedef struct {
> > > +	uint16_t current;
> > > +	uint16_t next;
> > > +} rte_ticketlock_t;
> > > +
> > > +/**
> > > + * A static ticketlock initializer.
> > > + */
> > > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > > +
> > > +/**
> > > + * Initialize the ticketlock to an unlocked state.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + */
> > > +static inline __rte_experimental void
> > > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > > +{
> > > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > > +}
> > > +
> > > +/**
> > > + * Take the ticketlock.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + */
> > > +static inline __rte_experimental void
> > > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > > +{
> > > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> > __ATOMIC_RELAXED);
> > > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > > +		rte_pause();
> > > +}
> > > +
> > > +/**
> > > + * Release the ticketlock.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + */
> > > +static inline __rte_experimental void
> > > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > > +{
> > > +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > > +}
> > > +
> > > +/**
> > > + * Try to take the lock.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + * @return
> > > + *   1 if the lock is successfully taken; 0 otherwise.
> > > + */
> > > +static inline __rte_experimental int
> > > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > > +{
> > > +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> > > +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > +	if (next == cur) {
> >
> > Probably a naïve one:
> > Suppose next==cur==1 here, then this thread will experience really long
> > context switch,
> 
> By saying context switch, do you mean running to here, it is out of CPU time and starving for CPU?

Yes.

> 
> > so next time it continues its execution tl->next value will wrap-up and will
> > be 1 again, and tl->current==0 (lock held).
> > I suppose this function will set tl->next=2 and will return a success?
> 
> If this thread was swapped out and another thread took/attempted to take the lock, yes, tl->next == 2 here,
> But as next == 1 unchanged, so it would not return a success.

I am not talking about situation when tl->next == 2,tl->current==1 (just one lock() was executed by different thread).
I am talking about situation when this thread was out of cpu for significant amount of cycles,
and in that period tl->next and tl->current were wrapped around (they both reached UINT16_MAX, then 0).
i.e. UINT16_MAX lock/unlock were executed while this thread was away from cpu.
After that another thread just did successful lock(), so tl->next==1 and tl->current==0.
Now this thread wakeups and continues with:
__atomic_compare_exchange_n(&tl->next, &next, next+1, ...)
As both tl->next==1 and next==1, it will succeed.
So we have 2 threads assuming they grabbed the lock successfully. 
Konstantin

> 
> > Wouldn't be better here and in _is_locked_ to do load/store for
> > next/current values in one go
> > (using 32bit op)?
> > Konstantin
> 
> To load both in one go is feasible, but no necessary as we need to compare them.
> We don't need store both as in this function tl->current is read only.
> tl->next is read-update-store, I ever thought of combining the two if-statements to one __atomic_compare_exchange_n(&(&tl->next,&tl-
> >current, tl->next+1, ...),
> but tl->next+1 is out of atomicity and may be the old value and corrupt the ticket lock waiting chain.
> 
> The current code works ok except it may fail spuriously(in case during context switch, the lock was taken and released by other threads,
> moving tl->next forward, in this case
> The lock is available but not obtained by this trylock).  Anyway, as the name suggests, it is a try/attempt, a spurious fail is not a big deal?
> And in most cases, dpdk running on dedicated cores,
> the context switch will not happen at all.
> 
> Any more comments are welcome!
> >
> > > +		if (__atomic_compare_exchange_n(&tl->next, &next,
> > next+1,
> > > +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> > > +			return 1;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +/**
> > > + * Test if the lock is taken.
> > > + *
> > > + * @param tl
> > > + *   A pointer to the ticketlock.
> > > + * @return
> > > + *   1 if the lock icurrently taken; 0 otherwise.
> > > + */
> > > +static inline __rte_experimental int
> > > +rte_ticketlock_is_locked(rte_ticketlock_t *tl)
> > > +{
> > > +	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
> > > +		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
> > > +}
> > > +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-19 10:15         ` Ananyev, Konstantin
  2019-03-19 10:15           ` Ananyev, Konstantin
@ 2019-03-20  5:11           ` Gavin Hu (Arm Technology China)
  2019-03-20  5:11             ` Gavin Hu (Arm Technology China)
  2019-03-20  9:47             ` Ananyev, Konstantin
  1 sibling, 2 replies; 74+ messages in thread
From: Gavin Hu (Arm Technology China) @ 2019-03-20  5:11 UTC (permalink / raw)
  To: Ananyev, Konstantin, dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China)

Hi Konstantin,

> -----Original Message-----
> From: Ananyev, Konstantin <konstantin.ananyev@intel.com>
> Sent: Tuesday, March 19, 2019 6:15 PM
> To: Gavin Hu (Arm Technology China) <Gavin.Hu@arm.com>; dev@dpdk.org
> Cc: nd <nd@arm.com>; stephen@networkplumber.org;
> jerin.jacob@caviumnetworks.com; thomas@monjalon.net; Honnappa
> Nagarahalli <Honnappa.Nagarahalli@arm.com>; Joyce Kong (Arm Technology
> China) <Joyce.Kong@arm.com>
> Subject: RE: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve
> fairness
> 
> 
> Hi Gavin,
> 
> > >
> > > > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > new file mode 100644
> > > > index 0000000..d63aaaa
> > > > --- /dev/null
> > > > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > @@ -0,0 +1,308 @@
> > > > +/* SPDX-License-Identifier: BSD-3-Clause
> > > > + * Copyright(c) 2019 Arm Limited
> > > > + */
> > > > +
> > > > +#ifndef _RTE_TICKETLOCK_H_
> > > > +#define _RTE_TICKETLOCK_H_
> > > > +
> > > > +/**
> > > > + * @file
> > > > + *
> > > > + * RTE ticket locks
> > > > + *
> > > > + * This file defines an API for ticket locks, which give each waiting
> > > > + * thread a ticket and take the lock one by one, first come, first
> > > > + * serviced.
> > > > + *
> > > > + * All locks must be initialised before use, and only initialised once.
> > > > + *
> > > > + */
> > > > +
> > > > +#ifdef __cplusplus
> > > > +extern "C" {
> > > > +#endif
> > > > +
> > > > +#include <rte_common.h>
> > > > +#include <rte_lcore.h>
> > > > +#include <rte_pause.h>
> > > > +
> > > > +/**
> > > > + * The rte_ticketlock_t type.
> > > > + */
> > > > +typedef struct {
> > > > +	uint16_t current;
> > > > +	uint16_t next;
> > > > +} rte_ticketlock_t;
> > > > +
> > > > +/**
> > > > + * A static ticketlock initializer.
> > > > + */
> > > > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > > > +
> > > > +/**
> > > > + * Initialize the ticketlock to an unlocked state.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + */
> > > > +static inline __rte_experimental void
> > > > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > > > +{
> > > > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > > > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > > > +}
> > > > +
> > > > +/**
> > > > + * Take the ticketlock.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + */
> > > > +static inline __rte_experimental void
> > > > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > > > +{
> > > > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> > > __ATOMIC_RELAXED);
> > > > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > > > +		rte_pause();
> > > > +}
> > > > +
> > > > +/**
> > > > + * Release the ticketlock.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + */
> > > > +static inline __rte_experimental void
> > > > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > > > +{
> > > > +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > > > +}
> > > > +
> > > > +/**
> > > > + * Try to take the lock.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + * @return
> > > > + *   1 if the lock is successfully taken; 0 otherwise.
> > > > + */
> > > > +static inline __rte_experimental int
> > > > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > > > +{
> > > > +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> > > > +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > > +	if (next == cur) {
> > >
> > > Probably a naïve one:
> > > Suppose next==cur==1 here, then this thread will experience really long
> > > context switch,
> >
> > By saying context switch, do you mean running to here, it is out of CPU time
> and starving for CPU?
> 
> Yes.
> 
> >
> > > so next time it continues its execution tl->next value will wrap-up and will
> > > be 1 again, and tl->current==0 (lock held).
> > > I suppose this function will set tl->next=2 and will return a success?
> >
> > If this thread was swapped out and another thread took/attempted to take
> the lock, yes, tl->next == 2 here,
> > But as next == 1 unchanged, so it would not return a success.
> 
> I am not talking about situation when tl->next == 2,tl->current==1 (just one
> lock() was executed by different thread).
> I am talking about situation when this thread was out of cpu for significant
> amount of cycles,
> and in that period tl->next and tl->current were wrapped around (they both
> reached UINT16_MAX, then 0).
> i.e. UINT16_MAX lock/unlock were executed while this thread was away from
> cpu.
> After that another thread just did successful lock(), so tl->next==1 and tl-
> >current==0.
> Now this thread wakeups and continues with:
> __atomic_compare_exchange_n(&tl->next, &next, next+1, ...)
> As both tl->next==1 and next==1, it will succeed.
> So we have 2 threads assuming they grabbed the lock successfully.
> Konstantin
> 
Now I understood your points, but not sure if it is a rare or even impossible case for this thread stalls for CPU and during this time, the other threads have taken the lock for 2^16 times, to wrap up. 

Anyway I made a patch, currently in internal review to fix this issue, the basic idea is to compare not only the next, but also the current, and update the next(+1 and take the lock) only if both of them were not changed(or wrapped up and the lock released).
I will submit the patch after internal review approved. Please let me know if you have more comments.

> >
> > > Wouldn't be better here and in _is_locked_ to do load/store for
> > > next/current values in one go
> > > (using 32bit op)?
> > > Konstantin
> >
> > To load both in one go is feasible, but no necessary as we need to compare
> them.
> > We don't need store both as in this function tl->current is read only.
> > tl->next is read-update-store, I ever thought of combining the two if-
> statements to one __atomic_compare_exchange_n(&(&tl->next,&tl-
> > >current, tl->next+1, ...),
> > but tl->next+1 is out of atomicity and may be the old value and corrupt the
> ticket lock waiting chain.
> >
> > The current code works ok except it may fail spuriously(in case during
> context switch, the lock was taken and released by other threads,
> > moving tl->next forward, in this case
> > The lock is available but not obtained by this trylock).  Anyway, as the name
> suggests, it is a try/attempt, a spurious fail is not a big deal?
> > And in most cases, dpdk running on dedicated cores,
> > the context switch will not happen at all.
> >
> > Any more comments are welcome!
> > >
> > > > +		if (__atomic_compare_exchange_n(&tl->next, &next,
> > > next+1,
> > > > +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> > > > +			return 1;
> > > > +	}
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +/**
> > > > + * Test if the lock is taken.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + * @return
> > > > + *   1 if the lock icurrently taken; 0 otherwise.
> > > > + */
> > > > +static inline __rte_experimental int
> > > > +rte_ticketlock_is_locked(rte_ticketlock_t *tl)
> > > > +{
> > > > +	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
> > > > +		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
> > > > +}
> > > > +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-20  5:11           ` Gavin Hu (Arm Technology China)
@ 2019-03-20  5:11             ` Gavin Hu (Arm Technology China)
  2019-03-20  9:47             ` Ananyev, Konstantin
  1 sibling, 0 replies; 74+ messages in thread
From: Gavin Hu (Arm Technology China) @ 2019-03-20  5:11 UTC (permalink / raw)
  To: Ananyev, Konstantin, dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China)

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="UTF-8", Size: 7690 bytes --]

Hi Konstantin,

> -----Original Message-----
> From: Ananyev, Konstantin <konstantin.ananyev@intel.com>
> Sent: Tuesday, March 19, 2019 6:15 PM
> To: Gavin Hu (Arm Technology China) <Gavin.Hu@arm.com>; dev@dpdk.org
> Cc: nd <nd@arm.com>; stephen@networkplumber.org;
> jerin.jacob@caviumnetworks.com; thomas@monjalon.net; Honnappa
> Nagarahalli <Honnappa.Nagarahalli@arm.com>; Joyce Kong (Arm Technology
> China) <Joyce.Kong@arm.com>
> Subject: RE: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve
> fairness
> 
> 
> Hi Gavin,
> 
> > >
> > > > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > new file mode 100644
> > > > index 0000000..d63aaaa
> > > > --- /dev/null
> > > > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > @@ -0,0 +1,308 @@
> > > > +/* SPDX-License-Identifier: BSD-3-Clause
> > > > + * Copyright(c) 2019 Arm Limited
> > > > + */
> > > > +
> > > > +#ifndef _RTE_TICKETLOCK_H_
> > > > +#define _RTE_TICKETLOCK_H_
> > > > +
> > > > +/**
> > > > + * @file
> > > > + *
> > > > + * RTE ticket locks
> > > > + *
> > > > + * This file defines an API for ticket locks, which give each waiting
> > > > + * thread a ticket and take the lock one by one, first come, first
> > > > + * serviced.
> > > > + *
> > > > + * All locks must be initialised before use, and only initialised once.
> > > > + *
> > > > + */
> > > > +
> > > > +#ifdef __cplusplus
> > > > +extern "C" {
> > > > +#endif
> > > > +
> > > > +#include <rte_common.h>
> > > > +#include <rte_lcore.h>
> > > > +#include <rte_pause.h>
> > > > +
> > > > +/**
> > > > + * The rte_ticketlock_t type.
> > > > + */
> > > > +typedef struct {
> > > > +	uint16_t current;
> > > > +	uint16_t next;
> > > > +} rte_ticketlock_t;
> > > > +
> > > > +/**
> > > > + * A static ticketlock initializer.
> > > > + */
> > > > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > > > +
> > > > +/**
> > > > + * Initialize the ticketlock to an unlocked state.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + */
> > > > +static inline __rte_experimental void
> > > > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > > > +{
> > > > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > > > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > > > +}
> > > > +
> > > > +/**
> > > > + * Take the ticketlock.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + */
> > > > +static inline __rte_experimental void
> > > > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > > > +{
> > > > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> > > __ATOMIC_RELAXED);
> > > > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > > > +		rte_pause();
> > > > +}
> > > > +
> > > > +/**
> > > > + * Release the ticketlock.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + */
> > > > +static inline __rte_experimental void
> > > > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > > > +{
> > > > +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > > > +}
> > > > +
> > > > +/**
> > > > + * Try to take the lock.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + * @return
> > > > + *   1 if the lock is successfully taken; 0 otherwise.
> > > > + */
> > > > +static inline __rte_experimental int
> > > > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > > > +{
> > > > +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> > > > +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > > +	if (next == cur) {
> > >
> > > Probably a naïve one:
> > > Suppose next==cur==1 here, then this thread will experience really long
> > > context switch,
> >
> > By saying context switch, do you mean running to here, it is out of CPU time
> and starving for CPU?
> 
> Yes.
> 
> >
> > > so next time it continues its execution tl->next value will wrap-up and will
> > > be 1 again, and tl->current==0 (lock held).
> > > I suppose this function will set tl->next=2 and will return a success?
> >
> > If this thread was swapped out and another thread took/attempted to take
> the lock, yes, tl->next == 2 here,
> > But as next == 1 unchanged, so it would not return a success.
> 
> I am not talking about situation when tl->next == 2,tl->current==1 (just one
> lock() was executed by different thread).
> I am talking about situation when this thread was out of cpu for significant
> amount of cycles,
> and in that period tl->next and tl->current were wrapped around (they both
> reached UINT16_MAX, then 0).
> i.e. UINT16_MAX lock/unlock were executed while this thread was away from
> cpu.
> After that another thread just did successful lock(), so tl->next==1 and tl-
> >current==0.
> Now this thread wakeups and continues with:
> __atomic_compare_exchange_n(&tl->next, &next, next+1, ...)
> As both tl->next==1 and next==1, it will succeed.
> So we have 2 threads assuming they grabbed the lock successfully.
> Konstantin
> 
Now I understood your points, but not sure if it is a rare or even impossible case for this thread stalls for CPU and during this time, the other threads have taken the lock for 2^16 times, to wrap up. 

Anyway I made a patch, currently in internal review to fix this issue, the basic idea is to compare not only the next, but also the current, and update the next(+1 and take the lock) only if both of them were not changed(or wrapped up and the lock released).
I will submit the patch after internal review approved. Please let me know if you have more comments.

> >
> > > Wouldn't be better here and in _is_locked_ to do load/store for
> > > next/current values in one go
> > > (using 32bit op)?
> > > Konstantin
> >
> > To load both in one go is feasible, but no necessary as we need to compare
> them.
> > We don't need store both as in this function tl->current is read only.
> > tl->next is read-update-store, I ever thought of combining the two if-
> statements to one __atomic_compare_exchange_n(&(&tl->next,&tl-
> > >current, tl->next+1, ...),
> > but tl->next+1 is out of atomicity and may be the old value and corrupt the
> ticket lock waiting chain.
> >
> > The current code works ok except it may fail spuriously(in case during
> context switch, the lock was taken and released by other threads,
> > moving tl->next forward, in this case
> > The lock is available but not obtained by this trylock).  Anyway, as the name
> suggests, it is a try/attempt, a spurious fail is not a big deal?
> > And in most cases, dpdk running on dedicated cores,
> > the context switch will not happen at all.
> >
> > Any more comments are welcome!
> > >
> > > > +		if (__atomic_compare_exchange_n(&tl->next, &next,
> > > next+1,
> > > > +		    0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> > > > +			return 1;
> > > > +	}
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +/**
> > > > + * Test if the lock is taken.
> > > > + *
> > > > + * @param tl
> > > > + *   A pointer to the ticketlock.
> > > > + * @return
> > > > + *   1 if the lock icurrently taken; 0 otherwise.
> > > > + */
> > > > +static inline __rte_experimental int
> > > > +rte_ticketlock_is_locked(rte_ticketlock_t *tl)
> > > > +{
> > > > +	return (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) !=
> > > > +		__atomic_load_n(&tl->next, __ATOMIC_ACQUIRE));
> > > > +}
> > > > +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-20  5:11           ` Gavin Hu (Arm Technology China)
  2019-03-20  5:11             ` Gavin Hu (Arm Technology China)
@ 2019-03-20  9:47             ` Ananyev, Konstantin
  2019-03-20  9:47               ` Ananyev, Konstantin
  2019-03-22  2:04               ` Gavin Hu (Arm Technology China)
  1 sibling, 2 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-20  9:47 UTC (permalink / raw)
  To: Gavin Hu (Arm Technology China), dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China)

Hi Gavin,
> > > >
> > > > > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > new file mode 100644
> > > > > index 0000000..d63aaaa
> > > > > --- /dev/null
> > > > > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > @@ -0,0 +1,308 @@
> > > > > +/* SPDX-License-Identifier: BSD-3-Clause
> > > > > + * Copyright(c) 2019 Arm Limited
> > > > > + */
> > > > > +
> > > > > +#ifndef _RTE_TICKETLOCK_H_
> > > > > +#define _RTE_TICKETLOCK_H_
> > > > > +
> > > > > +/**
> > > > > + * @file
> > > > > + *
> > > > > + * RTE ticket locks
> > > > > + *
> > > > > + * This file defines an API for ticket locks, which give each waiting
> > > > > + * thread a ticket and take the lock one by one, first come, first
> > > > > + * serviced.
> > > > > + *
> > > > > + * All locks must be initialised before use, and only initialised once.
> > > > > + *
> > > > > + */
> > > > > +
> > > > > +#ifdef __cplusplus
> > > > > +extern "C" {
> > > > > +#endif
> > > > > +
> > > > > +#include <rte_common.h>
> > > > > +#include <rte_lcore.h>
> > > > > +#include <rte_pause.h>
> > > > > +
> > > > > +/**
> > > > > + * The rte_ticketlock_t type.
> > > > > + */
> > > > > +typedef struct {
> > > > > +	uint16_t current;
> > > > > +	uint16_t next;
> > > > > +} rte_ticketlock_t;
> > > > > +
> > > > > +/**
> > > > > + * A static ticketlock initializer.
> > > > > + */
> > > > > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > > > > +
> > > > > +/**
> > > > > + * Initialize the ticketlock to an unlocked state.
> > > > > + *
> > > > > + * @param tl
> > > > > + *   A pointer to the ticketlock.
> > > > > + */
> > > > > +static inline __rte_experimental void
> > > > > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > > > > +{
> > > > > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > > > > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * Take the ticketlock.
> > > > > + *
> > > > > + * @param tl
> > > > > + *   A pointer to the ticketlock.
> > > > > + */
> > > > > +static inline __rte_experimental void
> > > > > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > > > > +{
> > > > > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> > > > __ATOMIC_RELAXED);
> > > > > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > > > > +		rte_pause();
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * Release the ticketlock.
> > > > > + *
> > > > > + * @param tl
> > > > > + *   A pointer to the ticketlock.
> > > > > + */
> > > > > +static inline __rte_experimental void
> > > > > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > > > > +{
> > > > > +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > > > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * Try to take the lock.
> > > > > + *
> > > > > + * @param tl
> > > > > + *   A pointer to the ticketlock.
> > > > > + * @return
> > > > > + *   1 if the lock is successfully taken; 0 otherwise.
> > > > > + */
> > > > > +static inline __rte_experimental int
> > > > > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > > > > +{
> > > > > +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> > > > > +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > > > +	if (next == cur) {
> > > >
> > > > Probably a naïve one:
> > > > Suppose next==cur==1 here, then this thread will experience really long
> > > > context switch,
> > >
> > > By saying context switch, do you mean running to here, it is out of CPU time
> > and starving for CPU?
> >
> > Yes.
> >
> > >
> > > > so next time it continues its execution tl->next value will wrap-up and will
> > > > be 1 again, and tl->current==0 (lock held).
> > > > I suppose this function will set tl->next=2 and will return a success?
> > >
> > > If this thread was swapped out and another thread took/attempted to take
> > the lock, yes, tl->next == 2 here,
> > > But as next == 1 unchanged, so it would not return a success.
> >
> > I am not talking about situation when tl->next == 2,tl->current==1 (just one
> > lock() was executed by different thread).
> > I am talking about situation when this thread was out of cpu for significant
> > amount of cycles,
> > and in that period tl->next and tl->current were wrapped around (they both
> > reached UINT16_MAX, then 0).
> > i.e. UINT16_MAX lock/unlock were executed while this thread was away from
> > cpu.
> > After that another thread just did successful lock(), so tl->next==1 and tl-
> > >current==0.
> > Now this thread wakeups and continues with:
> > __atomic_compare_exchange_n(&tl->next, &next, next+1, ...)
> > As both tl->next==1 and next==1, it will succeed.
> > So we have 2 threads assuming they grabbed the lock successfully.
> > Konstantin
> >
> Now I understood your points, but not sure if it is a rare or even impossible case for this thread stalls for CPU and during this time, the other
> threads have taken the lock for 2^16 times, to wrap up.

I am agree it should be very rare, but I am not sure it is impossible.
Let say thread is doing lock/unlock in a loop, with one iteration ~100 cycles.
Then it would wrap around in ~6.5M cycles (~3ms on modern cpus).

> 
> Anyway I made a patch, currently in internal review to fix this issue, the basic idea is to compare not only the next, but also the current, and
> update the next(+1 and take the lock) only if both of them were not changed(or wrapped up and the lock released).
> I will submit the patch after internal review approved. Please let me know if you have more comments.

Ok, thanks
Konstantin

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-20  9:47             ` Ananyev, Konstantin
@ 2019-03-20  9:47               ` Ananyev, Konstantin
  2019-03-22  2:04               ` Gavin Hu (Arm Technology China)
  1 sibling, 0 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-20  9:47 UTC (permalink / raw)
  To: Gavin Hu (Arm Technology China), dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China)

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="UTF-8", Size: 5843 bytes --]

Hi Gavin,
> > > >
> > > > > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > new file mode 100644
> > > > > index 0000000..d63aaaa
> > > > > --- /dev/null
> > > > > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > @@ -0,0 +1,308 @@
> > > > > +/* SPDX-License-Identifier: BSD-3-Clause
> > > > > + * Copyright(c) 2019 Arm Limited
> > > > > + */
> > > > > +
> > > > > +#ifndef _RTE_TICKETLOCK_H_
> > > > > +#define _RTE_TICKETLOCK_H_
> > > > > +
> > > > > +/**
> > > > > + * @file
> > > > > + *
> > > > > + * RTE ticket locks
> > > > > + *
> > > > > + * This file defines an API for ticket locks, which give each waiting
> > > > > + * thread a ticket and take the lock one by one, first come, first
> > > > > + * serviced.
> > > > > + *
> > > > > + * All locks must be initialised before use, and only initialised once.
> > > > > + *
> > > > > + */
> > > > > +
> > > > > +#ifdef __cplusplus
> > > > > +extern "C" {
> > > > > +#endif
> > > > > +
> > > > > +#include <rte_common.h>
> > > > > +#include <rte_lcore.h>
> > > > > +#include <rte_pause.h>
> > > > > +
> > > > > +/**
> > > > > + * The rte_ticketlock_t type.
> > > > > + */
> > > > > +typedef struct {
> > > > > +	uint16_t current;
> > > > > +	uint16_t next;
> > > > > +} rte_ticketlock_t;
> > > > > +
> > > > > +/**
> > > > > + * A static ticketlock initializer.
> > > > > + */
> > > > > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > > > > +
> > > > > +/**
> > > > > + * Initialize the ticketlock to an unlocked state.
> > > > > + *
> > > > > + * @param tl
> > > > > + *   A pointer to the ticketlock.
> > > > > + */
> > > > > +static inline __rte_experimental void
> > > > > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > > > > +{
> > > > > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > > > > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * Take the ticketlock.
> > > > > + *
> > > > > + * @param tl
> > > > > + *   A pointer to the ticketlock.
> > > > > + */
> > > > > +static inline __rte_experimental void
> > > > > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > > > > +{
> > > > > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> > > > __ATOMIC_RELAXED);
> > > > > +	while (__atomic_load_n(&tl->current, __ATOMIC_ACQUIRE) != me)
> > > > > +		rte_pause();
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * Release the ticketlock.
> > > > > + *
> > > > > + * @param tl
> > > > > + *   A pointer to the ticketlock.
> > > > > + */
> > > > > +static inline __rte_experimental void
> > > > > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > > > > +{
> > > > > +	uint16_t i = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > > > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * Try to take the lock.
> > > > > + *
> > > > > + * @param tl
> > > > > + *   A pointer to the ticketlock.
> > > > > + * @return
> > > > > + *   1 if the lock is successfully taken; 0 otherwise.
> > > > > + */
> > > > > +static inline __rte_experimental int
> > > > > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > > > > +{
> > > > > +	uint16_t next = __atomic_load_n(&tl->next, __ATOMIC_RELAXED);
> > > > > +	uint16_t cur = __atomic_load_n(&tl->current, __ATOMIC_RELAXED);
> > > > > +	if (next == cur) {
> > > >
> > > > Probably a naïve one:
> > > > Suppose next==cur==1 here, then this thread will experience really long
> > > > context switch,
> > >
> > > By saying context switch, do you mean running to here, it is out of CPU time
> > and starving for CPU?
> >
> > Yes.
> >
> > >
> > > > so next time it continues its execution tl->next value will wrap-up and will
> > > > be 1 again, and tl->current==0 (lock held).
> > > > I suppose this function will set tl->next=2 and will return a success?
> > >
> > > If this thread was swapped out and another thread took/attempted to take
> > the lock, yes, tl->next == 2 here,
> > > But as next == 1 unchanged, so it would not return a success.
> >
> > I am not talking about situation when tl->next == 2,tl->current==1 (just one
> > lock() was executed by different thread).
> > I am talking about situation when this thread was out of cpu for significant
> > amount of cycles,
> > and in that period tl->next and tl->current were wrapped around (they both
> > reached UINT16_MAX, then 0).
> > i.e. UINT16_MAX lock/unlock were executed while this thread was away from
> > cpu.
> > After that another thread just did successful lock(), so tl->next==1 and tl-
> > >current==0.
> > Now this thread wakeups and continues with:
> > __atomic_compare_exchange_n(&tl->next, &next, next+1, ...)
> > As both tl->next==1 and next==1, it will succeed.
> > So we have 2 threads assuming they grabbed the lock successfully.
> > Konstantin
> >
> Now I understood your points, but not sure if it is a rare or even impossible case for this thread stalls for CPU and during this time, the other
> threads have taken the lock for 2^16 times, to wrap up.

I am agree it should be very rare, but I am not sure it is impossible.
Let say thread is doing lock/unlock in a loop, with one iteration ~100 cycles.
Then it would wrap around in ~6.5M cycles (~3ms on modern cpus).

> 
> Anyway I made a patch, currently in internal review to fix this issue, the basic idea is to compare not only the next, but also the current, and
> update the next(+1 and take the lock) only if both of them were not changed(or wrapped up and the lock released).
> I will submit the patch after internal review approved. Please let me know if you have more comments.

Ok, thanks
Konstantin


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v7 0/3] ticketlock: implement ticketlock and add test case
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (5 preceding siblings ...)
  2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 2/2] test/ticketlock: add ticket lock test case Joyce Kong
@ 2019-03-21  9:13   ` Joyce Kong
  2019-03-21  9:13     ` Joyce Kong
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 2/3] eal/ticketlock: enable generic ticketlock on all arch Joyce Kong
                     ` (6 subsequent siblings)
  13 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-03-21  9:13 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

v7:
    1. Modify trylock to compare both current and next fields to gurantee the lock
    being obtained only if tl->current==1(lock is free).
    As within the trylock function, suppose next==curr=1, then this thread will
    experience really long context switch, and next time it continues its
    execution and tl->next wraps around to 1 again and tl_couurent==0(lock
    held by another thread),this trylock will return a success, that means
    two threads holding the lock. (Suggested by Konstantin Ananyev)
    2. Let all archs use generic ticketlock implementation.

v6:
    Add rte_ticketlock.h in lib/librte_eal/common/include/arch/arm/.
    Sort header inside ticketlock files by alphabetical order.

v5:
    Change the variants inside rte_ticket_lock from unint to uint16_t for binary
    compatibility with the plain spin lock(suggested by Honnappa Nagarahalli)).

v4:
    Change some assignment operation in recursive ticket lock to __atomic.

V3:
    1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
    2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).

V2:
    1.Update commit message(suggested by Jerin Jacob).
    2.Add ticketlock test cases(suggested by Jerin Jacob).

V1:
    Implement ticket lock to improve lock fairness and prdictability.

    As shown on thundex-2 platform:
    *** ticketlock_autotest with this patch ***
        Core [0] count = 496
        Core [1] count = 495
        Core [2] count = 498
        ...
        Core [209] count = 488
        Core [210] count = 490
        Core [211] count = 474

Joyce Kong (3):
  eal/ticketlock: ticket based to improve fairness
  eal/ticketlock: enable generic ticketlock on all arch
  test/ticketlock: add ticket lock test case

 MAINTAINERS                                        |   5 +
 app/test/Makefile                                  |   1 +
 app/test/autotest_data.py                          |   6 +
 app/test/meson.build                               |   1 +
 app/test/test_ticketlock.c                         | 311 +++++++++++++++++++++
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/arch/arm/rte_ticketlock.h       |  22 ++
 .../common/include/arch/ppc_64/rte_ticketlock.h    |  18 ++
 .../common/include/arch/x86/rte_ticketlock.h       |  18 ++
 .../common/include/generic/rte_ticketlock.h        | 215 ++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 12 files changed, 600 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_ticketlock.c
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v7 0/3] ticketlock: implement ticketlock and add test case
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 0/3] ticketlock: implement ticketlock and add " Joyce Kong
@ 2019-03-21  9:13     ` Joyce Kong
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-21  9:13 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

v7:
    1. Modify trylock to compare both current and next fields to gurantee the lock
    being obtained only if tl->current==1(lock is free).
    As within the trylock function, suppose next==curr=1, then this thread will
    experience really long context switch, and next time it continues its
    execution and tl->next wraps around to 1 again and tl_couurent==0(lock
    held by another thread),this trylock will return a success, that means
    two threads holding the lock. (Suggested by Konstantin Ananyev)
    2. Let all archs use generic ticketlock implementation.

v6:
    Add rte_ticketlock.h in lib/librte_eal/common/include/arch/arm/.
    Sort header inside ticketlock files by alphabetical order.

v5:
    Change the variants inside rte_ticket_lock from unint to uint16_t for binary
    compatibility with the plain spin lock(suggested by Honnappa Nagarahalli)).

v4:
    Change some assignment operation in recursive ticket lock to __atomic.

V3:
    1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
    2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).

V2:
    1.Update commit message(suggested by Jerin Jacob).
    2.Add ticketlock test cases(suggested by Jerin Jacob).

V1:
    Implement ticket lock to improve lock fairness and prdictability.

    As shown on thundex-2 platform:
    *** ticketlock_autotest with this patch ***
        Core [0] count = 496
        Core [1] count = 495
        Core [2] count = 498
        ...
        Core [209] count = 488
        Core [210] count = 490
        Core [211] count = 474

Joyce Kong (3):
  eal/ticketlock: ticket based to improve fairness
  eal/ticketlock: enable generic ticketlock on all arch
  test/ticketlock: add ticket lock test case

 MAINTAINERS                                        |   5 +
 app/test/Makefile                                  |   1 +
 app/test/autotest_data.py                          |   6 +
 app/test/meson.build                               |   1 +
 app/test/test_ticketlock.c                         | 311 +++++++++++++++++++++
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/arch/arm/rte_ticketlock.h       |  22 ++
 .../common/include/arch/ppc_64/rte_ticketlock.h    |  18 ++
 .../common/include/arch/x86/rte_ticketlock.h       |  18 ++
 .../common/include/generic/rte_ticketlock.h        | 215 ++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 12 files changed, 600 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_ticketlock.c
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v7 2/3] eal/ticketlock: enable generic ticketlock on all arch
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (6 preceding siblings ...)
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 0/3] ticketlock: implement ticketlock and add " Joyce Kong
@ 2019-03-21  9:13   ` Joyce Kong
  2019-03-21  9:13     ` Joyce Kong
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case Joyce Kong
                     ` (5 subsequent siblings)
  13 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-03-21  9:13 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

Let all architectures use generic ticketlock implementation.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
---
 .../common/include/arch/arm/rte_ticketlock.h       | 22 ++++++++++++++++++++++
 .../common/include/arch/ppc_64/rte_ticketlock.h    | 18 ++++++++++++++++++
 .../common/include/arch/x86/rte_ticketlock.h       | 18 ++++++++++++++++++
 3 files changed, 58 insertions(+)
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h

diff --git a/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
new file mode 100644
index 0000000..e09fbd6
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_ARM_H_
+#define _RTE_TICKETLOCK_ARM_H_
+
+#ifndef RTE_FORCE_INTRINSICS
+#  error Platform must be built with CONFIG_RTE_FORCE_INTRINSICS
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_ARM_H_ */
diff --git a/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h b/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
new file mode 100644
index 0000000..c175e9e
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_PPC_64_H_
+#define _RTE_TICKETLOCK_PPC_64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_PPC_64_H_ */
diff --git a/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h b/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
new file mode 100644
index 0000000..0cc01f6
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_X86_64_H_
+#define _RTE_TICKETLOCK_X86_64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_X86_64_H_ */
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v7 2/3] eal/ticketlock: enable generic ticketlock on all arch
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 2/3] eal/ticketlock: enable generic ticketlock on all arch Joyce Kong
@ 2019-03-21  9:13     ` Joyce Kong
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-21  9:13 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

Let all architectures use generic ticketlock implementation.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
---
 .../common/include/arch/arm/rte_ticketlock.h       | 22 ++++++++++++++++++++++
 .../common/include/arch/ppc_64/rte_ticketlock.h    | 18 ++++++++++++++++++
 .../common/include/arch/x86/rte_ticketlock.h       | 18 ++++++++++++++++++
 3 files changed, 58 insertions(+)
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h

diff --git a/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
new file mode 100644
index 0000000..e09fbd6
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_ARM_H_
+#define _RTE_TICKETLOCK_ARM_H_
+
+#ifndef RTE_FORCE_INTRINSICS
+#  error Platform must be built with CONFIG_RTE_FORCE_INTRINSICS
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_ARM_H_ */
diff --git a/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h b/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
new file mode 100644
index 0000000..c175e9e
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_PPC_64_H_
+#define _RTE_TICKETLOCK_PPC_64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_PPC_64_H_ */
diff --git a/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h b/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
new file mode 100644
index 0000000..0cc01f6
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_X86_64_H_
+#define _RTE_TICKETLOCK_X86_64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_X86_64_H_ */
-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (7 preceding siblings ...)
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 2/3] eal/ticketlock: enable generic ticketlock on all arch Joyce Kong
@ 2019-03-21  9:13   ` Joyce Kong
  2019-03-21  9:13     ` Joyce Kong
  2019-03-22 11:38     ` Ananyev, Konstantin
  2019-03-21  9:15   ` [dpdk-dev] [PATCH v7 1/3] eal/ticketlock: ticket based to improve fairness Joyce Kong
                     ` (4 subsequent siblings)
  13 siblings, 2 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-21  9:13 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ruifeng Wang <ruifeng.wang@arm.com>
---
 MAINTAINERS                |   1 +
 app/test/Makefile          |   1 +
 app/test/autotest_data.py  |   6 +
 app/test/meson.build       |   1 +
 app/test/test_ticketlock.c | 311 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 320 insertions(+)
 create mode 100644 app/test/test_ticketlock.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3521271..b1ed4cc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -213,6 +213,7 @@ F: app/test/test_bitmap.c
 Ticketlock
 M: Joyce Kong <joyce.kong@arm.com>
 F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+F: app/test/test_ticketlock.c
 
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
diff --git a/app/test/Makefile b/app/test/Makefile
index 89949c2..d6aa28b 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 5f87bb9..db25274 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report":  None,
+    }
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 05e5dde..ddb4d09 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -107,6 +107,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
new file mode 100644
index 0000000..67281ce
--- /dev/null
+++ b/app/test/test_ticketlock.c
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_atomic.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_ticketlock.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t tl, tl_try;
+static rte_ticketlock_t tl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t tlr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&tl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl);
+
+	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lock_count[RTE_MAX_LCORE] = {0};
+
+#define TIME_MS 100
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	uint64_t lcount = 0;
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_get_timer_cycles();
+	while (time_diff < hz * TIME_MS / 1000) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+		/* delay to make lock duty cycle slighlty realistic */
+		rte_delay_us(1);
+		time_diff = rte_get_timer_cycles() - begin;
+	}
+	lock_count[lcore] = lcount;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t total = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
+		total += lock_count[i];
+	}
+
+	printf("Total count = %"PRIu64"\n", total);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&tl_try) == 0) {
+		rte_ticketlock_lock(&tl);
+		count++;
+		rte_ticketlock_unlock(&tl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&tl);
+	rte_ticketlock_init(&tl_try);
+	rte_ticketlock_recursive_init(&tlr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&tl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&tl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&tl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&tlr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&tlr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&tlr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+					NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&tl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&tl_try);
+	if (rte_ticketlock_is_locked(&tl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&tl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&tl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_ticketlock_recursive_unlock(&tlr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case Joyce Kong
@ 2019-03-21  9:13     ` Joyce Kong
  2019-03-22 11:38     ` Ananyev, Konstantin
  1 sibling, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-21  9:13 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ruifeng Wang <ruifeng.wang@arm.com>
---
 MAINTAINERS                |   1 +
 app/test/Makefile          |   1 +
 app/test/autotest_data.py  |   6 +
 app/test/meson.build       |   1 +
 app/test/test_ticketlock.c | 311 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 320 insertions(+)
 create mode 100644 app/test/test_ticketlock.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3521271..b1ed4cc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -213,6 +213,7 @@ F: app/test/test_bitmap.c
 Ticketlock
 M: Joyce Kong <joyce.kong@arm.com>
 F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+F: app/test/test_ticketlock.c
 
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
diff --git a/app/test/Makefile b/app/test/Makefile
index 89949c2..d6aa28b 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 5f87bb9..db25274 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report":  None,
+    }
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 05e5dde..ddb4d09 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -107,6 +107,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
new file mode 100644
index 0000000..67281ce
--- /dev/null
+++ b/app/test/test_ticketlock.c
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_atomic.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_ticketlock.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t tl, tl_try;
+static rte_ticketlock_t tl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t tlr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&tl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl);
+
+	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lock_count[RTE_MAX_LCORE] = {0};
+
+#define TIME_MS 100
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	uint64_t lcount = 0;
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_get_timer_cycles();
+	while (time_diff < hz * TIME_MS / 1000) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+		/* delay to make lock duty cycle slighlty realistic */
+		rte_delay_us(1);
+		time_diff = rte_get_timer_cycles() - begin;
+	}
+	lock_count[lcore] = lcount;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t total = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
+	memset(lock_count, 0, sizeof(lock_count));
+
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
+		total += lock_count[i];
+	}
+
+	printf("Total count = %"PRIu64"\n", total);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&tl_try) == 0) {
+		rte_ticketlock_lock(&tl);
+		count++;
+		rte_ticketlock_unlock(&tl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&tl);
+	rte_ticketlock_init(&tl_try);
+	rte_ticketlock_recursive_init(&tlr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&tl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&tl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&tl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&tlr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&tlr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&tlr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+					NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&tl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&tl_try);
+	if (rte_ticketlock_is_locked(&tl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&tl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&tl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_ticketlock_recursive_unlock(&tlr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v7 1/3] eal/ticketlock: ticket based to improve fairness
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (8 preceding siblings ...)
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case Joyce Kong
@ 2019-03-21  9:15   ` Joyce Kong
  2019-03-21  9:15     ` Joyce Kong
  2019-03-22 10:56     ` Ananyev, Konstantin
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case Joyce Kong
                     ` (3 subsequent siblings)
  13 siblings, 2 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-21  9:15 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.

This patch introduces ticketlock which gives each waiting thread a
ticket and they can take the lock one by one. First come, first serviced.
This avoids starvation for too long time and is more predictable.

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
---
 MAINTAINERS                                        |   4 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 215 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 5 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 452b8eb..3521271 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -210,6 +210,10 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 F: lib/librte_eal/common/include/rte_bitmap.h
 F: app/test/test_bitmap.c
 
+Ticketlock
+M: Joyce Kong <joyce.kong@arm.com>
+F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
 M: Gavin Hu <gavin.hu@arm.com>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index c487201..ac3305c 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..c5203cb
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticket locks
+ *
+ * This file defines an API for ticket locks, which give each waiting
+ * thread a ticket and take the lock one by one, first come, first
+ * serviced.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_lcore.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef union {
+	uint32_t tickets;
+	struct {
+		uint16_t current;
+		uint16_t next;
+	} s;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->tickets, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->s.next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->s.current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	uint16_t i = __atomic_load_n(&tl->s.current, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->s.current, i + 1, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_t old, new;
+	old.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);
+	new.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);
+	new.s.next++;
+	if (old.s.next == old.s.current) {
+		if (__atomic_compare_exchange_n(&tl->tickets, &old.tickets,
+		    new.tickets, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is currently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_t tic;
+	tic.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_ACQUIRE);
+	return (tic.s.current != tic.s.next);
+}
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+#define TICKET_LOCK_INVALID_ID -1
+
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	int user; /**< core id using lock, TICKET_LOCK_INVALID_ID for unused */
+	unsigned int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, \
+					       TICKET_LOCK_INVALID_ID, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_init(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED);
+	tlr->count = 0;
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_lock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_unlock(rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID,
+				 __ATOMIC_RELAXED);
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_recursive_trylock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+	return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 5ecae0b..0670e41 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -99,6 +99,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v7 1/3] eal/ticketlock: ticket based to improve fairness
  2019-03-21  9:15   ` [dpdk-dev] [PATCH v7 1/3] eal/ticketlock: ticket based to improve fairness Joyce Kong
@ 2019-03-21  9:15     ` Joyce Kong
  2019-03-22 10:56     ` Ananyev, Konstantin
  1 sibling, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-21  9:15 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.

This patch introduces ticketlock which gives each waiting thread a
ticket and they can take the lock one by one. First come, first serviced.
This avoids starvation for too long time and is more predictable.

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
---
 MAINTAINERS                                        |   4 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 215 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 5 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 452b8eb..3521271 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -210,6 +210,10 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 F: lib/librte_eal/common/include/rte_bitmap.h
 F: app/test/test_bitmap.c
 
+Ticketlock
+M: Joyce Kong <joyce.kong@arm.com>
+F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
 M: Gavin Hu <gavin.hu@arm.com>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index c487201..ac3305c 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..c5203cb
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticket locks
+ *
+ * This file defines an API for ticket locks, which give each waiting
+ * thread a ticket and take the lock one by one, first come, first
+ * serviced.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_lcore.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef union {
+	uint32_t tickets;
+	struct {
+		uint16_t current;
+		uint16_t next;
+	} s;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->tickets, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->s.next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->s.current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	uint16_t i = __atomic_load_n(&tl->s.current, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->s.current, i + 1, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_t old, new;
+	old.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);
+	new.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);
+	new.s.next++;
+	if (old.s.next == old.s.current) {
+		if (__atomic_compare_exchange_n(&tl->tickets, &old.tickets,
+		    new.tickets, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is currently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_t tic;
+	tic.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_ACQUIRE);
+	return (tic.s.current != tic.s.next);
+}
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+#define TICKET_LOCK_INVALID_ID -1
+
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	int user; /**< core id using lock, TICKET_LOCK_INVALID_ID for unused */
+	unsigned int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, \
+					       TICKET_LOCK_INVALID_ID, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_init(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED);
+	tlr->count = 0;
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_lock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_unlock(rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID,
+				 __ATOMIC_RELAXED);
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_recursive_trylock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+	return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 5ecae0b..0670e41 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -99,6 +99,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-20  9:47             ` Ananyev, Konstantin
  2019-03-20  9:47               ` Ananyev, Konstantin
@ 2019-03-22  2:04               ` Gavin Hu (Arm Technology China)
  2019-03-22  2:04                 ` Gavin Hu (Arm Technology China)
  1 sibling, 1 reply; 74+ messages in thread
From: Gavin Hu (Arm Technology China) @ 2019-03-22  2:04 UTC (permalink / raw)
  To: Ananyev, Konstantin, dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China)

Hi Konstantin, 

> -----Original Message-----
> From: Ananyev, Konstantin <konstantin.ananyev@intel.com>
> Sent: Wednesday, March 20, 2019 5:47 PM
> To: Gavin Hu (Arm Technology China) <Gavin.Hu@arm.com>; dev@dpdk.org
> Cc: nd <nd@arm.com>; stephen@networkplumber.org;
> jerin.jacob@caviumnetworks.com; thomas@monjalon.net; Honnappa
> Nagarahalli <Honnappa.Nagarahalli@arm.com>; Joyce Kong (Arm Technology
> China) <Joyce.Kong@arm.com>
> Subject: RE: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve
> fairness
> 
> Hi Gavin,
> > > > >
> > > > > > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > > new file mode 100644
> > > > > > index 0000000..d63aaaa
> > > > > > --- /dev/null
> > > > > > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > > @@ -0,0 +1,308 @@
> > > > > > +/* SPDX-License-Identifier: BSD-3-Clause
> > > > > > + * Copyright(c) 2019 Arm Limited
> > > > > > + */
> > > > > > +
> > > > > > +#ifndef _RTE_TICKETLOCK_H_
> > > > > > +#define _RTE_TICKETLOCK_H_
> > > > > > +
> > > > > > +/**
> > > > > > + * @file
> > > > > > + *
> > > > > > + * RTE ticket locks
> > > > > > + *
> > > > > > + * This file defines an API for ticket locks, which give each waiting
> > > > > > + * thread a ticket and take the lock one by one, first come, first
> > > > > > + * serviced.
> > > > > > + *
> > > > > > + * All locks must be initialised before use, and only initialised once.
> > > > > > + *
> > > > > > + */
> > > > > > +
> > > > > > +#ifdef __cplusplus
> > > > > > +extern "C" {
> > > > > > +#endif
> > > > > > +
> > > > > > +#include <rte_common.h>
> > > > > > +#include <rte_lcore.h>
> > > > > > +#include <rte_pause.h>
> > > > > > +
> > > > > > +/**
> > > > > > + * The rte_ticketlock_t type.
> > > > > > + */
> > > > > > +typedef struct {
> > > > > > +	uint16_t current;
> > > > > > +	uint16_t next;
> > > > > > +} rte_ticketlock_t;
> > > > > > +
> > > > > > +/**
> > > > > > + * A static ticketlock initializer.
> > > > > > + */
> > > > > > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > > > > > +
> > > > > > +/**
> > > > > > + * Initialize the ticketlock to an unlocked state.
> > > > > > + *
> > > > > > + * @param tl
> > > > > > + *   A pointer to the ticketlock.
> > > > > > + */
> > > > > > +static inline __rte_experimental void
> > > > > > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > > > > > +{
> > > > > > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > > > > > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > > > > > +}
> > > > > > +
> > > > > > +/**
> > > > > > + * Take the ticketlock.
> > > > > > + *
> > > > > > + * @param tl
> > > > > > + *   A pointer to the ticketlock.
> > > > > > + */
> > > > > > +static inline __rte_experimental void
> > > > > > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > > > > > +{
> > > > > > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> > > > > __ATOMIC_RELAXED);
> > > > > > +	while (__atomic_load_n(&tl->current,
> __ATOMIC_ACQUIRE) != me)
> > > > > > +		rte_pause();
> > > > > > +}
> > > > > > +
> > > > > > +/**
> > > > > > + * Release the ticketlock.
> > > > > > + *
> > > > > > + * @param tl
> > > > > > + *   A pointer to the ticketlock.
> > > > > > + */
> > > > > > +static inline __rte_experimental void
> > > > > > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > > > > > +{
> > > > > > +	uint16_t i = __atomic_load_n(&tl->current,
> __ATOMIC_RELAXED);
> > > > > > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > > > > > +}
> > > > > > +
> > > > > > +/**
> > > > > > + * Try to take the lock.
> > > > > > + *
> > > > > > + * @param tl
> > > > > > + *   A pointer to the ticketlock.
> > > > > > + * @return
> > > > > > + *   1 if the lock is successfully taken; 0 otherwise.
> > > > > > + */
> > > > > > +static inline __rte_experimental int
> > > > > > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > > > > > +{
> > > > > > +	uint16_t next = __atomic_load_n(&tl->next,
> __ATOMIC_RELAXED);
> > > > > > +	uint16_t cur = __atomic_load_n(&tl->current,
> __ATOMIC_RELAXED);
> > > > > > +	if (next == cur) {
> > > > >
> > > > > Probably a naïve one:
> > > > > Suppose next==cur==1 here, then this thread will experience really
> long
> > > > > context switch,
> > > >
> > > > By saying context switch, do you mean running to here, it is out of CPU
> time
> > > and starving for CPU?
> > >
> > > Yes.
> > >
> > > >
> > > > > so next time it continues its execution tl->next value will wrap-up and
> will
> > > > > be 1 again, and tl->current==0 (lock held).
> > > > > I suppose this function will set tl->next=2 and will return a success?
> > > >
> > > > If this thread was swapped out and another thread took/attempted to
> take
> > > the lock, yes, tl->next == 2 here,
> > > > But as next == 1 unchanged, so it would not return a success.
> > >
> > > I am not talking about situation when tl->next == 2,tl->current==1 (just
> one
> > > lock() was executed by different thread).
> > > I am talking about situation when this thread was out of cpu for significant
> > > amount of cycles,
> > > and in that period tl->next and tl->current were wrapped around (they
> both
> > > reached UINT16_MAX, then 0).
> > > i.e. UINT16_MAX lock/unlock were executed while this thread was away
> from
> > > cpu.
> > > After that another thread just did successful lock(), so tl->next==1 and tl-
> > > >current==0.
> > > Now this thread wakeups and continues with:
> > > __atomic_compare_exchange_n(&tl->next, &next, next+1, ...)
> > > As both tl->next==1 and next==1, it will succeed.
> > > So we have 2 threads assuming they grabbed the lock successfully.
> > > Konstantin
> > >
> > Now I understood your points, but not sure if it is a rare or even impossible
> case for this thread stalls for CPU and during this time, the other
> > threads have taken the lock for 2^16 times, to wrap up.
> 
> I am agree it should be very rare, but I am not sure it is impossible.
> Let say thread is doing lock/unlock in a loop, with one iteration ~100 cycles.
> Then it would wrap around in ~6.5M cycles (~3ms on modern cpus).
> 

Thanks, agree, your quantitative way of analysis  helped me understand the issue.

> >
> > Anyway I made a patch, currently in internal review to fix this issue, the
> basic idea is to compare not only the next, but also the current, and
> > update the next(+1 and take the lock) only if both of them were not
> changed(or wrapped up and the lock released).
> > I will submit the patch after internal review approved. Please let me know if
> you have more comments.
> 
> Ok, thanks
> Konstantin

The fix was submitted into v7, 1/3, could you help review? 

Thanks!
Gavin

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness
  2019-03-22  2:04               ` Gavin Hu (Arm Technology China)
@ 2019-03-22  2:04                 ` Gavin Hu (Arm Technology China)
  0 siblings, 0 replies; 74+ messages in thread
From: Gavin Hu (Arm Technology China) @ 2019-03-22  2:04 UTC (permalink / raw)
  To: Ananyev, Konstantin, dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Joyce Kong (Arm Technology China)

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="UTF-8", Size: 6907 bytes --]

Hi Konstantin, 

> -----Original Message-----
> From: Ananyev, Konstantin <konstantin.ananyev@intel.com>
> Sent: Wednesday, March 20, 2019 5:47 PM
> To: Gavin Hu (Arm Technology China) <Gavin.Hu@arm.com>; dev@dpdk.org
> Cc: nd <nd@arm.com>; stephen@networkplumber.org;
> jerin.jacob@caviumnetworks.com; thomas@monjalon.net; Honnappa
> Nagarahalli <Honnappa.Nagarahalli@arm.com>; Joyce Kong (Arm Technology
> China) <Joyce.Kong@arm.com>
> Subject: RE: [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve
> fairness
> 
> Hi Gavin,
> > > > >
> > > > > > diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > > new file mode 100644
> > > > > > index 0000000..d63aaaa
> > > > > > --- /dev/null
> > > > > > +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> > > > > > @@ -0,0 +1,308 @@
> > > > > > +/* SPDX-License-Identifier: BSD-3-Clause
> > > > > > + * Copyright(c) 2019 Arm Limited
> > > > > > + */
> > > > > > +
> > > > > > +#ifndef _RTE_TICKETLOCK_H_
> > > > > > +#define _RTE_TICKETLOCK_H_
> > > > > > +
> > > > > > +/**
> > > > > > + * @file
> > > > > > + *
> > > > > > + * RTE ticket locks
> > > > > > + *
> > > > > > + * This file defines an API for ticket locks, which give each waiting
> > > > > > + * thread a ticket and take the lock one by one, first come, first
> > > > > > + * serviced.
> > > > > > + *
> > > > > > + * All locks must be initialised before use, and only initialised once.
> > > > > > + *
> > > > > > + */
> > > > > > +
> > > > > > +#ifdef __cplusplus
> > > > > > +extern "C" {
> > > > > > +#endif
> > > > > > +
> > > > > > +#include <rte_common.h>
> > > > > > +#include <rte_lcore.h>
> > > > > > +#include <rte_pause.h>
> > > > > > +
> > > > > > +/**
> > > > > > + * The rte_ticketlock_t type.
> > > > > > + */
> > > > > > +typedef struct {
> > > > > > +	uint16_t current;
> > > > > > +	uint16_t next;
> > > > > > +} rte_ticketlock_t;
> > > > > > +
> > > > > > +/**
> > > > > > + * A static ticketlock initializer.
> > > > > > + */
> > > > > > +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> > > > > > +
> > > > > > +/**
> > > > > > + * Initialize the ticketlock to an unlocked state.
> > > > > > + *
> > > > > > + * @param tl
> > > > > > + *   A pointer to the ticketlock.
> > > > > > + */
> > > > > > +static inline __rte_experimental void
> > > > > > +rte_ticketlock_init(rte_ticketlock_t *tl)
> > > > > > +{
> > > > > > +	__atomic_store_n(&tl->current, 0, __ATOMIC_RELAXED);
> > > > > > +	__atomic_store_n(&tl->next, 0, __ATOMIC_RELAXED);
> > > > > > +}
> > > > > > +
> > > > > > +/**
> > > > > > + * Take the ticketlock.
> > > > > > + *
> > > > > > + * @param tl
> > > > > > + *   A pointer to the ticketlock.
> > > > > > + */
> > > > > > +static inline __rte_experimental void
> > > > > > +rte_ticketlock_lock(rte_ticketlock_t *tl)
> > > > > > +{
> > > > > > +	uint16_t me = __atomic_fetch_add(&tl->next, 1,
> > > > > __ATOMIC_RELAXED);
> > > > > > +	while (__atomic_load_n(&tl->current,
> __ATOMIC_ACQUIRE) != me)
> > > > > > +		rte_pause();
> > > > > > +}
> > > > > > +
> > > > > > +/**
> > > > > > + * Release the ticketlock.
> > > > > > + *
> > > > > > + * @param tl
> > > > > > + *   A pointer to the ticketlock.
> > > > > > + */
> > > > > > +static inline __rte_experimental void
> > > > > > +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> > > > > > +{
> > > > > > +	uint16_t i = __atomic_load_n(&tl->current,
> __ATOMIC_RELAXED);
> > > > > > +	__atomic_store_n(&tl->current, i+1, __ATOMIC_RELEASE);
> > > > > > +}
> > > > > > +
> > > > > > +/**
> > > > > > + * Try to take the lock.
> > > > > > + *
> > > > > > + * @param tl
> > > > > > + *   A pointer to the ticketlock.
> > > > > > + * @return
> > > > > > + *   1 if the lock is successfully taken; 0 otherwise.
> > > > > > + */
> > > > > > +static inline __rte_experimental int
> > > > > > +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> > > > > > +{
> > > > > > +	uint16_t next = __atomic_load_n(&tl->next,
> __ATOMIC_RELAXED);
> > > > > > +	uint16_t cur = __atomic_load_n(&tl->current,
> __ATOMIC_RELAXED);
> > > > > > +	if (next == cur) {
> > > > >
> > > > > Probably a naïve one:
> > > > > Suppose next==cur==1 here, then this thread will experience really
> long
> > > > > context switch,
> > > >
> > > > By saying context switch, do you mean running to here, it is out of CPU
> time
> > > and starving for CPU?
> > >
> > > Yes.
> > >
> > > >
> > > > > so next time it continues its execution tl->next value will wrap-up and
> will
> > > > > be 1 again, and tl->current==0 (lock held).
> > > > > I suppose this function will set tl->next=2 and will return a success?
> > > >
> > > > If this thread was swapped out and another thread took/attempted to
> take
> > > the lock, yes, tl->next == 2 here,
> > > > But as next == 1 unchanged, so it would not return a success.
> > >
> > > I am not talking about situation when tl->next == 2,tl->current==1 (just
> one
> > > lock() was executed by different thread).
> > > I am talking about situation when this thread was out of cpu for significant
> > > amount of cycles,
> > > and in that period tl->next and tl->current were wrapped around (they
> both
> > > reached UINT16_MAX, then 0).
> > > i.e. UINT16_MAX lock/unlock were executed while this thread was away
> from
> > > cpu.
> > > After that another thread just did successful lock(), so tl->next==1 and tl-
> > > >current==0.
> > > Now this thread wakeups and continues with:
> > > __atomic_compare_exchange_n(&tl->next, &next, next+1, ...)
> > > As both tl->next==1 and next==1, it will succeed.
> > > So we have 2 threads assuming they grabbed the lock successfully.
> > > Konstantin
> > >
> > Now I understood your points, but not sure if it is a rare or even impossible
> case for this thread stalls for CPU and during this time, the other
> > threads have taken the lock for 2^16 times, to wrap up.
> 
> I am agree it should be very rare, but I am not sure it is impossible.
> Let say thread is doing lock/unlock in a loop, with one iteration ~100 cycles.
> Then it would wrap around in ~6.5M cycles (~3ms on modern cpus).
> 

Thanks, agree, your quantitative way of analysis  helped me understand the issue.

> >
> > Anyway I made a patch, currently in internal review to fix this issue, the
> basic idea is to compare not only the next, but also the current, and
> > update the next(+1 and take the lock) only if both of them were not
> changed(or wrapped up and the lock released).
> > I will submit the patch after internal review approved. Please let me know if
> you have more comments.
> 
> Ok, thanks
> Konstantin

The fix was submitted into v7, 1/3, could you help review? 

Thanks!
Gavin


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v7 1/3] eal/ticketlock: ticket based to improve fairness
  2019-03-21  9:15   ` [dpdk-dev] [PATCH v7 1/3] eal/ticketlock: ticket based to improve fairness Joyce Kong
  2019-03-21  9:15     ` Joyce Kong
@ 2019-03-22 10:56     ` Ananyev, Konstantin
  2019-03-22 10:56       ` Ananyev, Konstantin
  1 sibling, 1 reply; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-22 10:56 UTC (permalink / raw)
  To: Joyce Kong, dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu

Hi Joyce,

> 
> The spinlock implementation is unfair, some threads may take locks
> aggressively while leaving the other threads starving for long time.
> 
> This patch introduces ticketlock which gives each waiting thread a
> ticket and they can take the lock one by one. First come, first serviced.
> This avoids starvation for too long time and is more predictable.
> 
> Suggested-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
> Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
> ---
>  MAINTAINERS                                        |   4 +
>  doc/api/doxy-api-index.md                          |   1 +
>  lib/librte_eal/common/Makefile                     |   2 +-
>  .../common/include/generic/rte_ticketlock.h        | 215 +++++++++++++++++++++
>  lib/librte_eal/common/meson.build                  |   1 +
>  5 files changed, 222 insertions(+), 1 deletion(-)
>  create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 452b8eb..3521271 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -210,6 +210,10 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
>  F: lib/librte_eal/common/include/rte_bitmap.h
>  F: app/test/test_bitmap.c
> 
> +Ticketlock
> +M: Joyce Kong <joyce.kong@arm.com>
> +F: lib/librte_eal/common/include/generic/rte_ticketlock.h
> +
>  ARM v7
>  M: Jan Viktorin <viktorin@rehivetech.com>
>  M: Gavin Hu <gavin.hu@arm.com>
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index d95ad56..aacc66b 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -65,6 +65,7 @@ The public API headers are grouped by topics:
>    [atomic]             (@ref rte_atomic.h),
>    [rwlock]             (@ref rte_rwlock.h),
>    [spinlock]           (@ref rte_spinlock.h)
> +  [ticketlock]         (@ref rte_ticketlock.h)
> 
>  - **CPU arch**:
>    [branch prediction]  (@ref rte_branch_prediction.h),
> diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
> index c487201..ac3305c 100644
> --- a/lib/librte_eal/common/Makefile
> +++ b/lib/librte_eal/common/Makefile
> @@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
>  INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
> 
>  GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
> -GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
> +GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
>  GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
> 
>  # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
> diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> new file mode 100644
> index 0000000..c5203cb
> --- /dev/null
> +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> @@ -0,0 +1,215 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2019 Arm Limited
> + */
> +
> +#ifndef _RTE_TICKETLOCK_H_
> +#define _RTE_TICKETLOCK_H_
> +
> +/**
> + * @file
> + *
> + * RTE ticket locks
> + *
> + * This file defines an API for ticket locks, which give each waiting
> + * thread a ticket and take the lock one by one, first come, first
> + * serviced.
> + *
> + * All locks must be initialised before use, and only initialised once.
> + *
> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#include <rte_common.h>
> +#include <rte_lcore.h>
> +#include <rte_pause.h>
> +
> +/**
> + * The rte_ticketlock_t type.
> + */
> +typedef union {
> +	uint32_t tickets;
> +	struct {
> +		uint16_t current;
> +		uint16_t next;
> +	} s;
> +} rte_ticketlock_t;
> +
> +/**
> + * A static ticketlock initializer.
> + */
> +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> +
> +/**
> + * Initialize the ticketlock to an unlocked state.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_init(rte_ticketlock_t *tl)
> +{
> +	__atomic_store_n(&tl->tickets, 0, __ATOMIC_RELAXED);
> +}
> +
> +/**
> + * Take the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_lock(rte_ticketlock_t *tl)
> +{
> +	uint16_t me = __atomic_fetch_add(&tl->s.next, 1, __ATOMIC_RELAXED);
> +	while (__atomic_load_n(&tl->s.current, __ATOMIC_ACQUIRE) != me)
> +		rte_pause();
> +}
> +
> +/**
> + * Release the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> +{
> +	uint16_t i = __atomic_load_n(&tl->s.current, __ATOMIC_RELAXED);
> +	__atomic_store_n(&tl->s.current, i + 1, __ATOMIC_RELEASE);
> +}
> +
> +/**
> + * Try to take the lock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + * @return
> + *   1 if the lock is successfully taken; 0 otherwise.
> + */
> +static inline __rte_experimental int
> +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> +{
> +	rte_ticketlock_t old, new;
> +	old.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);
> +	new.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);

As a nit:  from my point, there is no need for second load, we can just do:
new.tickets = old.tickets;

Apart from that:
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>


> +	new.s.next++;
> +	if (old.s.next == old.s.current) {
> +		if (__atomic_compare_exchange_n(&tl->tickets, &old.tickets,
> +		    new.tickets, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> +			return 1;
> +	}
> +
> +	return 0;
> +}
> +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v7 1/3] eal/ticketlock: ticket based to improve fairness
  2019-03-22 10:56     ` Ananyev, Konstantin
@ 2019-03-22 10:56       ` Ananyev, Konstantin
  0 siblings, 0 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-22 10:56 UTC (permalink / raw)
  To: Joyce Kong, dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu

Hi Joyce,

> 
> The spinlock implementation is unfair, some threads may take locks
> aggressively while leaving the other threads starving for long time.
> 
> This patch introduces ticketlock which gives each waiting thread a
> ticket and they can take the lock one by one. First come, first serviced.
> This avoids starvation for too long time and is more predictable.
> 
> Suggested-by: Jerin Jacob <jerinj@marvell.com>
> Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
> Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
> ---
>  MAINTAINERS                                        |   4 +
>  doc/api/doxy-api-index.md                          |   1 +
>  lib/librte_eal/common/Makefile                     |   2 +-
>  .../common/include/generic/rte_ticketlock.h        | 215 +++++++++++++++++++++
>  lib/librte_eal/common/meson.build                  |   1 +
>  5 files changed, 222 insertions(+), 1 deletion(-)
>  create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 452b8eb..3521271 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -210,6 +210,10 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
>  F: lib/librte_eal/common/include/rte_bitmap.h
>  F: app/test/test_bitmap.c
> 
> +Ticketlock
> +M: Joyce Kong <joyce.kong@arm.com>
> +F: lib/librte_eal/common/include/generic/rte_ticketlock.h
> +
>  ARM v7
>  M: Jan Viktorin <viktorin@rehivetech.com>
>  M: Gavin Hu <gavin.hu@arm.com>
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index d95ad56..aacc66b 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -65,6 +65,7 @@ The public API headers are grouped by topics:
>    [atomic]             (@ref rte_atomic.h),
>    [rwlock]             (@ref rte_rwlock.h),
>    [spinlock]           (@ref rte_spinlock.h)
> +  [ticketlock]         (@ref rte_ticketlock.h)
> 
>  - **CPU arch**:
>    [branch prediction]  (@ref rte_branch_prediction.h),
> diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
> index c487201..ac3305c 100644
> --- a/lib/librte_eal/common/Makefile
> +++ b/lib/librte_eal/common/Makefile
> @@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
>  INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
> 
>  GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
> -GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
> +GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
>  GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
> 
>  # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
> diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> new file mode 100644
> index 0000000..c5203cb
> --- /dev/null
> +++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
> @@ -0,0 +1,215 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2019 Arm Limited
> + */
> +
> +#ifndef _RTE_TICKETLOCK_H_
> +#define _RTE_TICKETLOCK_H_
> +
> +/**
> + * @file
> + *
> + * RTE ticket locks
> + *
> + * This file defines an API for ticket locks, which give each waiting
> + * thread a ticket and take the lock one by one, first come, first
> + * serviced.
> + *
> + * All locks must be initialised before use, and only initialised once.
> + *
> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#include <rte_common.h>
> +#include <rte_lcore.h>
> +#include <rte_pause.h>
> +
> +/**
> + * The rte_ticketlock_t type.
> + */
> +typedef union {
> +	uint32_t tickets;
> +	struct {
> +		uint16_t current;
> +		uint16_t next;
> +	} s;
> +} rte_ticketlock_t;
> +
> +/**
> + * A static ticketlock initializer.
> + */
> +#define RTE_TICKETLOCK_INITIALIZER { 0 }
> +
> +/**
> + * Initialize the ticketlock to an unlocked state.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_init(rte_ticketlock_t *tl)
> +{
> +	__atomic_store_n(&tl->tickets, 0, __ATOMIC_RELAXED);
> +}
> +
> +/**
> + * Take the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_lock(rte_ticketlock_t *tl)
> +{
> +	uint16_t me = __atomic_fetch_add(&tl->s.next, 1, __ATOMIC_RELAXED);
> +	while (__atomic_load_n(&tl->s.current, __ATOMIC_ACQUIRE) != me)
> +		rte_pause();
> +}
> +
> +/**
> + * Release the ticketlock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + */
> +static inline __rte_experimental void
> +rte_ticketlock_unlock(rte_ticketlock_t *tl)
> +{
> +	uint16_t i = __atomic_load_n(&tl->s.current, __ATOMIC_RELAXED);
> +	__atomic_store_n(&tl->s.current, i + 1, __ATOMIC_RELEASE);
> +}
> +
> +/**
> + * Try to take the lock.
> + *
> + * @param tl
> + *   A pointer to the ticketlock.
> + * @return
> + *   1 if the lock is successfully taken; 0 otherwise.
> + */
> +static inline __rte_experimental int
> +rte_ticketlock_trylock(rte_ticketlock_t *tl)
> +{
> +	rte_ticketlock_t old, new;
> +	old.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);
> +	new.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);

As a nit:  from my point, there is no need for second load, we can just do:
new.tickets = old.tickets;

Apart from that:
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>


> +	new.s.next++;
> +	if (old.s.next == old.s.current) {
> +		if (__atomic_compare_exchange_n(&tl->tickets, &old.tickets,
> +		    new.tickets, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
> +			return 1;
> +	}
> +
> +	return 0;
> +}
> +

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case
  2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case Joyce Kong
  2019-03-21  9:13     ` Joyce Kong
@ 2019-03-22 11:38     ` Ananyev, Konstantin
  2019-03-22 11:38       ` Ananyev, Konstantin
  2019-03-25 10:25       ` Joyce Kong (Arm Technology China)
  1 sibling, 2 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-22 11:38 UTC (permalink / raw)
  To: Joyce Kong, dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu



> 
> Add test cases for ticket lock, recursive ticket lock,
> and ticket lock performance.
> 
> Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> Reviewed-by: Ruifeng Wang <ruifeng.wang@arm.com>
> ---
>  MAINTAINERS                |   1 +
>  app/test/Makefile          |   1 +
>  app/test/autotest_data.py  |   6 +
>  app/test/meson.build       |   1 +
>  app/test/test_ticketlock.c | 311 +++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 320 insertions(+)
>  create mode 100644 app/test/test_ticketlock.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3521271..b1ed4cc 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -213,6 +213,7 @@ F: app/test/test_bitmap.c
>  Ticketlock
>  M: Joyce Kong <joyce.kong@arm.com>
>  F: lib/librte_eal/common/include/generic/rte_ticketlock.h
> +F: app/test/test_ticketlock.c
> 
>  ARM v7
>  M: Jan Viktorin <viktorin@rehivetech.com>
> diff --git a/app/test/Makefile b/app/test/Makefile
> index 89949c2..d6aa28b 100644
> --- a/app/test/Makefile
> +++ b/app/test/Makefile
> @@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
>  SRCS-y += test_malloc.c
>  SRCS-y += test_cycles.c
>  SRCS-y += test_spinlock.c
> +SRCS-y += test_ticketlock.c
>  SRCS-y += test_memory.c
>  SRCS-y += test_memzone.c
>  SRCS-y += test_bitmap.c
> diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
> index 5f87bb9..db25274 100644
> --- a/app/test/autotest_data.py
> +++ b/app/test/autotest_data.py
> @@ -171,6 +171,12 @@
>          "Report":  None,
>      },
>      {
> +        "Name":    "Ticketlock autotest",
> +        "Command": "ticketlock_autotest",
> +        "Func":    ticketlock_autotest,
> +        "Report":  None,
> +    }
> +    {
>          "Name":    "Byte order autotest",
>          "Command": "byteorder_autotest",
>          "Func":    default_autotest,
> diff --git a/app/test/meson.build b/app/test/meson.build
> index 05e5dde..ddb4d09 100644
> --- a/app/test/meson.build
> +++ b/app/test/meson.build
> @@ -107,6 +107,7 @@ test_sources = files('commands.c',
>  	'test_timer.c',
>  	'test_timer_perf.c',
>  	'test_timer_racecond.c',
> +	'test_ticketlock.c',
>  	'test_version.c',
>  	'virtual_pmd.c'
>  )
> diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
> new file mode 100644
> index 0000000..67281ce
> --- /dev/null
> +++ b/app/test/test_ticketlock.c
> @@ -0,0 +1,311 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2018-2019 Arm Limited
> + */
> +
> +#include <inttypes.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/queue.h>
> +#include <unistd.h>
> +
> +#include <rte_atomic.h>
> +#include <rte_common.h>
> +#include <rte_cycles.h>
> +#include <rte_eal.h>
> +#include <rte_launch.h>
> +#include <rte_lcore.h>
> +#include <rte_memory.h>
> +#include <rte_per_lcore.h>
> +#include <rte_ticketlock.h>
> +
> +#include "test.h"
> +
> +/*
> + * Ticketlock test
> + * =============
> + *
> + * - There is a global ticketlock and a table of ticketlocks (one per lcore).
> + *
> + * - The test function takes all of these locks and launches the
> + *   ``test_ticketlock_per_core()`` function on each core (except the master).
> + *
> + *   - The function takes the global lock, display something, then releases
> + *     the global lock.
> + *   - The function takes the per-lcore lock, display something, then releases
> + *     the per-core lock.
> + *
> + * - The main function unlocks the per-lcore locks sequentially and
> + *   waits between each lock. This triggers the display of a message
> + *   for each core, in the correct order. The autotest script checks that
> + *   this order is correct.
> + *
> + * - A load test is carried out, with all cores attempting to lock a single lock
> + *   multiple times
> + */
> +
> +static rte_ticketlock_t tl, tl_try;
> +static rte_ticketlock_t tl_tab[RTE_MAX_LCORE];
> +static rte_ticketlock_recursive_t tlr;
> +static unsigned int count;
> +
> +static rte_atomic32_t synchro;
> +
> +static int
> +test_ticketlock_per_core(__attribute__((unused)) void *arg)
> +{
> +	rte_ticketlock_lock(&tl);
> +	printf("Global lock taken on core %u\n", rte_lcore_id());
> +	rte_ticketlock_unlock(&tl);
> +
> +	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
> +	printf("Hello from core %u !\n", rte_lcore_id());
> +	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
> +
> +	return 0;
> +}

I think that's probably no enough for functional testing.
Need something extra to ensure that it provides correct locking in MT env.
Probably extend the perf test below to do both?
Something like that:

static uint64_t lcount __rte_cache_aligned;
static uint64_t lcore_count[RTE_MAX_LCORE] __rte_cache_aligned;

...

load_loop_fn(...)
{
   ...
   rte_ticketlock_lock(&lk);
   lcount++;
   rte_ticketlock_unlock(&lk);
   lcore_count[current_lcore]++;
}

Then in test_ticketlock_perf() make sure that sum of al
lcore_count[] values equals to lcount value:
tcount = 0;
for (i = 0; i != RTE_DIM(lcore_count); i++)
   tcount += lcore_count[i];

if (tcount != lcount)
  <error>

Same thought for trylock.
Konstantin

> +
> +static int
> +test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
> +{
> +	unsigned int id = rte_lcore_id();
> +
> +	rte_ticketlock_recursive_lock(&tlr);
> +	printf("Global recursive lock taken on core %u - count = %d\n",
> +	       id, tlr.count);
> +	rte_ticketlock_recursive_lock(&tlr);
> +	printf("Global recursive lock taken on core %u - count = %d\n",
> +	       id, tlr.count);
> +	rte_ticketlock_recursive_lock(&tlr);
> +	printf("Global recursive lock taken on core %u - count = %d\n",
> +	       id, tlr.count);
> +
> +	printf("Hello from within recursive locks from core %u !\n", id);
> +
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	printf("Global recursive lock released on core %u - count = %d\n",
> +	       id, tlr.count);
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	printf("Global recursive lock released on core %u - count = %d\n",
> +	       id, tlr.count);
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	printf("Global recursive lock released on core %u - count = %d\n",
> +	       id, tlr.count);
> +
> +	return 0;
> +}
> +
> +static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
> +static uint64_t lock_count[RTE_MAX_LCORE] = {0};
> +
> +#define TIME_MS 100
> +
> +static int
> +load_loop_fn(void *func_param)
> +{
> +	uint64_t time_diff = 0, begin;
> +	uint64_t hz = rte_get_timer_hz();
> +	uint64_t lcount = 0;
> +	const int use_lock = *(int *)func_param;
> +	const unsigned int lcore = rte_lcore_id();
> +
> +	/* wait synchro for slaves */
> +	if (lcore != rte_get_master_lcore())
> +		while (rte_atomic32_read(&synchro) == 0)
> +			;
> +
> +	begin = rte_get_timer_cycles();
> +	while (time_diff < hz * TIME_MS / 1000) {
> +		if (use_lock)
> +			rte_ticketlock_lock(&lk);
> +		lcount++;
> +		if (use_lock)
> +			rte_ticketlock_unlock(&lk);
> +		/* delay to make lock duty cycle slighlty realistic */

Probably better to do here the same as in test spinlock patches:
 - remove delay_us()
- move
  time_diff = rte_get_timer_cycles() - begin;
 out of the loop and report aggregate cycles.

> +		rte_delay_us(1);
> +		time_diff = rte_get_timer_cycles() - begin;
> +	}
> +	lock_count[lcore] = lcount;
> +	return 0;
> +}
> +
> +static int
> +test_ticketlock_perf(void)
> +{
> +	unsigned int i;
> +	uint64_t total = 0;
> +	int lock = 0;
> +	const unsigned int lcore = rte_lcore_id();
> +
> +	printf("\nTest with no lock on single core...\n");
> +	load_loop_fn(&lock);
> +	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
> +	memset(lock_count, 0, sizeof(lock_count));
> +
> +	printf("\nTest with lock on single core...\n");
> +	lock = 1;
> +	load_loop_fn(&lock);
> +	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
> +	memset(lock_count, 0, sizeof(lock_count));
> +
> +	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
> +
> +	/* Clear synchro and start slaves */
> +	rte_atomic32_set(&synchro, 0);
> +	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
> +
> +	/* start synchro and launch test on master */
> +	rte_atomic32_set(&synchro, 1);
> +	load_loop_fn(&lock);
> +
> +	rte_eal_mp_wait_lcore();
> +
> +	RTE_LCORE_FOREACH(i) {
> +		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
> +		total += lock_count[i];
> +	}
> +
> +	printf("Total count = %"PRIu64"\n", total);
> +
> +	return 0;
> +}
> +
> +/*
> + * Use rte_ticketlock_trylock() to trylock a ticketlock object,
> + * If it could not lock the object successfully, it would
> + * return immediately and the variable of "count" would be
> + * increased by one per times. the value of "count" could be
> + * checked as the result later.
> + */
> +static int
> +test_ticketlock_try(__attribute__((unused)) void *arg)
> +{
> +	if (rte_ticketlock_trylock(&tl_try) == 0) {
> +		rte_ticketlock_lock(&tl);
> +		count++;
> +		rte_ticketlock_unlock(&tl);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +/*
> + * Test rte_eal_get_lcore_state() in addition to ticketlocks
> + * as we have "waiting" then "running" lcores.
> + */
> +static int
> +test_ticketlock(void)
> +{
> +	int ret = 0;
> +	int i;
> +
> +	/* slave cores should be waiting: print it */
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		printf("lcore %d state: %d\n", i,
> +		       (int) rte_eal_get_lcore_state(i));
> +	}
> +
> +	rte_ticketlock_init(&tl);
> +	rte_ticketlock_init(&tl_try);
> +	rte_ticketlock_recursive_init(&tlr);
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_ticketlock_init(&tl_tab[i]);
> +	}
> +
> +	rte_ticketlock_lock(&tl);
> +
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_ticketlock_lock(&tl_tab[i]);
> +		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
> +	}
> +
> +	/* slave cores should be busy: print it */
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		printf("lcore %d state: %d\n", i,
> +		       (int) rte_eal_get_lcore_state(i));
> +	}
> +	rte_ticketlock_unlock(&tl);
> +
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_ticketlock_unlock(&tl_tab[i]);
> +		rte_delay_ms(10);
> +	}
> +
> +	rte_eal_mp_wait_lcore();
> +
> +	rte_ticketlock_recursive_lock(&tlr);
> +
> +	/*
> +	 * Try to acquire a lock that we already own
> +	 */
> +	if (!rte_ticketlock_recursive_trylock(&tlr)) {
> +		printf("rte_ticketlock_recursive_trylock failed on a lock that "
> +		       "we already own\n");
> +		ret = -1;
> +	} else
> +		rte_ticketlock_recursive_unlock(&tlr);
> +
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
> +					NULL, i);
> +	}
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	rte_eal_mp_wait_lcore();
> +
> +	/*
> +	 * Test if it could return immediately from try-locking a locked object.
> +	 * Here it will lock the ticketlock object first, then launch all the
> +	 * slave lcores to trylock the same ticketlock object.
> +	 * All the slave lcores should give up try-locking a locked object and
> +	 * return immediately, and then increase the "count" initialized with
> +	 * zero by one per times.
> +	 * We can check if the "count" is finally equal to the number of all
> +	 * slave lcores to see if the behavior of try-locking a locked
> +	 * ticketlock object is correct.
> +	 */
> +	if (rte_ticketlock_trylock(&tl_try) == 0)
> +		return -1;
> +
> +	count = 0;
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
> +	}
> +	rte_eal_mp_wait_lcore();
> +	rte_ticketlock_unlock(&tl_try);
> +	if (rte_ticketlock_is_locked(&tl)) {
> +		printf("ticketlock is locked but it should not be\n");
> +		return -1;
> +	}
> +	rte_ticketlock_lock(&tl);
> +	if (count != (rte_lcore_count() - 1))
> +		ret = -1;
> +
> +	rte_ticketlock_unlock(&tl);
> +
> +	/*
> +	 * Test if it can trylock recursively.
> +	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
> +	 * a ticketlock object recursively. Here it will try to lock a
> +	 * ticketlock object twice.
> +	 */
> +	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
> +		printf("It failed to do the first ticketlock_recursive_trylock "
> +			   "but it should able to do\n");
> +		return -1;
> +	}
> +	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
> +		printf("It failed to do the second ticketlock_recursive_trylock "
> +			   "but it should able to do\n");
> +		return -1;
> +	}
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	rte_ticketlock_recursive_unlock(&tlr);
> +
> +	if (test_ticketlock_perf() < 0)
> +		return -1;
> +
> +	return ret;
> +}
> +
> +REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
> --
> 2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case
  2019-03-22 11:38     ` Ananyev, Konstantin
@ 2019-03-22 11:38       ` Ananyev, Konstantin
  2019-03-25 10:25       ` Joyce Kong (Arm Technology China)
  1 sibling, 0 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-22 11:38 UTC (permalink / raw)
  To: Joyce Kong, dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu



> 
> Add test cases for ticket lock, recursive ticket lock,
> and ticket lock performance.
> 
> Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> Reviewed-by: Ruifeng Wang <ruifeng.wang@arm.com>
> ---
>  MAINTAINERS                |   1 +
>  app/test/Makefile          |   1 +
>  app/test/autotest_data.py  |   6 +
>  app/test/meson.build       |   1 +
>  app/test/test_ticketlock.c | 311 +++++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 320 insertions(+)
>  create mode 100644 app/test/test_ticketlock.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3521271..b1ed4cc 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -213,6 +213,7 @@ F: app/test/test_bitmap.c
>  Ticketlock
>  M: Joyce Kong <joyce.kong@arm.com>
>  F: lib/librte_eal/common/include/generic/rte_ticketlock.h
> +F: app/test/test_ticketlock.c
> 
>  ARM v7
>  M: Jan Viktorin <viktorin@rehivetech.com>
> diff --git a/app/test/Makefile b/app/test/Makefile
> index 89949c2..d6aa28b 100644
> --- a/app/test/Makefile
> +++ b/app/test/Makefile
> @@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
>  SRCS-y += test_malloc.c
>  SRCS-y += test_cycles.c
>  SRCS-y += test_spinlock.c
> +SRCS-y += test_ticketlock.c
>  SRCS-y += test_memory.c
>  SRCS-y += test_memzone.c
>  SRCS-y += test_bitmap.c
> diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
> index 5f87bb9..db25274 100644
> --- a/app/test/autotest_data.py
> +++ b/app/test/autotest_data.py
> @@ -171,6 +171,12 @@
>          "Report":  None,
>      },
>      {
> +        "Name":    "Ticketlock autotest",
> +        "Command": "ticketlock_autotest",
> +        "Func":    ticketlock_autotest,
> +        "Report":  None,
> +    }
> +    {
>          "Name":    "Byte order autotest",
>          "Command": "byteorder_autotest",
>          "Func":    default_autotest,
> diff --git a/app/test/meson.build b/app/test/meson.build
> index 05e5dde..ddb4d09 100644
> --- a/app/test/meson.build
> +++ b/app/test/meson.build
> @@ -107,6 +107,7 @@ test_sources = files('commands.c',
>  	'test_timer.c',
>  	'test_timer_perf.c',
>  	'test_timer_racecond.c',
> +	'test_ticketlock.c',
>  	'test_version.c',
>  	'virtual_pmd.c'
>  )
> diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
> new file mode 100644
> index 0000000..67281ce
> --- /dev/null
> +++ b/app/test/test_ticketlock.c
> @@ -0,0 +1,311 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2018-2019 Arm Limited
> + */
> +
> +#include <inttypes.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/queue.h>
> +#include <unistd.h>
> +
> +#include <rte_atomic.h>
> +#include <rte_common.h>
> +#include <rte_cycles.h>
> +#include <rte_eal.h>
> +#include <rte_launch.h>
> +#include <rte_lcore.h>
> +#include <rte_memory.h>
> +#include <rte_per_lcore.h>
> +#include <rte_ticketlock.h>
> +
> +#include "test.h"
> +
> +/*
> + * Ticketlock test
> + * =============
> + *
> + * - There is a global ticketlock and a table of ticketlocks (one per lcore).
> + *
> + * - The test function takes all of these locks and launches the
> + *   ``test_ticketlock_per_core()`` function on each core (except the master).
> + *
> + *   - The function takes the global lock, display something, then releases
> + *     the global lock.
> + *   - The function takes the per-lcore lock, display something, then releases
> + *     the per-core lock.
> + *
> + * - The main function unlocks the per-lcore locks sequentially and
> + *   waits between each lock. This triggers the display of a message
> + *   for each core, in the correct order. The autotest script checks that
> + *   this order is correct.
> + *
> + * - A load test is carried out, with all cores attempting to lock a single lock
> + *   multiple times
> + */
> +
> +static rte_ticketlock_t tl, tl_try;
> +static rte_ticketlock_t tl_tab[RTE_MAX_LCORE];
> +static rte_ticketlock_recursive_t tlr;
> +static unsigned int count;
> +
> +static rte_atomic32_t synchro;
> +
> +static int
> +test_ticketlock_per_core(__attribute__((unused)) void *arg)
> +{
> +	rte_ticketlock_lock(&tl);
> +	printf("Global lock taken on core %u\n", rte_lcore_id());
> +	rte_ticketlock_unlock(&tl);
> +
> +	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
> +	printf("Hello from core %u !\n", rte_lcore_id());
> +	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
> +
> +	return 0;
> +}

I think that's probably no enough for functional testing.
Need something extra to ensure that it provides correct locking in MT env.
Probably extend the perf test below to do both?
Something like that:

static uint64_t lcount __rte_cache_aligned;
static uint64_t lcore_count[RTE_MAX_LCORE] __rte_cache_aligned;

...

load_loop_fn(...)
{
   ...
   rte_ticketlock_lock(&lk);
   lcount++;
   rte_ticketlock_unlock(&lk);
   lcore_count[current_lcore]++;
}

Then in test_ticketlock_perf() make sure that sum of al
lcore_count[] values equals to lcount value:
tcount = 0;
for (i = 0; i != RTE_DIM(lcore_count); i++)
   tcount += lcore_count[i];

if (tcount != lcount)
  <error>

Same thought for trylock.
Konstantin

> +
> +static int
> +test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
> +{
> +	unsigned int id = rte_lcore_id();
> +
> +	rte_ticketlock_recursive_lock(&tlr);
> +	printf("Global recursive lock taken on core %u - count = %d\n",
> +	       id, tlr.count);
> +	rte_ticketlock_recursive_lock(&tlr);
> +	printf("Global recursive lock taken on core %u - count = %d\n",
> +	       id, tlr.count);
> +	rte_ticketlock_recursive_lock(&tlr);
> +	printf("Global recursive lock taken on core %u - count = %d\n",
> +	       id, tlr.count);
> +
> +	printf("Hello from within recursive locks from core %u !\n", id);
> +
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	printf("Global recursive lock released on core %u - count = %d\n",
> +	       id, tlr.count);
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	printf("Global recursive lock released on core %u - count = %d\n",
> +	       id, tlr.count);
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	printf("Global recursive lock released on core %u - count = %d\n",
> +	       id, tlr.count);
> +
> +	return 0;
> +}
> +
> +static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
> +static uint64_t lock_count[RTE_MAX_LCORE] = {0};
> +
> +#define TIME_MS 100
> +
> +static int
> +load_loop_fn(void *func_param)
> +{
> +	uint64_t time_diff = 0, begin;
> +	uint64_t hz = rte_get_timer_hz();
> +	uint64_t lcount = 0;
> +	const int use_lock = *(int *)func_param;
> +	const unsigned int lcore = rte_lcore_id();
> +
> +	/* wait synchro for slaves */
> +	if (lcore != rte_get_master_lcore())
> +		while (rte_atomic32_read(&synchro) == 0)
> +			;
> +
> +	begin = rte_get_timer_cycles();
> +	while (time_diff < hz * TIME_MS / 1000) {
> +		if (use_lock)
> +			rte_ticketlock_lock(&lk);
> +		lcount++;
> +		if (use_lock)
> +			rte_ticketlock_unlock(&lk);
> +		/* delay to make lock duty cycle slighlty realistic */

Probably better to do here the same as in test spinlock patches:
 - remove delay_us()
- move
  time_diff = rte_get_timer_cycles() - begin;
 out of the loop and report aggregate cycles.

> +		rte_delay_us(1);
> +		time_diff = rte_get_timer_cycles() - begin;
> +	}
> +	lock_count[lcore] = lcount;
> +	return 0;
> +}
> +
> +static int
> +test_ticketlock_perf(void)
> +{
> +	unsigned int i;
> +	uint64_t total = 0;
> +	int lock = 0;
> +	const unsigned int lcore = rte_lcore_id();
> +
> +	printf("\nTest with no lock on single core...\n");
> +	load_loop_fn(&lock);
> +	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
> +	memset(lock_count, 0, sizeof(lock_count));
> +
> +	printf("\nTest with lock on single core...\n");
> +	lock = 1;
> +	load_loop_fn(&lock);
> +	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
> +	memset(lock_count, 0, sizeof(lock_count));
> +
> +	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
> +
> +	/* Clear synchro and start slaves */
> +	rte_atomic32_set(&synchro, 0);
> +	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
> +
> +	/* start synchro and launch test on master */
> +	rte_atomic32_set(&synchro, 1);
> +	load_loop_fn(&lock);
> +
> +	rte_eal_mp_wait_lcore();
> +
> +	RTE_LCORE_FOREACH(i) {
> +		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
> +		total += lock_count[i];
> +	}
> +
> +	printf("Total count = %"PRIu64"\n", total);
> +
> +	return 0;
> +}
> +
> +/*
> + * Use rte_ticketlock_trylock() to trylock a ticketlock object,
> + * If it could not lock the object successfully, it would
> + * return immediately and the variable of "count" would be
> + * increased by one per times. the value of "count" could be
> + * checked as the result later.
> + */
> +static int
> +test_ticketlock_try(__attribute__((unused)) void *arg)
> +{
> +	if (rte_ticketlock_trylock(&tl_try) == 0) {
> +		rte_ticketlock_lock(&tl);
> +		count++;
> +		rte_ticketlock_unlock(&tl);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +/*
> + * Test rte_eal_get_lcore_state() in addition to ticketlocks
> + * as we have "waiting" then "running" lcores.
> + */
> +static int
> +test_ticketlock(void)
> +{
> +	int ret = 0;
> +	int i;
> +
> +	/* slave cores should be waiting: print it */
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		printf("lcore %d state: %d\n", i,
> +		       (int) rte_eal_get_lcore_state(i));
> +	}
> +
> +	rte_ticketlock_init(&tl);
> +	rte_ticketlock_init(&tl_try);
> +	rte_ticketlock_recursive_init(&tlr);
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_ticketlock_init(&tl_tab[i]);
> +	}
> +
> +	rte_ticketlock_lock(&tl);
> +
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_ticketlock_lock(&tl_tab[i]);
> +		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
> +	}
> +
> +	/* slave cores should be busy: print it */
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		printf("lcore %d state: %d\n", i,
> +		       (int) rte_eal_get_lcore_state(i));
> +	}
> +	rte_ticketlock_unlock(&tl);
> +
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_ticketlock_unlock(&tl_tab[i]);
> +		rte_delay_ms(10);
> +	}
> +
> +	rte_eal_mp_wait_lcore();
> +
> +	rte_ticketlock_recursive_lock(&tlr);
> +
> +	/*
> +	 * Try to acquire a lock that we already own
> +	 */
> +	if (!rte_ticketlock_recursive_trylock(&tlr)) {
> +		printf("rte_ticketlock_recursive_trylock failed on a lock that "
> +		       "we already own\n");
> +		ret = -1;
> +	} else
> +		rte_ticketlock_recursive_unlock(&tlr);
> +
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
> +					NULL, i);
> +	}
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	rte_eal_mp_wait_lcore();
> +
> +	/*
> +	 * Test if it could return immediately from try-locking a locked object.
> +	 * Here it will lock the ticketlock object first, then launch all the
> +	 * slave lcores to trylock the same ticketlock object.
> +	 * All the slave lcores should give up try-locking a locked object and
> +	 * return immediately, and then increase the "count" initialized with
> +	 * zero by one per times.
> +	 * We can check if the "count" is finally equal to the number of all
> +	 * slave lcores to see if the behavior of try-locking a locked
> +	 * ticketlock object is correct.
> +	 */
> +	if (rte_ticketlock_trylock(&tl_try) == 0)
> +		return -1;
> +
> +	count = 0;
> +	RTE_LCORE_FOREACH_SLAVE(i) {
> +		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
> +	}
> +	rte_eal_mp_wait_lcore();
> +	rte_ticketlock_unlock(&tl_try);
> +	if (rte_ticketlock_is_locked(&tl)) {
> +		printf("ticketlock is locked but it should not be\n");
> +		return -1;
> +	}
> +	rte_ticketlock_lock(&tl);
> +	if (count != (rte_lcore_count() - 1))
> +		ret = -1;
> +
> +	rte_ticketlock_unlock(&tl);
> +
> +	/*
> +	 * Test if it can trylock recursively.
> +	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
> +	 * a ticketlock object recursively. Here it will try to lock a
> +	 * ticketlock object twice.
> +	 */
> +	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
> +		printf("It failed to do the first ticketlock_recursive_trylock "
> +			   "but it should able to do\n");
> +		return -1;
> +	}
> +	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
> +		printf("It failed to do the second ticketlock_recursive_trylock "
> +			   "but it should able to do\n");
> +		return -1;
> +	}
> +	rte_ticketlock_recursive_unlock(&tlr);
> +	rte_ticketlock_recursive_unlock(&tlr);
> +
> +	if (test_ticketlock_perf() < 0)
> +		return -1;
> +
> +	return ret;
> +}
> +
> +REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
> --
> 2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case
  2019-03-22 11:38     ` Ananyev, Konstantin
  2019-03-22 11:38       ` Ananyev, Konstantin
@ 2019-03-25 10:25       ` Joyce Kong (Arm Technology China)
  2019-03-25 10:25         ` Joyce Kong (Arm Technology China)
  1 sibling, 1 reply; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-03-25 10:25 UTC (permalink / raw)
  To: Ananyev, Konstantin, dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Gavin Hu (Arm Technology China)

Hi Konstantin,

> -----Original Message-----
> From: Ananyev, Konstantin <konstantin.ananyev@intel.com>
> Sent: Friday, March 22, 2019 7:39 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: nd <nd@arm.com>; stephen@networkplumber.org;
> jerin.jacob@caviumnetworks.com; thomas@monjalon.net; Honnappa
> Nagarahalli <Honnappa.Nagarahalli@arm.com>; Gavin Hu (Arm Technology
> China) <Gavin.Hu@arm.com>
> Subject: RE: [PATCH v7 3/3] test/ticketlock: add ticket lock test case
> 
> 
> 
> >
> > Add test cases for ticket lock, recursive ticket lock, and ticket lock
> > performance.
> >
> > Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> > Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> > Reviewed-by: Ruifeng Wang <ruifeng.wang@arm.com>
> > ---
> >  MAINTAINERS                |   1 +
> >  app/test/Makefile          |   1 +
> >  app/test/autotest_data.py  |   6 +
> >  app/test/meson.build       |   1 +
> >  app/test/test_ticketlock.c | 311
> > +++++++++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 320 insertions(+)
> >  create mode 100644 app/test/test_ticketlock.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS index 3521271..b1ed4cc 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -213,6 +213,7 @@ F: app/test/test_bitmap.c  Ticketlock
> >  M: Joyce Kong <joyce.kong@arm.com>
> >  F: lib/librte_eal/common/include/generic/rte_ticketlock.h
> > +F: app/test/test_ticketlock.c
> >
> >  ARM v7
> >  M: Jan Viktorin <viktorin@rehivetech.com> diff --git
> > a/app/test/Makefile b/app/test/Makefile index 89949c2..d6aa28b 100644
> > --- a/app/test/Makefile
> > +++ b/app/test/Makefile
> > @@ -65,6 +65,7 @@ SRCS-y += test_barrier.c  SRCS-y += test_malloc.c
> > SRCS-y += test_cycles.c  SRCS-y += test_spinlock.c
> > +SRCS-y += test_ticketlock.c
> >  SRCS-y += test_memory.c
> >  SRCS-y += test_memzone.c
> >  SRCS-y += test_bitmap.c
> > diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
> > index 5f87bb9..db25274 100644
> > --- a/app/test/autotest_data.py
> > +++ b/app/test/autotest_data.py
> > @@ -171,6 +171,12 @@
> >          "Report":  None,
> >      },
> >      {
> > +        "Name":    "Ticketlock autotest",
> > +        "Command": "ticketlock_autotest",
> > +        "Func":    ticketlock_autotest,
> > +        "Report":  None,
> > +    }
> > +    {
> >          "Name":    "Byte order autotest",
> >          "Command": "byteorder_autotest",
> >          "Func":    default_autotest,
> > diff --git a/app/test/meson.build b/app/test/meson.build index
> > 05e5dde..ddb4d09 100644
> > --- a/app/test/meson.build
> > +++ b/app/test/meson.build
> > @@ -107,6 +107,7 @@ test_sources = files('commands.c',
> >  	'test_timer.c',
> >  	'test_timer_perf.c',
> >  	'test_timer_racecond.c',
> > +	'test_ticketlock.c',
> >  	'test_version.c',
> >  	'virtual_pmd.c'
> >  )
> > diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
> > new file mode 100644 index 0000000..67281ce
> > --- /dev/null
> > +++ b/app/test/test_ticketlock.c
> > @@ -0,0 +1,311 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2018-2019 Arm Limited  */
> > +
> > +#include <inttypes.h>
> > +#include <stdint.h>
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <sys/queue.h>
> > +#include <unistd.h>
> > +
> > +#include <rte_atomic.h>
> > +#include <rte_common.h>
> > +#include <rte_cycles.h>
> > +#include <rte_eal.h>
> > +#include <rte_launch.h>
> > +#include <rte_lcore.h>
> > +#include <rte_memory.h>
> > +#include <rte_per_lcore.h>
> > +#include <rte_ticketlock.h>
> > +
> > +#include "test.h"
> > +
> > +/*
> > + * Ticketlock test
> > + * =============
> > + *
> > + * - There is a global ticketlock and a table of ticketlocks (one per lcore).
> > + *
> > + * - The test function takes all of these locks and launches the
> > + *   ``test_ticketlock_per_core()`` function on each core (except the master).
> > + *
> > + *   - The function takes the global lock, display something, then releases
> > + *     the global lock.
> > + *   - The function takes the per-lcore lock, display something, then
> releases
> > + *     the per-core lock.
> > + *
> > + * - The main function unlocks the per-lcore locks sequentially and
> > + *   waits between each lock. This triggers the display of a message
> > + *   for each core, in the correct order. The autotest script checks that
> > + *   this order is correct.
> > + *
> > + * - A load test is carried out, with all cores attempting to lock a single lock
> > + *   multiple times
> > + */
> > +
> > +static rte_ticketlock_t tl, tl_try;
> > +static rte_ticketlock_t tl_tab[RTE_MAX_LCORE]; static
> > +rte_ticketlock_recursive_t tlr; static unsigned int count;
> > +
> > +static rte_atomic32_t synchro;
> > +
> > +static int
> > +test_ticketlock_per_core(__attribute__((unused)) void *arg) {
> > +	rte_ticketlock_lock(&tl);
> > +	printf("Global lock taken on core %u\n", rte_lcore_id());
> > +	rte_ticketlock_unlock(&tl);
> > +
> > +	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
> > +	printf("Hello from core %u !\n", rte_lcore_id());
> > +	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
> > +
> > +	return 0;
> > +}
> 
> I think that's probably no enough for functional testing.
> Need something extra to ensure that it provides correct locking in MT env.
> Probably extend the perf test below to do both?
> Something like that:
> 
> static uint64_t lcount __rte_cache_aligned; static uint64_t
> lcore_count[RTE_MAX_LCORE] __rte_cache_aligned;
> 
> ...
> 
> load_loop_fn(...)
> {
>    ...
>    rte_ticketlock_lock(&lk);
>    lcount++;
>    rte_ticketlock_unlock(&lk);
>    lcore_count[current_lcore]++;
> }
> 
> Then in test_ticketlock_perf() make sure that sum of al lcore_count[] values
> equals to lcount value:
> tcount = 0;
> for (i = 0; i != RTE_DIM(lcore_count); i++)
>    tcount += lcore_count[i];
> 
> if (tcount != lcount)
>   <error>
> 
> Same thought for trylock.
> Konstantin
> 

Got your opinion and will do this in next version.

> > +
> > +static int
> > +test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
> > +{
> > +	unsigned int id = rte_lcore_id();
> > +
> > +	rte_ticketlock_recursive_lock(&tlr);
> > +	printf("Global recursive lock taken on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +	rte_ticketlock_recursive_lock(&tlr);
> > +	printf("Global recursive lock taken on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +	rte_ticketlock_recursive_lock(&tlr);
> > +	printf("Global recursive lock taken on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +
> > +	printf("Hello from within recursive locks from core %u !\n", id);
> > +
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	printf("Global recursive lock released on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	printf("Global recursive lock released on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	printf("Global recursive lock released on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +
> > +	return 0;
> > +}
> > +
> > +static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER; static
> > +uint64_t lock_count[RTE_MAX_LCORE] = {0};
> > +
> > +#define TIME_MS 100
> > +
> > +static int
> > +load_loop_fn(void *func_param)
> > +{
> > +	uint64_t time_diff = 0, begin;
> > +	uint64_t hz = rte_get_timer_hz();
> > +	uint64_t lcount = 0;
> > +	const int use_lock = *(int *)func_param;
> > +	const unsigned int lcore = rte_lcore_id();
> > +
> > +	/* wait synchro for slaves */
> > +	if (lcore != rte_get_master_lcore())
> > +		while (rte_atomic32_read(&synchro) == 0)
> > +			;
> > +
> > +	begin = rte_get_timer_cycles();
> > +	while (time_diff < hz * TIME_MS / 1000) {
> > +		if (use_lock)
> > +			rte_ticketlock_lock(&lk);
> > +		lcount++;
> > +		if (use_lock)
> > +			rte_ticketlock_unlock(&lk);
> > +		/* delay to make lock duty cycle slighlty realistic */
> 
> Probably better to do here the same as in test spinlock patches:
>  - remove delay_us()
> - move
>   time_diff = rte_get_timer_cycles() - begin;  out of the loop and report
> aggregate cycles.
> 

Will do the same as test spinlock patches in next version.

> > +		rte_delay_us(1);
> > +		time_diff = rte_get_timer_cycles() - begin;
> > +	}
> > +	lock_count[lcore] = lcount;
> > +	return 0;
> > +}
> > +
> > +static int
> > +test_ticketlock_perf(void)
> > +{
> > +	unsigned int i;
> > +	uint64_t total = 0;
> > +	int lock = 0;
> > +	const unsigned int lcore = rte_lcore_id();
> > +
> > +	printf("\nTest with no lock on single core...\n");
> > +	load_loop_fn(&lock);
> > +	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
> > +	memset(lock_count, 0, sizeof(lock_count));
> > +
> > +	printf("\nTest with lock on single core...\n");
> > +	lock = 1;
> > +	load_loop_fn(&lock);
> > +	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
> > +	memset(lock_count, 0, sizeof(lock_count));
> > +
> > +	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
> > +
> > +	/* Clear synchro and start slaves */
> > +	rte_atomic32_set(&synchro, 0);
> > +	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
> > +
> > +	/* start synchro and launch test on master */
> > +	rte_atomic32_set(&synchro, 1);
> > +	load_loop_fn(&lock);
> > +
> > +	rte_eal_mp_wait_lcore();
> > +
> > +	RTE_LCORE_FOREACH(i) {
> > +		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
> > +		total += lock_count[i];
> > +	}
> > +
> > +	printf("Total count = %"PRIu64"\n", total);
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * Use rte_ticketlock_trylock() to trylock a ticketlock object,
> > + * If it could not lock the object successfully, it would
> > + * return immediately and the variable of "count" would be
> > + * increased by one per times. the value of "count" could be
> > + * checked as the result later.
> > + */
> > +static int
> > +test_ticketlock_try(__attribute__((unused)) void *arg) {
> > +	if (rte_ticketlock_trylock(&tl_try) == 0) {
> > +		rte_ticketlock_lock(&tl);
> > +		count++;
> > +		rte_ticketlock_unlock(&tl);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +/*
> > + * Test rte_eal_get_lcore_state() in addition to ticketlocks
> > + * as we have "waiting" then "running" lcores.
> > + */
> > +static int
> > +test_ticketlock(void)
> > +{
> > +	int ret = 0;
> > +	int i;
> > +
> > +	/* slave cores should be waiting: print it */
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		printf("lcore %d state: %d\n", i,
> > +		       (int) rte_eal_get_lcore_state(i));
> > +	}
> > +
> > +	rte_ticketlock_init(&tl);
> > +	rte_ticketlock_init(&tl_try);
> > +	rte_ticketlock_recursive_init(&tlr);
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_ticketlock_init(&tl_tab[i]);
> > +	}
> > +
> > +	rte_ticketlock_lock(&tl);
> > +
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_ticketlock_lock(&tl_tab[i]);
> > +		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
> > +	}
> > +
> > +	/* slave cores should be busy: print it */
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		printf("lcore %d state: %d\n", i,
> > +		       (int) rte_eal_get_lcore_state(i));
> > +	}
> > +	rte_ticketlock_unlock(&tl);
> > +
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_ticketlock_unlock(&tl_tab[i]);
> > +		rte_delay_ms(10);
> > +	}
> > +
> > +	rte_eal_mp_wait_lcore();
> > +
> > +	rte_ticketlock_recursive_lock(&tlr);
> > +
> > +	/*
> > +	 * Try to acquire a lock that we already own
> > +	 */
> > +	if (!rte_ticketlock_recursive_trylock(&tlr)) {
> > +		printf("rte_ticketlock_recursive_trylock failed on a lock that "
> > +		       "we already own\n");
> > +		ret = -1;
> > +	} else
> > +		rte_ticketlock_recursive_unlock(&tlr);
> > +
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
> > +					NULL, i);
> > +	}
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	rte_eal_mp_wait_lcore();
> > +
> > +	/*
> > +	 * Test if it could return immediately from try-locking a locked object.
> > +	 * Here it will lock the ticketlock object first, then launch all the
> > +	 * slave lcores to trylock the same ticketlock object.
> > +	 * All the slave lcores should give up try-locking a locked object and
> > +	 * return immediately, and then increase the "count" initialized with
> > +	 * zero by one per times.
> > +	 * We can check if the "count" is finally equal to the number of all
> > +	 * slave lcores to see if the behavior of try-locking a locked
> > +	 * ticketlock object is correct.
> > +	 */
> > +	if (rte_ticketlock_trylock(&tl_try) == 0)
> > +		return -1;
> > +
> > +	count = 0;
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
> > +	}
> > +	rte_eal_mp_wait_lcore();
> > +	rte_ticketlock_unlock(&tl_try);
> > +	if (rte_ticketlock_is_locked(&tl)) {
> > +		printf("ticketlock is locked but it should not be\n");
> > +		return -1;
> > +	}
> > +	rte_ticketlock_lock(&tl);
> > +	if (count != (rte_lcore_count() - 1))
> > +		ret = -1;
> > +
> > +	rte_ticketlock_unlock(&tl);
> > +
> > +	/*
> > +	 * Test if it can trylock recursively.
> > +	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
> > +	 * a ticketlock object recursively. Here it will try to lock a
> > +	 * ticketlock object twice.
> > +	 */
> > +	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
> > +		printf("It failed to do the first ticketlock_recursive_trylock "
> > +			   "but it should able to do\n");
> > +		return -1;
> > +	}
> > +	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
> > +		printf("It failed to do the second ticketlock_recursive_trylock
> "
> > +			   "but it should able to do\n");
> > +		return -1;
> > +	}
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +
> > +	if (test_ticketlock_perf() < 0)
> > +		return -1;
> > +
> > +	return ret;
> > +}
> > +
> > +REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
> > --
> > 2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case
  2019-03-25 10:25       ` Joyce Kong (Arm Technology China)
@ 2019-03-25 10:25         ` Joyce Kong (Arm Technology China)
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-03-25 10:25 UTC (permalink / raw)
  To: Ananyev, Konstantin, dev
  Cc: nd, stephen, jerin.jacob, thomas, Honnappa Nagarahalli,
	Gavin Hu (Arm Technology China)

Hi Konstantin,

> -----Original Message-----
> From: Ananyev, Konstantin <konstantin.ananyev@intel.com>
> Sent: Friday, March 22, 2019 7:39 PM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>;
> dev@dpdk.org
> Cc: nd <nd@arm.com>; stephen@networkplumber.org;
> jerin.jacob@caviumnetworks.com; thomas@monjalon.net; Honnappa
> Nagarahalli <Honnappa.Nagarahalli@arm.com>; Gavin Hu (Arm Technology
> China) <Gavin.Hu@arm.com>
> Subject: RE: [PATCH v7 3/3] test/ticketlock: add ticket lock test case
> 
> 
> 
> >
> > Add test cases for ticket lock, recursive ticket lock, and ticket lock
> > performance.
> >
> > Signed-off-by: Joyce Kong <joyce.kong@arm.com>
> > Reviewed-by: Gavin Hu <gavin.hu@arm.com>
> > Reviewed-by: Ruifeng Wang <ruifeng.wang@arm.com>
> > ---
> >  MAINTAINERS                |   1 +
> >  app/test/Makefile          |   1 +
> >  app/test/autotest_data.py  |   6 +
> >  app/test/meson.build       |   1 +
> >  app/test/test_ticketlock.c | 311
> > +++++++++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 320 insertions(+)
> >  create mode 100644 app/test/test_ticketlock.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS index 3521271..b1ed4cc 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -213,6 +213,7 @@ F: app/test/test_bitmap.c  Ticketlock
> >  M: Joyce Kong <joyce.kong@arm.com>
> >  F: lib/librte_eal/common/include/generic/rte_ticketlock.h
> > +F: app/test/test_ticketlock.c
> >
> >  ARM v7
> >  M: Jan Viktorin <viktorin@rehivetech.com> diff --git
> > a/app/test/Makefile b/app/test/Makefile index 89949c2..d6aa28b 100644
> > --- a/app/test/Makefile
> > +++ b/app/test/Makefile
> > @@ -65,6 +65,7 @@ SRCS-y += test_barrier.c  SRCS-y += test_malloc.c
> > SRCS-y += test_cycles.c  SRCS-y += test_spinlock.c
> > +SRCS-y += test_ticketlock.c
> >  SRCS-y += test_memory.c
> >  SRCS-y += test_memzone.c
> >  SRCS-y += test_bitmap.c
> > diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
> > index 5f87bb9..db25274 100644
> > --- a/app/test/autotest_data.py
> > +++ b/app/test/autotest_data.py
> > @@ -171,6 +171,12 @@
> >          "Report":  None,
> >      },
> >      {
> > +        "Name":    "Ticketlock autotest",
> > +        "Command": "ticketlock_autotest",
> > +        "Func":    ticketlock_autotest,
> > +        "Report":  None,
> > +    }
> > +    {
> >          "Name":    "Byte order autotest",
> >          "Command": "byteorder_autotest",
> >          "Func":    default_autotest,
> > diff --git a/app/test/meson.build b/app/test/meson.build index
> > 05e5dde..ddb4d09 100644
> > --- a/app/test/meson.build
> > +++ b/app/test/meson.build
> > @@ -107,6 +107,7 @@ test_sources = files('commands.c',
> >  	'test_timer.c',
> >  	'test_timer_perf.c',
> >  	'test_timer_racecond.c',
> > +	'test_ticketlock.c',
> >  	'test_version.c',
> >  	'virtual_pmd.c'
> >  )
> > diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
> > new file mode 100644 index 0000000..67281ce
> > --- /dev/null
> > +++ b/app/test/test_ticketlock.c
> > @@ -0,0 +1,311 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2018-2019 Arm Limited  */
> > +
> > +#include <inttypes.h>
> > +#include <stdint.h>
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <sys/queue.h>
> > +#include <unistd.h>
> > +
> > +#include <rte_atomic.h>
> > +#include <rte_common.h>
> > +#include <rte_cycles.h>
> > +#include <rte_eal.h>
> > +#include <rte_launch.h>
> > +#include <rte_lcore.h>
> > +#include <rte_memory.h>
> > +#include <rte_per_lcore.h>
> > +#include <rte_ticketlock.h>
> > +
> > +#include "test.h"
> > +
> > +/*
> > + * Ticketlock test
> > + * =============
> > + *
> > + * - There is a global ticketlock and a table of ticketlocks (one per lcore).
> > + *
> > + * - The test function takes all of these locks and launches the
> > + *   ``test_ticketlock_per_core()`` function on each core (except the master).
> > + *
> > + *   - The function takes the global lock, display something, then releases
> > + *     the global lock.
> > + *   - The function takes the per-lcore lock, display something, then
> releases
> > + *     the per-core lock.
> > + *
> > + * - The main function unlocks the per-lcore locks sequentially and
> > + *   waits between each lock. This triggers the display of a message
> > + *   for each core, in the correct order. The autotest script checks that
> > + *   this order is correct.
> > + *
> > + * - A load test is carried out, with all cores attempting to lock a single lock
> > + *   multiple times
> > + */
> > +
> > +static rte_ticketlock_t tl, tl_try;
> > +static rte_ticketlock_t tl_tab[RTE_MAX_LCORE]; static
> > +rte_ticketlock_recursive_t tlr; static unsigned int count;
> > +
> > +static rte_atomic32_t synchro;
> > +
> > +static int
> > +test_ticketlock_per_core(__attribute__((unused)) void *arg) {
> > +	rte_ticketlock_lock(&tl);
> > +	printf("Global lock taken on core %u\n", rte_lcore_id());
> > +	rte_ticketlock_unlock(&tl);
> > +
> > +	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
> > +	printf("Hello from core %u !\n", rte_lcore_id());
> > +	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
> > +
> > +	return 0;
> > +}
> 
> I think that's probably no enough for functional testing.
> Need something extra to ensure that it provides correct locking in MT env.
> Probably extend the perf test below to do both?
> Something like that:
> 
> static uint64_t lcount __rte_cache_aligned; static uint64_t
> lcore_count[RTE_MAX_LCORE] __rte_cache_aligned;
> 
> ...
> 
> load_loop_fn(...)
> {
>    ...
>    rte_ticketlock_lock(&lk);
>    lcount++;
>    rte_ticketlock_unlock(&lk);
>    lcore_count[current_lcore]++;
> }
> 
> Then in test_ticketlock_perf() make sure that sum of al lcore_count[] values
> equals to lcount value:
> tcount = 0;
> for (i = 0; i != RTE_DIM(lcore_count); i++)
>    tcount += lcore_count[i];
> 
> if (tcount != lcount)
>   <error>
> 
> Same thought for trylock.
> Konstantin
> 

Got your opinion and will do this in next version.

> > +
> > +static int
> > +test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
> > +{
> > +	unsigned int id = rte_lcore_id();
> > +
> > +	rte_ticketlock_recursive_lock(&tlr);
> > +	printf("Global recursive lock taken on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +	rte_ticketlock_recursive_lock(&tlr);
> > +	printf("Global recursive lock taken on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +	rte_ticketlock_recursive_lock(&tlr);
> > +	printf("Global recursive lock taken on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +
> > +	printf("Hello from within recursive locks from core %u !\n", id);
> > +
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	printf("Global recursive lock released on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	printf("Global recursive lock released on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	printf("Global recursive lock released on core %u - count = %d\n",
> > +	       id, tlr.count);
> > +
> > +	return 0;
> > +}
> > +
> > +static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER; static
> > +uint64_t lock_count[RTE_MAX_LCORE] = {0};
> > +
> > +#define TIME_MS 100
> > +
> > +static int
> > +load_loop_fn(void *func_param)
> > +{
> > +	uint64_t time_diff = 0, begin;
> > +	uint64_t hz = rte_get_timer_hz();
> > +	uint64_t lcount = 0;
> > +	const int use_lock = *(int *)func_param;
> > +	const unsigned int lcore = rte_lcore_id();
> > +
> > +	/* wait synchro for slaves */
> > +	if (lcore != rte_get_master_lcore())
> > +		while (rte_atomic32_read(&synchro) == 0)
> > +			;
> > +
> > +	begin = rte_get_timer_cycles();
> > +	while (time_diff < hz * TIME_MS / 1000) {
> > +		if (use_lock)
> > +			rte_ticketlock_lock(&lk);
> > +		lcount++;
> > +		if (use_lock)
> > +			rte_ticketlock_unlock(&lk);
> > +		/* delay to make lock duty cycle slighlty realistic */
> 
> Probably better to do here the same as in test spinlock patches:
>  - remove delay_us()
> - move
>   time_diff = rte_get_timer_cycles() - begin;  out of the loop and report
> aggregate cycles.
> 

Will do the same as test spinlock patches in next version.

> > +		rte_delay_us(1);
> > +		time_diff = rte_get_timer_cycles() - begin;
> > +	}
> > +	lock_count[lcore] = lcount;
> > +	return 0;
> > +}
> > +
> > +static int
> > +test_ticketlock_perf(void)
> > +{
> > +	unsigned int i;
> > +	uint64_t total = 0;
> > +	int lock = 0;
> > +	const unsigned int lcore = rte_lcore_id();
> > +
> > +	printf("\nTest with no lock on single core...\n");
> > +	load_loop_fn(&lock);
> > +	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
> > +	memset(lock_count, 0, sizeof(lock_count));
> > +
> > +	printf("\nTest with lock on single core...\n");
> > +	lock = 1;
> > +	load_loop_fn(&lock);
> > +	printf("Core [%u] count = %"PRIu64"\n", lcore, lock_count[lcore]);
> > +	memset(lock_count, 0, sizeof(lock_count));
> > +
> > +	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
> > +
> > +	/* Clear synchro and start slaves */
> > +	rte_atomic32_set(&synchro, 0);
> > +	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
> > +
> > +	/* start synchro and launch test on master */
> > +	rte_atomic32_set(&synchro, 1);
> > +	load_loop_fn(&lock);
> > +
> > +	rte_eal_mp_wait_lcore();
> > +
> > +	RTE_LCORE_FOREACH(i) {
> > +		printf("Core [%u] count = %"PRIu64"\n", i, lock_count[i]);
> > +		total += lock_count[i];
> > +	}
> > +
> > +	printf("Total count = %"PRIu64"\n", total);
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * Use rte_ticketlock_trylock() to trylock a ticketlock object,
> > + * If it could not lock the object successfully, it would
> > + * return immediately and the variable of "count" would be
> > + * increased by one per times. the value of "count" could be
> > + * checked as the result later.
> > + */
> > +static int
> > +test_ticketlock_try(__attribute__((unused)) void *arg) {
> > +	if (rte_ticketlock_trylock(&tl_try) == 0) {
> > +		rte_ticketlock_lock(&tl);
> > +		count++;
> > +		rte_ticketlock_unlock(&tl);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +/*
> > + * Test rte_eal_get_lcore_state() in addition to ticketlocks
> > + * as we have "waiting" then "running" lcores.
> > + */
> > +static int
> > +test_ticketlock(void)
> > +{
> > +	int ret = 0;
> > +	int i;
> > +
> > +	/* slave cores should be waiting: print it */
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		printf("lcore %d state: %d\n", i,
> > +		       (int) rte_eal_get_lcore_state(i));
> > +	}
> > +
> > +	rte_ticketlock_init(&tl);
> > +	rte_ticketlock_init(&tl_try);
> > +	rte_ticketlock_recursive_init(&tlr);
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_ticketlock_init(&tl_tab[i]);
> > +	}
> > +
> > +	rte_ticketlock_lock(&tl);
> > +
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_ticketlock_lock(&tl_tab[i]);
> > +		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
> > +	}
> > +
> > +	/* slave cores should be busy: print it */
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		printf("lcore %d state: %d\n", i,
> > +		       (int) rte_eal_get_lcore_state(i));
> > +	}
> > +	rte_ticketlock_unlock(&tl);
> > +
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_ticketlock_unlock(&tl_tab[i]);
> > +		rte_delay_ms(10);
> > +	}
> > +
> > +	rte_eal_mp_wait_lcore();
> > +
> > +	rte_ticketlock_recursive_lock(&tlr);
> > +
> > +	/*
> > +	 * Try to acquire a lock that we already own
> > +	 */
> > +	if (!rte_ticketlock_recursive_trylock(&tlr)) {
> > +		printf("rte_ticketlock_recursive_trylock failed on a lock that "
> > +		       "we already own\n");
> > +		ret = -1;
> > +	} else
> > +		rte_ticketlock_recursive_unlock(&tlr);
> > +
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
> > +					NULL, i);
> > +	}
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	rte_eal_mp_wait_lcore();
> > +
> > +	/*
> > +	 * Test if it could return immediately from try-locking a locked object.
> > +	 * Here it will lock the ticketlock object first, then launch all the
> > +	 * slave lcores to trylock the same ticketlock object.
> > +	 * All the slave lcores should give up try-locking a locked object and
> > +	 * return immediately, and then increase the "count" initialized with
> > +	 * zero by one per times.
> > +	 * We can check if the "count" is finally equal to the number of all
> > +	 * slave lcores to see if the behavior of try-locking a locked
> > +	 * ticketlock object is correct.
> > +	 */
> > +	if (rte_ticketlock_trylock(&tl_try) == 0)
> > +		return -1;
> > +
> > +	count = 0;
> > +	RTE_LCORE_FOREACH_SLAVE(i) {
> > +		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
> > +	}
> > +	rte_eal_mp_wait_lcore();
> > +	rte_ticketlock_unlock(&tl_try);
> > +	if (rte_ticketlock_is_locked(&tl)) {
> > +		printf("ticketlock is locked but it should not be\n");
> > +		return -1;
> > +	}
> > +	rte_ticketlock_lock(&tl);
> > +	if (count != (rte_lcore_count() - 1))
> > +		ret = -1;
> > +
> > +	rte_ticketlock_unlock(&tl);
> > +
> > +	/*
> > +	 * Test if it can trylock recursively.
> > +	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
> > +	 * a ticketlock object recursively. Here it will try to lock a
> > +	 * ticketlock object twice.
> > +	 */
> > +	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
> > +		printf("It failed to do the first ticketlock_recursive_trylock "
> > +			   "but it should able to do\n");
> > +		return -1;
> > +	}
> > +	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
> > +		printf("It failed to do the second ticketlock_recursive_trylock
> "
> > +			   "but it should able to do\n");
> > +		return -1;
> > +	}
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +	rte_ticketlock_recursive_unlock(&tlr);
> > +
> > +	if (test_ticketlock_perf() < 0)
> > +		return -1;
> > +
> > +	return ret;
> > +}
> > +
> > +REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
> > --
> > 2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (9 preceding siblings ...)
  2019-03-21  9:15   ` [dpdk-dev] [PATCH v7 1/3] eal/ticketlock: ticket based to improve fairness Joyce Kong
@ 2019-03-25 11:11   ` Joyce Kong
  2019-03-25 11:11     ` Joyce Kong
  2019-03-27 11:20     ` Ananyev, Konstantin
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 1/3] eal/ticketlock: ticket based to improve fairness Joyce Kong
                     ` (2 subsequent siblings)
  13 siblings, 2 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-25 11:11 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

v8:
    1. Enhance functional test for ticketlock by adding extra verification to ensure correct locking in MT env.
    2. Amortize the cost of getting time in test case.
    (Suggested by Konstantin Ananyev)

v7:
    1. Modify trylock to compare both current and next fields to gurantee the lock
    being obtained only if tl->current==1(lock is free).
    As within the trylock function, suppose next==curr=1, then this thread will
    experience really long context switch, and next time it continues its
    execution and tl->next wraps around to 1 again and tl_couurent==0(lock
    held by another thread),this trylock will return a success, that means
    two threads holding the lock. (Suggested by Konstantin Ananyev)
    2. Let all archs use generic ticketlock implementation.

v6:
    Add rte_ticketlock.h in lib/librte_eal/common/include/arch/arm/.
    Sort header inside ticketlock files by alphabetical order.

v5:
    Change the variants inside rte_ticket_lock from unint to uint16_t for binary
    compatibility with the plain spin lock(suggested by Honnappa Nagarahalli)).

v4:
    Change some assignment operation in recursive ticket lock to __atomic.

V3:
    1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
    2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).

V2:
    1.Update commit message(suggested by Jerin Jacob).
    2.Add ticketlock test cases(suggested by Jerin Jacob).

V1:
    Implement ticket lock to improve lock fairness and prdictability.

    As shown on thundex-2 platform:
    *** ticketlock_autotest with this patch ***
        Core [0] count = 496
        Core [1] count = 495
        Core [2] count = 498
        ...
        Core [209] count = 488
        Core [210] count = 490
        Core [211] count = 474

Joyce Kong (3):
  eal/ticketlock: ticket based to improve fairness
  eal/ticketlock: enable generic ticketlock on all arch
  test/ticketlock: add ticket lock test case

 MAINTAINERS                                        |   5 +
 app/test/Makefile                                  |   1 +
 app/test/autotest_data.py                          |   6 +
 app/test/meson.build                               |   1 +
 app/test/test_ticketlock.c                         | 319 +++++++++++++++++++++
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/arch/arm/rte_ticketlock.h       |  22 ++
 .../common/include/arch/ppc_64/rte_ticketlock.h    |  18 ++
 .../common/include/arch/x86/rte_ticketlock.h       |  18 ++
 .../common/include/generic/rte_ticketlock.h        | 215 ++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 12 files changed, 608 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_ticketlock.c
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case Joyce Kong
@ 2019-03-25 11:11     ` Joyce Kong
  2019-03-27 11:20     ` Ananyev, Konstantin
  1 sibling, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-25 11:11 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

v8:
    1. Enhance functional test for ticketlock by adding extra verification to ensure correct locking in MT env.
    2. Amortize the cost of getting time in test case.
    (Suggested by Konstantin Ananyev)

v7:
    1. Modify trylock to compare both current and next fields to gurantee the lock
    being obtained only if tl->current==1(lock is free).
    As within the trylock function, suppose next==curr=1, then this thread will
    experience really long context switch, and next time it continues its
    execution and tl->next wraps around to 1 again and tl_couurent==0(lock
    held by another thread),this trylock will return a success, that means
    two threads holding the lock. (Suggested by Konstantin Ananyev)
    2. Let all archs use generic ticketlock implementation.

v6:
    Add rte_ticketlock.h in lib/librte_eal/common/include/arch/arm/.
    Sort header inside ticketlock files by alphabetical order.

v5:
    Change the variants inside rte_ticket_lock from unint to uint16_t for binary
    compatibility with the plain spin lock(suggested by Honnappa Nagarahalli)).

v4:
    Change some assignment operation in recursive ticket lock to __atomic.

V3:
    1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
    2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).

V2:
    1.Update commit message(suggested by Jerin Jacob).
    2.Add ticketlock test cases(suggested by Jerin Jacob).

V1:
    Implement ticket lock to improve lock fairness and prdictability.

    As shown on thundex-2 platform:
    *** ticketlock_autotest with this patch ***
        Core [0] count = 496
        Core [1] count = 495
        Core [2] count = 498
        ...
        Core [209] count = 488
        Core [210] count = 490
        Core [211] count = 474

Joyce Kong (3):
  eal/ticketlock: ticket based to improve fairness
  eal/ticketlock: enable generic ticketlock on all arch
  test/ticketlock: add ticket lock test case

 MAINTAINERS                                        |   5 +
 app/test/Makefile                                  |   1 +
 app/test/autotest_data.py                          |   6 +
 app/test/meson.build                               |   1 +
 app/test/test_ticketlock.c                         | 319 +++++++++++++++++++++
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/arch/arm/rte_ticketlock.h       |  22 ++
 .../common/include/arch/ppc_64/rte_ticketlock.h    |  18 ++
 .../common/include/arch/x86/rte_ticketlock.h       |  18 ++
 .../common/include/generic/rte_ticketlock.h        | 215 ++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 12 files changed, 608 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_ticketlock.c
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v8 1/3] eal/ticketlock: ticket based to improve fairness
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (10 preceding siblings ...)
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case Joyce Kong
@ 2019-03-25 11:11   ` Joyce Kong
  2019-03-25 11:11     ` Joyce Kong
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 2/3] eal/ticketlock: enable generic ticketlock on all arch Joyce Kong
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case Joyce Kong
  13 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-03-25 11:11 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.

This patch introduces ticketlock which gives each waiting thread a
ticket and they can take the lock one by one. First come, first serviced.
This avoids starvation for too long time and is more predictable.

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 MAINTAINERS                                        |   4 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 215 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 5 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 452b8eb..3521271 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -210,6 +210,10 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 F: lib/librte_eal/common/include/rte_bitmap.h
 F: app/test/test_bitmap.c
 
+Ticketlock
+M: Joyce Kong <joyce.kong@arm.com>
+F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
 M: Gavin Hu <gavin.hu@arm.com>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index c487201..ac3305c 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..191146f
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticket locks
+ *
+ * This file defines an API for ticket locks, which give each waiting
+ * thread a ticket and take the lock one by one, first come, first
+ * serviced.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_lcore.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef union {
+	uint32_t tickets;
+	struct {
+		uint16_t current;
+		uint16_t next;
+	} s;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->tickets, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->s.next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->s.current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	uint16_t i = __atomic_load_n(&tl->s.current, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->s.current, i + 1, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_t old, new;
+	old.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);
+	new.tickets = old.tickets;
+	new.s.next++;
+	if (old.s.next == old.s.current) {
+		if (__atomic_compare_exchange_n(&tl->tickets, &old.tickets,
+		    new.tickets, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is currently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_t tic;
+	tic.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_ACQUIRE);
+	return (tic.s.current != tic.s.next);
+}
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+#define TICKET_LOCK_INVALID_ID -1
+
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	int user; /**< core id using lock, TICKET_LOCK_INVALID_ID for unused */
+	unsigned int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, \
+					      TICKET_LOCK_INVALID_ID, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_init(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED);
+	tlr->count = 0;
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_lock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_unlock(rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID,
+				 __ATOMIC_RELAXED);
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_recursive_trylock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+	return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 5ecae0b..0670e41 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -99,6 +99,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v8 1/3] eal/ticketlock: ticket based to improve fairness
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 1/3] eal/ticketlock: ticket based to improve fairness Joyce Kong
@ 2019-03-25 11:11     ` Joyce Kong
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-25 11:11 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

The spinlock implementation is unfair, some threads may take locks
aggressively while leaving the other threads starving for long time.

This patch introduces ticketlock which gives each waiting thread a
ticket and they can take the lock one by one. First come, first serviced.
This avoids starvation for too long time and is more predictable.

Suggested-by: Jerin Jacob <jerinj@marvell.com>
Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>
Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 MAINTAINERS                                        |   4 +
 doc/api/doxy-api-index.md                          |   1 +
 lib/librte_eal/common/Makefile                     |   2 +-
 .../common/include/generic/rte_ticketlock.h        | 215 +++++++++++++++++++++
 lib/librte_eal/common/meson.build                  |   1 +
 5 files changed, 222 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 452b8eb..3521271 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -210,6 +210,10 @@ M: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 F: lib/librte_eal/common/include/rte_bitmap.h
 F: app/test/test_bitmap.c
 
+Ticketlock
+M: Joyce Kong <joyce.kong@arm.com>
+F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
 M: Gavin Hu <gavin.hu@arm.com>
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index d95ad56..aacc66b 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -65,6 +65,7 @@ The public API headers are grouped by topics:
   [atomic]             (@ref rte_atomic.h),
   [rwlock]             (@ref rte_rwlock.h),
   [spinlock]           (@ref rte_spinlock.h)
+  [ticketlock]         (@ref rte_ticketlock.h)
 
 - **CPU arch**:
   [branch prediction]  (@ref rte_branch_prediction.h),
diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile
index c487201..ac3305c 100644
--- a/lib/librte_eal/common/Makefile
+++ b/lib/librte_eal/common/Makefile
@@ -20,7 +20,7 @@ INC += rte_bitmap.h rte_vfio.h rte_hypervisor.h rte_test.h
 INC += rte_reciprocal.h rte_fbarray.h rte_uuid.h
 
 GENERIC_INC := rte_atomic.h rte_byteorder.h rte_cycles.h rte_prefetch.h
-GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h
+GENERIC_INC += rte_spinlock.h rte_memcpy.h rte_cpuflags.h rte_rwlock.h rte_ticketlock.h
 GENERIC_INC += rte_vect.h rte_pause.h rte_io.h
 
 # defined in mk/arch/$(RTE_ARCH)/rte.vars.mk
diff --git a/lib/librte_eal/common/include/generic/rte_ticketlock.h b/lib/librte_eal/common/include/generic/rte_ticketlock.h
new file mode 100644
index 0000000..191146f
--- /dev/null
+++ b/lib/librte_eal/common/include/generic/rte_ticketlock.h
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_H_
+#define _RTE_TICKETLOCK_H_
+
+/**
+ * @file
+ *
+ * RTE ticket locks
+ *
+ * This file defines an API for ticket locks, which give each waiting
+ * thread a ticket and take the lock one by one, first come, first
+ * serviced.
+ *
+ * All locks must be initialised before use, and only initialised once.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_common.h>
+#include <rte_lcore.h>
+#include <rte_pause.h>
+
+/**
+ * The rte_ticketlock_t type.
+ */
+typedef union {
+	uint32_t tickets;
+	struct {
+		uint16_t current;
+		uint16_t next;
+	} s;
+} rte_ticketlock_t;
+
+/**
+ * A static ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_INITIALIZER { 0 }
+
+/**
+ * Initialize the ticketlock to an unlocked state.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_init(rte_ticketlock_t *tl)
+{
+	__atomic_store_n(&tl->tickets, 0, __ATOMIC_RELAXED);
+}
+
+/**
+ * Take the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_lock(rte_ticketlock_t *tl)
+{
+	uint16_t me = __atomic_fetch_add(&tl->s.next, 1, __ATOMIC_RELAXED);
+	while (__atomic_load_n(&tl->s.current, __ATOMIC_ACQUIRE) != me)
+		rte_pause();
+}
+
+/**
+ * Release the ticketlock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_unlock(rte_ticketlock_t *tl)
+{
+	uint16_t i = __atomic_load_n(&tl->s.current, __ATOMIC_RELAXED);
+	__atomic_store_n(&tl->s.current, i + 1, __ATOMIC_RELEASE);
+}
+
+/**
+ * Try to take the lock.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_trylock(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_t old, new;
+	old.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_RELAXED);
+	new.tickets = old.tickets;
+	new.s.next++;
+	if (old.s.next == old.s.current) {
+		if (__atomic_compare_exchange_n(&tl->tickets, &old.tickets,
+		    new.tickets, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Test if the lock is taken.
+ *
+ * @param tl
+ *   A pointer to the ticketlock.
+ * @return
+ *   1 if the lock is currently taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_is_locked(rte_ticketlock_t *tl)
+{
+	rte_ticketlock_t tic;
+	tic.tickets = __atomic_load_n(&tl->tickets, __ATOMIC_ACQUIRE);
+	return (tic.s.current != tic.s.next);
+}
+
+/**
+ * The rte_ticketlock_recursive_t type.
+ */
+#define TICKET_LOCK_INVALID_ID -1
+
+typedef struct {
+	rte_ticketlock_t tl; /**< the actual ticketlock */
+	int user; /**< core id using lock, TICKET_LOCK_INVALID_ID for unused */
+	unsigned int count; /**< count of time this lock has been called */
+} rte_ticketlock_recursive_t;
+
+/**
+ * A static recursive ticketlock initializer.
+ */
+#define RTE_TICKETLOCK_RECURSIVE_INITIALIZER {RTE_TICKETLOCK_INITIALIZER, \
+					      TICKET_LOCK_INVALID_ID, 0}
+
+/**
+ * Initialize the recursive ticketlock to an unlocked state.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_init(rte_ticketlock_recursive_t *tlr)
+{
+	rte_ticketlock_init(&tlr->tl);
+	__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID, __ATOMIC_RELAXED);
+	tlr->count = 0;
+}
+
+/**
+ * Take the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_lock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		rte_ticketlock_lock(&tlr->tl);
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+}
+
+/**
+ * Release the recursive ticketlock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ */
+static inline __rte_experimental void
+rte_ticketlock_recursive_unlock(rte_ticketlock_recursive_t *tlr)
+{
+	if (--(tlr->count) == 0) {
+		__atomic_store_n(&tlr->user, TICKET_LOCK_INVALID_ID,
+				 __ATOMIC_RELAXED);
+		rte_ticketlock_unlock(&tlr->tl);
+	}
+}
+
+/**
+ * Try to take the recursive lock.
+ *
+ * @param tlr
+ *   A pointer to the recursive ticketlock.
+ * @return
+ *   1 if the lock is successfully taken; 0 otherwise.
+ */
+static inline __rte_experimental int
+rte_ticketlock_recursive_trylock(rte_ticketlock_recursive_t *tlr)
+{
+	int id = rte_gettid();
+
+	if (__atomic_load_n(&tlr->user, __ATOMIC_RELAXED) != id) {
+		if (rte_ticketlock_trylock(&tlr->tl) == 0)
+			return 0;
+		__atomic_store_n(&tlr->user, id, __ATOMIC_RELAXED);
+	}
+	tlr->count++;
+	return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_H_ */
diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build
index 5ecae0b..0670e41 100644
--- a/lib/librte_eal/common/meson.build
+++ b/lib/librte_eal/common/meson.build
@@ -99,6 +99,7 @@ generic_headers = files(
 	'include/generic/rte_prefetch.h',
 	'include/generic/rte_rwlock.h',
 	'include/generic/rte_spinlock.h',
+	'include/generic/rte_ticketlock.h',
 	'include/generic/rte_vect.h')
 install_headers(generic_headers, subdir: 'generic')
 
-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v8 2/3] eal/ticketlock: enable generic ticketlock on all arch
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (11 preceding siblings ...)
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 1/3] eal/ticketlock: ticket based to improve fairness Joyce Kong
@ 2019-03-25 11:11   ` Joyce Kong
  2019-03-25 11:11     ` Joyce Kong
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case Joyce Kong
  13 siblings, 1 reply; 74+ messages in thread
From: Joyce Kong @ 2019-03-25 11:11 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

Let all architectures use generic ticketlock implementation.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
---
 .../common/include/arch/arm/rte_ticketlock.h       | 22 ++++++++++++++++++++++
 .../common/include/arch/ppc_64/rte_ticketlock.h    | 18 ++++++++++++++++++
 .../common/include/arch/x86/rte_ticketlock.h       | 18 ++++++++++++++++++
 3 files changed, 58 insertions(+)
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h

diff --git a/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
new file mode 100644
index 0000000..e09fbd6
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_ARM_H_
+#define _RTE_TICKETLOCK_ARM_H_
+
+#ifndef RTE_FORCE_INTRINSICS
+#  error Platform must be built with CONFIG_RTE_FORCE_INTRINSICS
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_ARM_H_ */
diff --git a/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h b/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
new file mode 100644
index 0000000..c175e9e
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_PPC_64_H_
+#define _RTE_TICKETLOCK_PPC_64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_PPC_64_H_ */
diff --git a/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h b/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
new file mode 100644
index 0000000..0cc01f6
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_X86_64_H_
+#define _RTE_TICKETLOCK_X86_64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_X86_64_H_ */
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v8 2/3] eal/ticketlock: enable generic ticketlock on all arch
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 2/3] eal/ticketlock: enable generic ticketlock on all arch Joyce Kong
@ 2019-03-25 11:11     ` Joyce Kong
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-25 11:11 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

Let all architectures use generic ticketlock implementation.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
---
 .../common/include/arch/arm/rte_ticketlock.h       | 22 ++++++++++++++++++++++
 .../common/include/arch/ppc_64/rte_ticketlock.h    | 18 ++++++++++++++++++
 .../common/include/arch/x86/rte_ticketlock.h       | 18 ++++++++++++++++++
 3 files changed, 58 insertions(+)
 create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
 create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h

diff --git a/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
new file mode 100644
index 0000000..e09fbd6
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_ARM_H_
+#define _RTE_TICKETLOCK_ARM_H_
+
+#ifndef RTE_FORCE_INTRINSICS
+#  error Platform must be built with CONFIG_RTE_FORCE_INTRINSICS
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_ARM_H_ */
diff --git a/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h b/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
new file mode 100644
index 0000000..c175e9e
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_PPC_64_H_
+#define _RTE_TICKETLOCK_PPC_64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_PPC_64_H_ */
diff --git a/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h b/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
new file mode 100644
index 0000000..0cc01f6
--- /dev/null
+++ b/lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Arm Limited
+ */
+
+#ifndef _RTE_TICKETLOCK_X86_64_H_
+#define _RTE_TICKETLOCK_X86_64_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "generic/rte_ticketlock.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TICKETLOCK_X86_64_H_ */
-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case
  2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
                     ` (12 preceding siblings ...)
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 2/3] eal/ticketlock: enable generic ticketlock on all arch Joyce Kong
@ 2019-03-25 11:11   ` Joyce Kong
  2019-03-25 11:11     ` Joyce Kong
  2019-04-08 20:18     ` David Marchand
  13 siblings, 2 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-25 11:11 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Phil Yang <phil.yang@arm.com>
---
 MAINTAINERS                |   1 +
 app/test/Makefile          |   1 +
 app/test/autotest_data.py  |   6 +
 app/test/meson.build       |   1 +
 app/test/test_ticketlock.c | 319 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 328 insertions(+)
 create mode 100644 app/test/test_ticketlock.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3521271..b1ed4cc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -213,6 +213,7 @@ F: app/test/test_bitmap.c
 Ticketlock
 M: Joyce Kong <joyce.kong@arm.com>
 F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+F: app/test/test_ticketlock.c
 
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
diff --git a/app/test/Makefile b/app/test/Makefile
index 89949c2..d6aa28b 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 5f87bb9..db25274 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report":  None,
+    }
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 05e5dde..ddb4d09 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -107,6 +107,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
new file mode 100644
index 0000000..13bdadb
--- /dev/null
+++ b/app/test/test_ticketlock.c
@@ -0,0 +1,319 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_atomic.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_ticketlock.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t tl, tl_try;
+static rte_ticketlock_t tl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t tlr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&tl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl);
+
+	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lcount __rte_cache_aligned;
+static uint64_t lcore_count[RTE_MAX_LCORE] __rte_cache_aligned;
+static uint64_t time_cost[RTE_MAX_LCORE];
+
+#define MAX_LOOP 10000
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_rdtsc_precise();
+	while (lcore_count[lcore] < MAX_LOOP) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcore_count[lcore]++;
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+	}
+	time_diff = rte_rdtsc_precise() - begin;
+	time_cost[lcore] = time_diff * 1000000 / hz;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t tcount = 0;
+	uint64_t total_time = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]);
+	memset(lcore_count, 0, sizeof(lcore_count));
+	memset(time_cost, 0, sizeof(time_cost));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]);
+	memset(lcore_count, 0, sizeof(lcore_count));
+	memset(time_cost, 0, sizeof(time_cost));
+
+	lcount = 0;
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] cost time = %"PRIu64" us\n", i, time_cost[i]);
+		tcount += lcore_count[i];
+		total_time += time_cost[i];
+	}
+
+	if (tcount != lcount)
+		return -1;
+
+	printf("Total cost time = %"PRIu64" us\n", total_time);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&tl_try) == 0) {
+		rte_ticketlock_lock(&tl);
+		count++;
+		rte_ticketlock_unlock(&tl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&tl);
+	rte_ticketlock_init(&tl_try);
+	rte_ticketlock_recursive_init(&tlr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&tl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&tl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&tl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&tlr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&tlr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&tlr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+					NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&tl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&tl_try);
+	if (rte_ticketlock_is_locked(&tl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&tl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&tl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_ticketlock_recursive_unlock(&tlr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case Joyce Kong
@ 2019-03-25 11:11     ` Joyce Kong
  2019-04-08 20:18     ` David Marchand
  1 sibling, 0 replies; 74+ messages in thread
From: Joyce Kong @ 2019-03-25 11:11 UTC (permalink / raw)
  To: dev
  Cc: nd, stephen, jerin.jacob, konstantin.ananyev, thomas,
	honnappa.nagarahalli, gavin.hu

Add test cases for ticket lock, recursive ticket lock,
and ticket lock performance.

Signed-off-by: Joyce Kong <joyce.kong@arm.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
Reviewed-by: Phil Yang <phil.yang@arm.com>
---
 MAINTAINERS                |   1 +
 app/test/Makefile          |   1 +
 app/test/autotest_data.py  |   6 +
 app/test/meson.build       |   1 +
 app/test/test_ticketlock.c | 319 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 328 insertions(+)
 create mode 100644 app/test/test_ticketlock.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3521271..b1ed4cc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -213,6 +213,7 @@ F: app/test/test_bitmap.c
 Ticketlock
 M: Joyce Kong <joyce.kong@arm.com>
 F: lib/librte_eal/common/include/generic/rte_ticketlock.h
+F: app/test/test_ticketlock.c
 
 ARM v7
 M: Jan Viktorin <viktorin@rehivetech.com>
diff --git a/app/test/Makefile b/app/test/Makefile
index 89949c2..d6aa28b 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -65,6 +65,7 @@ SRCS-y += test_barrier.c
 SRCS-y += test_malloc.c
 SRCS-y += test_cycles.c
 SRCS-y += test_spinlock.c
+SRCS-y += test_ticketlock.c
 SRCS-y += test_memory.c
 SRCS-y += test_memzone.c
 SRCS-y += test_bitmap.c
diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
index 5f87bb9..db25274 100644
--- a/app/test/autotest_data.py
+++ b/app/test/autotest_data.py
@@ -171,6 +171,12 @@
         "Report":  None,
     },
     {
+        "Name":    "Ticketlock autotest",
+        "Command": "ticketlock_autotest",
+        "Func":    ticketlock_autotest,
+        "Report":  None,
+    }
+    {
         "Name":    "Byte order autotest",
         "Command": "byteorder_autotest",
         "Func":    default_autotest,
diff --git a/app/test/meson.build b/app/test/meson.build
index 05e5dde..ddb4d09 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -107,6 +107,7 @@ test_sources = files('commands.c',
 	'test_timer.c',
 	'test_timer_perf.c',
 	'test_timer_racecond.c',
+	'test_ticketlock.c',
 	'test_version.c',
 	'virtual_pmd.c'
 )
diff --git a/app/test/test_ticketlock.c b/app/test/test_ticketlock.c
new file mode 100644
index 0000000..13bdadb
--- /dev/null
+++ b/app/test/test_ticketlock.c
@@ -0,0 +1,319 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2019 Arm Limited
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include <rte_atomic.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_eal.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+#include <rte_ticketlock.h>
+
+#include "test.h"
+
+/*
+ * Ticketlock test
+ * =============
+ *
+ * - There is a global ticketlock and a table of ticketlocks (one per lcore).
+ *
+ * - The test function takes all of these locks and launches the
+ *   ``test_ticketlock_per_core()`` function on each core (except the master).
+ *
+ *   - The function takes the global lock, display something, then releases
+ *     the global lock.
+ *   - The function takes the per-lcore lock, display something, then releases
+ *     the per-core lock.
+ *
+ * - The main function unlocks the per-lcore locks sequentially and
+ *   waits between each lock. This triggers the display of a message
+ *   for each core, in the correct order. The autotest script checks that
+ *   this order is correct.
+ *
+ * - A load test is carried out, with all cores attempting to lock a single lock
+ *   multiple times
+ */
+
+static rte_ticketlock_t tl, tl_try;
+static rte_ticketlock_t tl_tab[RTE_MAX_LCORE];
+static rte_ticketlock_recursive_t tlr;
+static unsigned int count;
+
+static rte_atomic32_t synchro;
+
+static int
+test_ticketlock_per_core(__attribute__((unused)) void *arg)
+{
+	rte_ticketlock_lock(&tl);
+	printf("Global lock taken on core %u\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl);
+
+	rte_ticketlock_lock(&tl_tab[rte_lcore_id()]);
+	printf("Hello from core %u !\n", rte_lcore_id());
+	rte_ticketlock_unlock(&tl_tab[rte_lcore_id()]);
+
+	return 0;
+}
+
+static int
+test_ticketlock_recursive_per_core(__attribute__((unused)) void *arg)
+{
+	unsigned int id = rte_lcore_id();
+
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_lock(&tlr);
+	printf("Global recursive lock taken on core %u - count = %d\n",
+	       id, tlr.count);
+
+	printf("Hello from within recursive locks from core %u !\n", id);
+
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+	rte_ticketlock_recursive_unlock(&tlr);
+	printf("Global recursive lock released on core %u - count = %d\n",
+	       id, tlr.count);
+
+	return 0;
+}
+
+static rte_ticketlock_t lk = RTE_TICKETLOCK_INITIALIZER;
+static uint64_t lcount __rte_cache_aligned;
+static uint64_t lcore_count[RTE_MAX_LCORE] __rte_cache_aligned;
+static uint64_t time_cost[RTE_MAX_LCORE];
+
+#define MAX_LOOP 10000
+
+static int
+load_loop_fn(void *func_param)
+{
+	uint64_t time_diff = 0, begin;
+	uint64_t hz = rte_get_timer_hz();
+	const int use_lock = *(int *)func_param;
+	const unsigned int lcore = rte_lcore_id();
+
+	/* wait synchro for slaves */
+	if (lcore != rte_get_master_lcore())
+		while (rte_atomic32_read(&synchro) == 0)
+			;
+
+	begin = rte_rdtsc_precise();
+	while (lcore_count[lcore] < MAX_LOOP) {
+		if (use_lock)
+			rte_ticketlock_lock(&lk);
+		lcore_count[lcore]++;
+		lcount++;
+		if (use_lock)
+			rte_ticketlock_unlock(&lk);
+	}
+	time_diff = rte_rdtsc_precise() - begin;
+	time_cost[lcore] = time_diff * 1000000 / hz;
+	return 0;
+}
+
+static int
+test_ticketlock_perf(void)
+{
+	unsigned int i;
+	uint64_t tcount = 0;
+	uint64_t total_time = 0;
+	int lock = 0;
+	const unsigned int lcore = rte_lcore_id();
+
+	printf("\nTest with no lock on single core...\n");
+	load_loop_fn(&lock);
+	printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]);
+	memset(lcore_count, 0, sizeof(lcore_count));
+	memset(time_cost, 0, sizeof(time_cost));
+
+	printf("\nTest with lock on single core...\n");
+	lock = 1;
+	load_loop_fn(&lock);
+	printf("Core [%u] cost time = %"PRIu64" us\n", lcore, time_cost[lcore]);
+	memset(lcore_count, 0, sizeof(lcore_count));
+	memset(time_cost, 0, sizeof(time_cost));
+
+	lcount = 0;
+	printf("\nTest with lock on %u cores...\n", rte_lcore_count());
+
+	/* Clear synchro and start slaves */
+	rte_atomic32_set(&synchro, 0);
+	rte_eal_mp_remote_launch(load_loop_fn, &lock, SKIP_MASTER);
+
+	/* start synchro and launch test on master */
+	rte_atomic32_set(&synchro, 1);
+	load_loop_fn(&lock);
+
+	rte_eal_mp_wait_lcore();
+
+	RTE_LCORE_FOREACH(i) {
+		printf("Core [%u] cost time = %"PRIu64" us\n", i, time_cost[i]);
+		tcount += lcore_count[i];
+		total_time += time_cost[i];
+	}
+
+	if (tcount != lcount)
+		return -1;
+
+	printf("Total cost time = %"PRIu64" us\n", total_time);
+
+	return 0;
+}
+
+/*
+ * Use rte_ticketlock_trylock() to trylock a ticketlock object,
+ * If it could not lock the object successfully, it would
+ * return immediately and the variable of "count" would be
+ * increased by one per times. the value of "count" could be
+ * checked as the result later.
+ */
+static int
+test_ticketlock_try(__attribute__((unused)) void *arg)
+{
+	if (rte_ticketlock_trylock(&tl_try) == 0) {
+		rte_ticketlock_lock(&tl);
+		count++;
+		rte_ticketlock_unlock(&tl);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Test rte_eal_get_lcore_state() in addition to ticketlocks
+ * as we have "waiting" then "running" lcores.
+ */
+static int
+test_ticketlock(void)
+{
+	int ret = 0;
+	int i;
+
+	/* slave cores should be waiting: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+
+	rte_ticketlock_init(&tl);
+	rte_ticketlock_init(&tl_try);
+	rte_ticketlock_recursive_init(&tlr);
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_init(&tl_tab[i]);
+	}
+
+	rte_ticketlock_lock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_lock(&tl_tab[i]);
+		rte_eal_remote_launch(test_ticketlock_per_core, NULL, i);
+	}
+
+	/* slave cores should be busy: print it */
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		printf("lcore %d state: %d\n", i,
+		       (int) rte_eal_get_lcore_state(i));
+	}
+	rte_ticketlock_unlock(&tl);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_ticketlock_unlock(&tl_tab[i]);
+		rte_delay_ms(10);
+	}
+
+	rte_eal_mp_wait_lcore();
+
+	rte_ticketlock_recursive_lock(&tlr);
+
+	/*
+	 * Try to acquire a lock that we already own
+	 */
+	if (!rte_ticketlock_recursive_trylock(&tlr)) {
+		printf("rte_ticketlock_recursive_trylock failed on a lock that "
+		       "we already own\n");
+		ret = -1;
+	} else
+		rte_ticketlock_recursive_unlock(&tlr);
+
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_recursive_per_core,
+					NULL, i);
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_eal_mp_wait_lcore();
+
+	/*
+	 * Test if it could return immediately from try-locking a locked object.
+	 * Here it will lock the ticketlock object first, then launch all the
+	 * slave lcores to trylock the same ticketlock object.
+	 * All the slave lcores should give up try-locking a locked object and
+	 * return immediately, and then increase the "count" initialized with
+	 * zero by one per times.
+	 * We can check if the "count" is finally equal to the number of all
+	 * slave lcores to see if the behavior of try-locking a locked
+	 * ticketlock object is correct.
+	 */
+	if (rte_ticketlock_trylock(&tl_try) == 0)
+		return -1;
+
+	count = 0;
+	RTE_LCORE_FOREACH_SLAVE(i) {
+		rte_eal_remote_launch(test_ticketlock_try, NULL, i);
+	}
+	rte_eal_mp_wait_lcore();
+	rte_ticketlock_unlock(&tl_try);
+	if (rte_ticketlock_is_locked(&tl)) {
+		printf("ticketlock is locked but it should not be\n");
+		return -1;
+	}
+	rte_ticketlock_lock(&tl);
+	if (count != (rte_lcore_count() - 1))
+		ret = -1;
+
+	rte_ticketlock_unlock(&tl);
+
+	/*
+	 * Test if it can trylock recursively.
+	 * Use rte_ticketlock_recursive_trylock() to check if it can lock
+	 * a ticketlock object recursively. Here it will try to lock a
+	 * ticketlock object twice.
+	 */
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the first ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	if (rte_ticketlock_recursive_trylock(&tlr) == 0) {
+		printf("It failed to do the second ticketlock_recursive_trylock "
+			   "but it should able to do\n");
+		return -1;
+	}
+	rte_ticketlock_recursive_unlock(&tlr);
+	rte_ticketlock_recursive_unlock(&tlr);
+
+	if (test_ticketlock_perf() < 0)
+		return -1;
+
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(ticketlock_autotest, test_ticketlock);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case Joyce Kong
  2019-03-25 11:11     ` Joyce Kong
@ 2019-03-27 11:20     ` Ananyev, Konstantin
  2019-03-27 11:20       ` Ananyev, Konstantin
  2019-03-28 14:02       ` Thomas Monjalon
  1 sibling, 2 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-27 11:20 UTC (permalink / raw)
  To: Joyce Kong, dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu


> 
> v8:
>     1. Enhance functional test for ticketlock by adding extra verification to ensure correct locking in MT env.
>     2. Amortize the cost of getting time in test case.
>     (Suggested by Konstantin Ananyev)
> 
> v7:
>     1. Modify trylock to compare both current and next fields to gurantee the lock
>     being obtained only if tl->current==1(lock is free).
>     As within the trylock function, suppose next==curr=1, then this thread will
>     experience really long context switch, and next time it continues its
>     execution and tl->next wraps around to 1 again and tl_couurent==0(lock
>     held by another thread),this trylock will return a success, that means
>     two threads holding the lock. (Suggested by Konstantin Ananyev)
>     2. Let all archs use generic ticketlock implementation.
> 
> v6:
>     Add rte_ticketlock.h in lib/librte_eal/common/include/arch/arm/.
>     Sort header inside ticketlock files by alphabetical order.
> 
> v5:
>     Change the variants inside rte_ticket_lock from unint to uint16_t for binary
>     compatibility with the plain spin lock(suggested by Honnappa Nagarahalli)).
> 
> v4:
>     Change some assignment operation in recursive ticket lock to __atomic.
> 
> V3:
>     1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
>     2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).
> 
> V2:
>     1.Update commit message(suggested by Jerin Jacob).
>     2.Add ticketlock test cases(suggested by Jerin Jacob).
> 
> V1:
>     Implement ticket lock to improve lock fairness and prdictability.
> 
>     As shown on thundex-2 platform:
>     *** ticketlock_autotest with this patch ***
>         Core [0] count = 496
>         Core [1] count = 495
>         Core [2] count = 498
>         ...
>         Core [209] count = 488
>         Core [210] count = 490
>         Core [211] count = 474
> 
> Joyce Kong (3):
>   eal/ticketlock: ticket based to improve fairness
>   eal/ticketlock: enable generic ticketlock on all arch
>   test/ticketlock: add ticket lock test case
> 
>  MAINTAINERS                                        |   5 +
>  app/test/Makefile                                  |   1 +
>  app/test/autotest_data.py                          |   6 +
>  app/test/meson.build                               |   1 +
>  app/test/test_ticketlock.c                         | 319 +++++++++++++++++++++
>  doc/api/doxy-api-index.md                          |   1 +
>  lib/librte_eal/common/Makefile                     |   2 +-
>  .../common/include/arch/arm/rte_ticketlock.h       |  22 ++
>  .../common/include/arch/ppc_64/rte_ticketlock.h    |  18 ++
>  .../common/include/arch/x86/rte_ticketlock.h       |  18 ++
>  .../common/include/generic/rte_ticketlock.h        | 215 ++++++++++++++
>  lib/librte_eal/common/meson.build                  |   1 +
>  12 files changed, 608 insertions(+), 1 deletion(-)
>  create mode 100644 app/test/test_ticketlock.c
>  create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
>  create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
>  create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
>  create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h
> 
> --

Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

> 2.7.4

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case
  2019-03-27 11:20     ` Ananyev, Konstantin
@ 2019-03-27 11:20       ` Ananyev, Konstantin
  2019-03-28 14:02       ` Thomas Monjalon
  1 sibling, 0 replies; 74+ messages in thread
From: Ananyev, Konstantin @ 2019-03-27 11:20 UTC (permalink / raw)
  To: Joyce Kong, dev
  Cc: nd, stephen, jerin.jacob, thomas, honnappa.nagarahalli, gavin.hu


> 
> v8:
>     1. Enhance functional test for ticketlock by adding extra verification to ensure correct locking in MT env.
>     2. Amortize the cost of getting time in test case.
>     (Suggested by Konstantin Ananyev)
> 
> v7:
>     1. Modify trylock to compare both current and next fields to gurantee the lock
>     being obtained only if tl->current==1(lock is free).
>     As within the trylock function, suppose next==curr=1, then this thread will
>     experience really long context switch, and next time it continues its
>     execution and tl->next wraps around to 1 again and tl_couurent==0(lock
>     held by another thread),this trylock will return a success, that means
>     two threads holding the lock. (Suggested by Konstantin Ananyev)
>     2. Let all archs use generic ticketlock implementation.
> 
> v6:
>     Add rte_ticketlock.h in lib/librte_eal/common/include/arch/arm/.
>     Sort header inside ticketlock files by alphabetical order.
> 
> v5:
>     Change the variants inside rte_ticket_lock from unint to uint16_t for binary
>     compatibility with the plain spin lock(suggested by Honnappa Nagarahalli)).
> 
> v4:
>     Change some assignment operation in recursive ticket lock to __atomic.
> 
> V3:
>     1.Update ticketlock intrduction(suggested by Honnappa Nagarahalli).
>     2.Change the implementation of rte_ticketlock_trylock to CAS(suggested by Honnappa Nagarahalli).
> 
> V2:
>     1.Update commit message(suggested by Jerin Jacob).
>     2.Add ticketlock test cases(suggested by Jerin Jacob).
> 
> V1:
>     Implement ticket lock to improve lock fairness and prdictability.
> 
>     As shown on thundex-2 platform:
>     *** ticketlock_autotest with this patch ***
>         Core [0] count = 496
>         Core [1] count = 495
>         Core [2] count = 498
>         ...
>         Core [209] count = 488
>         Core [210] count = 490
>         Core [211] count = 474
> 
> Joyce Kong (3):
>   eal/ticketlock: ticket based to improve fairness
>   eal/ticketlock: enable generic ticketlock on all arch
>   test/ticketlock: add ticket lock test case
> 
>  MAINTAINERS                                        |   5 +
>  app/test/Makefile                                  |   1 +
>  app/test/autotest_data.py                          |   6 +
>  app/test/meson.build                               |   1 +
>  app/test/test_ticketlock.c                         | 319 +++++++++++++++++++++
>  doc/api/doxy-api-index.md                          |   1 +
>  lib/librte_eal/common/Makefile                     |   2 +-
>  .../common/include/arch/arm/rte_ticketlock.h       |  22 ++
>  .../common/include/arch/ppc_64/rte_ticketlock.h    |  18 ++
>  .../common/include/arch/x86/rte_ticketlock.h       |  18 ++
>  .../common/include/generic/rte_ticketlock.h        | 215 ++++++++++++++
>  lib/librte_eal/common/meson.build                  |   1 +
>  12 files changed, 608 insertions(+), 1 deletion(-)
>  create mode 100644 app/test/test_ticketlock.c
>  create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
>  create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
>  create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
>  create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h
> 
> --

Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

> 2.7.4


^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case
  2019-03-27 11:20     ` Ananyev, Konstantin
  2019-03-27 11:20       ` Ananyev, Konstantin
@ 2019-03-28 14:02       ` Thomas Monjalon
  2019-03-28 14:02         ` Thomas Monjalon
  1 sibling, 1 reply; 74+ messages in thread
From: Thomas Monjalon @ 2019-03-28 14:02 UTC (permalink / raw)
  To: Joyce Kong
  Cc: dev, Ananyev, Konstantin, nd, stephen, jerin.jacob,
	honnappa.nagarahalli, gavin.hu

> > Joyce Kong (3):
> >   eal/ticketlock: ticket based to improve fairness
> >   eal/ticketlock: enable generic ticketlock on all arch
> >   test/ticketlock: add ticket lock test case
> > 
> >  MAINTAINERS                                        |   5 +
> >  app/test/Makefile                                  |   1 +
> >  app/test/autotest_data.py                          |   6 +
> >  app/test/meson.build                               |   1 +
> >  app/test/test_ticketlock.c                         | 319 +++++++++++++++++++++
> >  doc/api/doxy-api-index.md                          |   1 +
> >  lib/librte_eal/common/Makefile                     |   2 +-
> >  .../common/include/arch/arm/rte_ticketlock.h       |  22 ++
> >  .../common/include/arch/ppc_64/rte_ticketlock.h    |  18 ++
> >  .../common/include/arch/x86/rte_ticketlock.h       |  18 ++
> >  .../common/include/generic/rte_ticketlock.h        | 215 ++++++++++++++
> >  lib/librte_eal/common/meson.build                  |   1 +
> >  12 files changed, 608 insertions(+), 1 deletion(-)
> >  create mode 100644 app/test/test_ticketlock.c
> >  create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
> >  create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
> >  create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
> >  create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h
> 
> Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

Applied, thanks

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case
  2019-03-28 14:02       ` Thomas Monjalon
@ 2019-03-28 14:02         ` Thomas Monjalon
  0 siblings, 0 replies; 74+ messages in thread
From: Thomas Monjalon @ 2019-03-28 14:02 UTC (permalink / raw)
  To: Joyce Kong
  Cc: dev, Ananyev, Konstantin, nd, stephen, jerin.jacob,
	honnappa.nagarahalli, gavin.hu

> > Joyce Kong (3):
> >   eal/ticketlock: ticket based to improve fairness
> >   eal/ticketlock: enable generic ticketlock on all arch
> >   test/ticketlock: add ticket lock test case
> > 
> >  MAINTAINERS                                        |   5 +
> >  app/test/Makefile                                  |   1 +
> >  app/test/autotest_data.py                          |   6 +
> >  app/test/meson.build                               |   1 +
> >  app/test/test_ticketlock.c                         | 319 +++++++++++++++++++++
> >  doc/api/doxy-api-index.md                          |   1 +
> >  lib/librte_eal/common/Makefile                     |   2 +-
> >  .../common/include/arch/arm/rte_ticketlock.h       |  22 ++
> >  .../common/include/arch/ppc_64/rte_ticketlock.h    |  18 ++
> >  .../common/include/arch/x86/rte_ticketlock.h       |  18 ++
> >  .../common/include/generic/rte_ticketlock.h        | 215 ++++++++++++++
> >  lib/librte_eal/common/meson.build                  |   1 +
> >  12 files changed, 608 insertions(+), 1 deletion(-)
> >  create mode 100644 app/test/test_ticketlock.c
> >  create mode 100644 lib/librte_eal/common/include/arch/arm/rte_ticketlock.h
> >  create mode 100644 lib/librte_eal/common/include/arch/ppc_64/rte_ticketlock.h
> >  create mode 100644 lib/librte_eal/common/include/arch/x86/rte_ticketlock.h
> >  create mode 100644 lib/librte_eal/common/include/generic/rte_ticketlock.h
> 
> Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>

Applied, thanks



^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case
  2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case Joyce Kong
  2019-03-25 11:11     ` Joyce Kong
@ 2019-04-08 20:18     ` David Marchand
  2019-04-08 20:18       ` David Marchand
  2019-04-14 20:37       ` Thomas Monjalon
  1 sibling, 2 replies; 74+ messages in thread
From: David Marchand @ 2019-04-08 20:18 UTC (permalink / raw)
  To: Joyce Kong
  Cc: dev, nd, Stephen Hemminger, Jerin Jacob, Ananyev, Konstantin,
	Honnappa Nagarahalli, Gavin Hu, Thomas Monjalon

On Mon, Mar 25, 2019 at 12:12 PM Joyce Kong <joyce.kong@arm.com> wrote:

> diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
> index 5f87bb9..db25274 100644
> --- a/app/test/autotest_data.py
> +++ b/app/test/autotest_data.py
> @@ -171,6 +171,12 @@
>          "Report":  None,
>      },
>      {
> +        "Name":    "Ticketlock autotest",
> +        "Command": "ticketlock_autotest",
> +        "Func":    ticketlock_autotest,
> +        "Report":  None,
> +    }
> +    {
>          "Name":    "Byte order autotest",
>          "Command": "byteorder_autotest",
>          "Func":    default_autotest,
>

Please, can you fix this ?

We are missing a , and ticketlock_autotest has no implementation in python
afaics.


-- 
David Marchand

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case
  2019-04-08 20:18     ` David Marchand
@ 2019-04-08 20:18       ` David Marchand
  2019-04-14 20:37       ` Thomas Monjalon
  1 sibling, 0 replies; 74+ messages in thread
From: David Marchand @ 2019-04-08 20:18 UTC (permalink / raw)
  To: Joyce Kong
  Cc: dev, nd, Stephen Hemminger, Jerin Jacob, Ananyev, Konstantin,
	Honnappa Nagarahalli, Gavin Hu, Thomas Monjalon

On Mon, Mar 25, 2019 at 12:12 PM Joyce Kong <joyce.kong@arm.com> wrote:

> diff --git a/app/test/autotest_data.py b/app/test/autotest_data.py
> index 5f87bb9..db25274 100644
> --- a/app/test/autotest_data.py
> +++ b/app/test/autotest_data.py
> @@ -171,6 +171,12 @@
>          "Report":  None,
>      },
>      {
> +        "Name":    "Ticketlock autotest",
> +        "Command": "ticketlock_autotest",
> +        "Func":    ticketlock_autotest,
> +        "Report":  None,
> +    }
> +    {
>          "Name":    "Byte order autotest",
>          "Command": "byteorder_autotest",
>          "Func":    default_autotest,
>

Please, can you fix this ?

We are missing a , and ticketlock_autotest has no implementation in python
afaics.


-- 
David Marchand

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case
  2019-04-08 20:18     ` David Marchand
  2019-04-08 20:18       ` David Marchand
@ 2019-04-14 20:37       ` Thomas Monjalon
  2019-04-14 20:37         ` Thomas Monjalon
  2019-04-15  9:07         ` Joyce Kong (Arm Technology China)
  1 sibling, 2 replies; 74+ messages in thread
From: Thomas Monjalon @ 2019-04-14 20:37 UTC (permalink / raw)
  To: Joyce Kong
  Cc: dev, David Marchand, nd, Stephen Hemminger, Jerin Jacob, Ananyev,
	Konstantin, Honnappa Nagarahalli, Gavin Hu

08/04/2019 22:18, David Marchand:
> On Mon, Mar 25, 2019 at 12:12 PM Joyce Kong <joyce.kong@arm.com> wrote:
> > --- a/app/test/autotest_data.py
> > +++ b/app/test/autotest_data.py
> > @@ -171,6 +171,12 @@
> >          "Report":  None,
> >      },
> >      {
> > +        "Name":    "Ticketlock autotest",
> > +        "Command": "ticketlock_autotest",
> > +        "Func":    ticketlock_autotest,
> > +        "Report":  None,
> > +    }
> > +    {
> >          "Name":    "Byte order autotest",
> >          "Command": "byteorder_autotest",
> >          "Func":    default_autotest,
> >
> 
> Please, can you fix this ?
> 
> We are missing a , and ticketlock_autotest has no implementation in python
> afaics.

Please, send a fix ASAP.

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case
  2019-04-14 20:37       ` Thomas Monjalon
@ 2019-04-14 20:37         ` Thomas Monjalon
  2019-04-15  9:07         ` Joyce Kong (Arm Technology China)
  1 sibling, 0 replies; 74+ messages in thread
From: Thomas Monjalon @ 2019-04-14 20:37 UTC (permalink / raw)
  To: Joyce Kong
  Cc: dev, David Marchand, nd, Stephen Hemminger, Jerin Jacob, Ananyev,
	Konstantin, Honnappa Nagarahalli, Gavin Hu

08/04/2019 22:18, David Marchand:
> On Mon, Mar 25, 2019 at 12:12 PM Joyce Kong <joyce.kong@arm.com> wrote:
> > --- a/app/test/autotest_data.py
> > +++ b/app/test/autotest_data.py
> > @@ -171,6 +171,12 @@
> >          "Report":  None,
> >      },
> >      {
> > +        "Name":    "Ticketlock autotest",
> > +        "Command": "ticketlock_autotest",
> > +        "Func":    ticketlock_autotest,
> > +        "Report":  None,
> > +    }
> > +    {
> >          "Name":    "Byte order autotest",
> >          "Command": "byteorder_autotest",
> >          "Func":    default_autotest,
> >
> 
> Please, can you fix this ?
> 
> We are missing a , and ticketlock_autotest has no implementation in python
> afaics.

Please, send a fix ASAP.



^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case
  2019-04-14 20:37       ` Thomas Monjalon
  2019-04-14 20:37         ` Thomas Monjalon
@ 2019-04-15  9:07         ` Joyce Kong (Arm Technology China)
  2019-04-15  9:07           ` Joyce Kong (Arm Technology China)
  1 sibling, 1 reply; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-04-15  9:07 UTC (permalink / raw)
  To: thomas
  Cc: dev, David Marchand, nd, Stephen Hemminger, Jerin Jacob, Ananyev,
	Konstantin, Honnappa Nagarahalli, Gavin Hu (Arm Technology China)

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Monday, April 15, 2019 4:38 AM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>
> Cc: dev@dpdk.org; David Marchand <david.marchand@redhat.com>; nd
> <nd@arm.com>; Stephen Hemminger <stephen@networkplumber.org>;
> Jerin Jacob <jerin.jacob@caviumnetworks.com>; Ananyev, Konstantin
> <konstantin.ananyev@intel.com>; Honnappa Nagarahalli
> <Honnappa.Nagarahalli@arm.com>; Gavin Hu (Arm Technology China)
> <Gavin.Hu@arm.com>
> Subject: Re: [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test
> case
> 
> 08/04/2019 22:18, David Marchand:
> > On Mon, Mar 25, 2019 at 12:12 PM Joyce Kong <joyce.kong@arm.com>
> wrote:
> > > --- a/app/test/autotest_data.py
> > > +++ b/app/test/autotest_data.py
> > > @@ -171,6 +171,12 @@
> > >          "Report":  None,
> > >      },
> > >      {
> > > +        "Name":    "Ticketlock autotest",
> > > +        "Command": "ticketlock_autotest",
> > > +        "Func":    ticketlock_autotest,
> > > +        "Report":  None,
> > > +    }
> > > +    {
> > >          "Name":    "Byte order autotest",
> > >          "Command": "byteorder_autotest",
> > >          "Func":    default_autotest,
> > >
> >
> > Please, can you fix this ?
> >
> > We are missing a , and ticketlock_autotest has no implementation in
> > python afaics.
> 
> Please, send a fix ASAP.

Fix in the patch test/ticketlock: implement ticketlock autotest .

^ permalink raw reply	[flat|nested] 74+ messages in thread

* Re: [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case
  2019-04-15  9:07         ` Joyce Kong (Arm Technology China)
@ 2019-04-15  9:07           ` Joyce Kong (Arm Technology China)
  0 siblings, 0 replies; 74+ messages in thread
From: Joyce Kong (Arm Technology China) @ 2019-04-15  9:07 UTC (permalink / raw)
  To: thomas
  Cc: dev, David Marchand, nd, Stephen Hemminger, Jerin Jacob, Ananyev,
	Konstantin, Honnappa Nagarahalli, Gavin Hu (Arm Technology China)

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Monday, April 15, 2019 4:38 AM
> To: Joyce Kong (Arm Technology China) <Joyce.Kong@arm.com>
> Cc: dev@dpdk.org; David Marchand <david.marchand@redhat.com>; nd
> <nd@arm.com>; Stephen Hemminger <stephen@networkplumber.org>;
> Jerin Jacob <jerin.jacob@caviumnetworks.com>; Ananyev, Konstantin
> <konstantin.ananyev@intel.com>; Honnappa Nagarahalli
> <Honnappa.Nagarahalli@arm.com>; Gavin Hu (Arm Technology China)
> <Gavin.Hu@arm.com>
> Subject: Re: [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test
> case
> 
> 08/04/2019 22:18, David Marchand:
> > On Mon, Mar 25, 2019 at 12:12 PM Joyce Kong <joyce.kong@arm.com>
> wrote:
> > > --- a/app/test/autotest_data.py
> > > +++ b/app/test/autotest_data.py
> > > @@ -171,6 +171,12 @@
> > >          "Report":  None,
> > >      },
> > >      {
> > > +        "Name":    "Ticketlock autotest",
> > > +        "Command": "ticketlock_autotest",
> > > +        "Func":    ticketlock_autotest,
> > > +        "Report":  None,
> > > +    }
> > > +    {
> > >          "Name":    "Byte order autotest",
> > >          "Command": "byteorder_autotest",
> > >          "Func":    default_autotest,
> > >
> >
> > Please, can you fix this ?
> >
> > We are missing a , and ticketlock_autotest has no implementation in
> > python afaics.
> 
> Please, send a fix ASAP.

Fix in the patch test/ticketlock: implement ticketlock autotest .

^ permalink raw reply	[flat|nested] 74+ messages in thread

end of thread, other threads:[~2019-04-15  9:11 UTC | newest]

Thread overview: 74+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-13 14:46 [dpdk-dev] [PATCH v1] ticketlock: ticket based to improve fairness Gavin Hu
2019-01-14  7:59 ` [dpdk-dev] [EXT] " Jerin Jacob Kollanukkaran
2019-01-14 16:57   ` Gavin Hu (Arm Technology China)
2019-01-14 23:36 ` [dpdk-dev] " Honnappa Nagarahalli
2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 1/2] " Joyce Kong
2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 0/2] ticketlock: implement ticketlock and add test case Joyce Kong
2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 " Joyce Kong
2019-03-11  5:52       ` [dpdk-dev] [PATCH v5 " Joyce Kong
2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 1/2] ticketlock: ticket based to improve fairness Joyce Kong
2019-03-11  5:52       ` [dpdk-dev] [PATCH v5 1/2] eal/ticketlock: " Joyce Kong
2019-03-13  9:41         ` Jerin Jacob Kollanukkaran
2019-03-15  6:57           ` Joyce Kong (Arm Technology China)
2019-03-15  6:57             ` Joyce Kong (Arm Technology China)
2019-03-13 15:36         ` Jerin Jacob Kollanukkaran
2019-03-15  6:58           ` Joyce Kong (Arm Technology China)
2019-03-15  6:58             ` Joyce Kong (Arm Technology China)
2019-02-19 10:48     ` [dpdk-dev] [PATCH v4 2/2] test/ticketlock: add ticket lock test case Joyce Kong
2019-03-11  5:52       ` [dpdk-dev] [PATCH v5 " Joyce Kong
2019-03-13 13:31         ` Jerin Jacob Kollanukkaran
2019-03-15  6:57           ` Joyce Kong (Arm Technology China)
2019-03-15  6:57             ` Joyce Kong (Arm Technology China)
2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 1/2] ticketlock: ticket based to improve fairness Joyce Kong
2019-01-25  8:37   ` [dpdk-dev] [PATCH v3 2/2] test/ticketlock: add ticket lock test case Joyce Kong
2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 0/2] ticketlock: implement ticketlock and add " Joyce Kong
2019-03-15  6:56     ` Joyce Kong
2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 1/2] eal/ticketlock: ticket based to improve fairness Joyce Kong
2019-03-15  6:56     ` Joyce Kong
2019-03-15 12:55     ` Ananyev, Konstantin
2019-03-15 12:55       ` Ananyev, Konstantin
2019-03-19  9:44       ` Gavin Hu (Arm Technology China)
2019-03-19  9:44         ` Gavin Hu (Arm Technology China)
2019-03-19 10:15         ` Ananyev, Konstantin
2019-03-19 10:15           ` Ananyev, Konstantin
2019-03-20  5:11           ` Gavin Hu (Arm Technology China)
2019-03-20  5:11             ` Gavin Hu (Arm Technology China)
2019-03-20  9:47             ` Ananyev, Konstantin
2019-03-20  9:47               ` Ananyev, Konstantin
2019-03-22  2:04               ` Gavin Hu (Arm Technology China)
2019-03-22  2:04                 ` Gavin Hu (Arm Technology China)
2019-03-15  6:56   ` [dpdk-dev] [PATCH v6 2/2] test/ticketlock: add ticket lock test case Joyce Kong
2019-03-15  6:56     ` Joyce Kong
2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 0/3] ticketlock: implement ticketlock and add " Joyce Kong
2019-03-21  9:13     ` Joyce Kong
2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 2/3] eal/ticketlock: enable generic ticketlock on all arch Joyce Kong
2019-03-21  9:13     ` Joyce Kong
2019-03-21  9:13   ` [dpdk-dev] [PATCH v7 3/3] test/ticketlock: add ticket lock test case Joyce Kong
2019-03-21  9:13     ` Joyce Kong
2019-03-22 11:38     ` Ananyev, Konstantin
2019-03-22 11:38       ` Ananyev, Konstantin
2019-03-25 10:25       ` Joyce Kong (Arm Technology China)
2019-03-25 10:25         ` Joyce Kong (Arm Technology China)
2019-03-21  9:15   ` [dpdk-dev] [PATCH v7 1/3] eal/ticketlock: ticket based to improve fairness Joyce Kong
2019-03-21  9:15     ` Joyce Kong
2019-03-22 10:56     ` Ananyev, Konstantin
2019-03-22 10:56       ` Ananyev, Konstantin
2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 0/3] ticketlock: implement ticketlock and add test case Joyce Kong
2019-03-25 11:11     ` Joyce Kong
2019-03-27 11:20     ` Ananyev, Konstantin
2019-03-27 11:20       ` Ananyev, Konstantin
2019-03-28 14:02       ` Thomas Monjalon
2019-03-28 14:02         ` Thomas Monjalon
2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 1/3] eal/ticketlock: ticket based to improve fairness Joyce Kong
2019-03-25 11:11     ` Joyce Kong
2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 2/3] eal/ticketlock: enable generic ticketlock on all arch Joyce Kong
2019-03-25 11:11     ` Joyce Kong
2019-03-25 11:11   ` [dpdk-dev] [PATCH v8 3/3] test/ticketlock: add ticket lock test case Joyce Kong
2019-03-25 11:11     ` Joyce Kong
2019-04-08 20:18     ` David Marchand
2019-04-08 20:18       ` David Marchand
2019-04-14 20:37       ` Thomas Monjalon
2019-04-14 20:37         ` Thomas Monjalon
2019-04-15  9:07         ` Joyce Kong (Arm Technology China)
2019-04-15  9:07           ` Joyce Kong (Arm Technology China)
2019-01-18  9:15 ` [dpdk-dev] [PATCH v2 2/2] " Joyce Kong

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).