=== Analysis of Abort rate ===
Linux Perf shows PMU counters for transactional memory as below.
$ perf record -e tx-start,tx-abort,tx-commit,tx-conflict <program>
The results from 'perf report' of the three TM experiments show:
2% 5% 10%
tx-start 10K 14K 16K (count start transaction)
tx-abort 7K 13K 14K (count all aborts)
tx-commit 10K 14K 16K (count commit of transactions)
tx-conflict 7K 13K 14K (count aborts due to conflict with other CPUs)
Note:
While test-cmap benchmark works fine, the patch fails a few cases
when doing "make check". Failed cases:
717 ofproto - del group (OpenFlow 1.5), and the following 3
816 ofproto-dpif - active-backup bonding, and 817, 818
939 ofproto-dpif megaflow - normal, active-backup bonding, and 940, 955
Signed-off-by: William Tu <u9012063@gmail.com>
---
configure.ac | 2 +
lib/ovs-thread.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
m4/openvswitch.m4 | 21 +++++++++++
3 files changed, 130 insertions(+)
@@ -113,6 +113,8 @@ OVS_CHECK_PKIDIR
OVS_CHECK_RUNDIR
OVS_CHECK_DBDIR
OVS_CHECK_BACKTRACE
+OVS_CHECK_RTM
+OVS_CHECK_GLIBC_TSX
OVS_CHECK_PERF_EVENT
OVS_CHECK_VALGRIND
OVS_CHECK_SOCKET_LIBS
@@ -52,6 +52,110 @@ static const char *must_not_fork;
/* True if we created any threads beyond the main initial thread. */
static bool multithreaded;
+#if defined(HAVE_RTM) && !defined(HAVE_GLIBC_TSX)
+/* Intel Transactional Memory (TSX) support */
+#define _XBEGIN_STARTED (~0u)
+#define _ABORT_EXPLICIT (1 << 0)
+#define _ABORT_RETRY (1 << 1)
+#define _ABORT_CONFLICT (1 << 2)
+#define _ABORT_CAPACITY (1 << 3)
+#define _ABORT_DEBUG (1 << 4)
+#define _ABORT_NESTED (1 << 5)
+#define _XABORT_CODE(x) (((x) >> 24) & 0xff)
+#define _ABORT_LOCK_BUSY 0xff
+
+#define MAX_RETRY_XBEGIN 10
+#define MAX_ABORT_RETRY 5
+
+#define __force_inline __attribute__((__always_inline__)) inline
+
+/* See: Intel 64 and IA-32 Architectures Software Developer's Manual
+ * Instruction Set Reference. */
+
+static __force_inline int _xbegin(void)
+{
+ int ret = _XBEGIN_STARTED;
+ __asm__ volatile (".byte 0xc7,0xf8 ; .long 0" : "+a" (ret) :: "memory");
+ return ret;
+}
+
+static __force_inline void _xend(void)
+{
+ __asm__ volatile (".byte 0x0f,0x01,0xd5" ::: "memory");
+}
+
+static __force_inline void _xabort(const unsigned int status)
+{
+ __asm__ volatile (".byte 0xc6,0xf8,%P0" :: "i" (status) : "memory");
+}
+
+static __force_inline int _xtest(void)
+{
+ unsigned char out;
+ /* return 1 if RTM_ACTIVE = 1 */
+ __asm__ volatile (".byte 0x0f,0x01,0xd6 ; setnz %0" : "=r" (out) :: "memory");
+ return out;
+}
+
+#define OVS_LOCK_ELISION(mutex) \
+{\
+ unsigned status; \
+ int i; \
+ int abort_retry = 0; \
+ for(i = 0; i < MAX_RETRY_XBEGIN; i++) { \
+ if ((status = _xbegin()) == _XBEGIN_STARTED) { \
+ if ((mutex)->__data.__lock == 0) {\
+ return; \
+ }\
+ _xabort(_ABORT_LOCK_BUSY);\
+ } \
+ if (!(status & _ABORT_RETRY)) { \
+ if (abort_retry >= MAX_ABORT_RETRY) {\
+ break; \
+ }\
+ abort_retry++; \
+ } \
+ } \
+}
+
+/* Same as OVS_LOCK_ELISION, but has return value */
+#define OVS_TRYLOCK_ELISION(mutex) \
+{\
+ unsigned status; \
+ int i; \
+ int abort_retry = 0; \
+ for(i = 0; i < MAX_RETRY_XBEGIN; i++) { \
+ if ((status = _xbegin()) == _XBEGIN_STARTED) { \
+ if ((mutex)->__data.__lock == 0) {\
+ return 0; \
+ }\
+ _xabort(_ABORT_LOCK_BUSY);\
+ } \
+ if (!(status & _ABORT_RETRY)) { \
+ if (abort_retry >= MAX_ABORT_RETRY) {\
+ break; \
+ }\
+ abort_retry++; \
+ } \
+ } \
+}
+
+#define OVS_UNLOCK_ELISION(mutex) \
+{\
+ if (((mutex)->__data.__lock == 0)) { \
+ if (_xtest() == 1) {\
+ _xend();\
+ }\
+ return;\
+ }\
+}
+
+#else /* HAVE_RTM disable below */
+#define OVS_LOCK_ELISION(mutex)
+#define OVS_TRYLOCK_ELISION(mutex)
+#define OVS_UNLOCK_ELISION(mutex)
+#endif
+
#define LOCK_FUNCTION(TYPE, FUN) \
void \
ovs_##TYPE##_##FUN##_at(const struct ovs_##TYPE *l_, \
@@ -60,6 +164,7 @@ static bool multithreaded;
{ \
struct ovs_##TYPE *l = CONST_CAST(struct ovs_##TYPE *, l_); \
int error; \
+ OVS_LOCK_ELISION(&l->lock); \
\
/* Verify that 'l' was initialized. */ \
if (OVS_UNLIKELY(!l->where)) { \
@@ -85,6 +190,7 @@ LOCK_FUNCTION(rwlock, wrlock);
{ \
struct ovs_##TYPE *l = CONST_CAST(struct ovs_##TYPE *, l_); \
int error; \
+ OVS_TRYLOCK_ELISION(&l->lock); \
\
/* Verify that 'l' was initialized. */ \
if (OVS_UNLIKELY(!l->where)) { \
@@ -112,6 +218,7 @@ TRY_LOCK_FUNCTION(rwlock, trywrlock);
{ \
struct ovs_##TYPE *l = CONST_CAST(struct ovs_##TYPE *, l_); \
int error; \
+ OVS_UNLOCK_ELISION(&l->lock); \
\
/* Verify that 'l' was initialized. */ \
ovs_assert(l->where); \
@@ -309,6 +309,27 @@ AC_DEFUN([OVS_CHECK_BACKTRACE],
[AC_DEFINE([HAVE_BACKTRACE], [1],
[Define to 1 if you have backtrace(3).])])])
+dnl Defines HAVE_RTM if CPU supports TSX
+AC_DEFUN([OVS_CHECK_RTM],
+ [ if (test -e /proc/cpuinfo && cat /proc/cpuinfo | grep rtm > /dev/null); then
+ AC_DEFINE([HAVE_RTM], [1], [Define to 1 if CPU has Transactional Memory.])
+ fi])
+
+dnl Defines HAVE_GLIBC_TSX if glibc supports TSX
+AC_DEFUN([OVS_CHECK_GLIBC_TSX],
+ [AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([],
+ [[
+ #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 21
+ return 0;
+ #else
+ return 1;
+ #endif
+ ]])],
+ [AC_DEFINE([HAVE_GLIBC_TSX], [1], [Defines HAVE_GLIBC_TSX if glibc supports TSX])],
+ [], [])
+ ])
+
dnl Defines HAVE_PERF_EVENT if linux/perf_event.h is found.
AC_DEFUN([OVS_CHECK_PERF_EVENT],
[AC_CHECK_HEADERS([linux/perf_event.h])])