* [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 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 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 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 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
* 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 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-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
* 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
* [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 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 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 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
* [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 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
* 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
* 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
* [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
* [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
* 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 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 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
* [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
* 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
* [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 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