Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 9 Mar 2017 18:33:40 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r314970 - in head/sys: compat/linuxkpi/common/include/linux compat/linuxkpi/common/src conf modules/linuxkpi
Message-ID:  <201703091833.v29IXedF092439@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Thu Mar  9 18:33:40 2017
New Revision: 314970
URL: https://svnweb.freebsd.org/changeset/base/314970

Log:
  Implement support for mutexes with deadlock avoidance in the LinuxKPI.
  
  When locking a mutex and deadlock is detected the first mutex lock
  call that sees the deadlock will return -EDEADLK .
  
  MFC after:		1 week
  Sponsored by:		Mellanox Technologies

Added:
  head/sys/compat/linuxkpi/common/include/linux/ww_mutex.h   (contents, props changed)
  head/sys/compat/linuxkpi/common/src/linux_lock.c   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/modules/linuxkpi/Makefile

Added: head/sys/compat/linuxkpi/common/include/linux/ww_mutex.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/compat/linuxkpi/common/include/linux/ww_mutex.h	Thu Mar  9 18:33:40 2017	(r314970)
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2017 Mellanox Technologies, Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef	_LINUX_WW_MUTEX_H_
+#define	_LINUX_WW_MUTEX_H_
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+
+#include <linux/mutex.h>
+
+struct ww_class {
+	const char *mutex_name;
+};
+
+struct ww_acquire_ctx {
+};
+
+struct ww_mutex {
+	struct mutex base;
+	struct cv condvar;
+};
+
+#define	DEFINE_WW_CLASS(name)					\
+	struct ww_class name = {				\
+		.mutex_name = mutex_name(#name "_mutex")	\
+	}
+
+#define	DEFINE_WW_MUTEX(name, ww_class)					\
+	struct ww_mutex name;						\
+	static void name##_init(void *arg)				\
+	{								\
+		ww_mutex_init(&name, &ww_class);			\
+	}								\
+	SYSINIT(name, SI_SUB_LOCK, SI_ORDER_SECOND, name##_init, NULL)
+
+#define	ww_mutex_is_locked(_m) \
+	sx_xlocked(&(_m)->base.sx)
+
+#define	ww_mutex_lock_slow(_m, _x) \
+	ww_mutex_lock(_m, _x)
+
+#define	ww_mutex_lock_slow_interruptible(_m, _x) \
+	ww_mutex_lock_interruptible(_m, _x)
+
+static inline int __must_check
+ww_mutex_trylock(struct ww_mutex *lock)
+{
+	return (mutex_trylock(&lock->base));
+}
+
+extern int linux_ww_mutex_lock_sub(struct ww_mutex *, int catch_signal);
+
+static inline int
+ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+{
+	if (MUTEX_SKIP())
+		return (0);
+	else if ((struct thread *)SX_OWNER(lock->base.sx.sx_lock) == curthread)
+		return (-EALREADY);
+	else
+		return (linux_ww_mutex_lock_sub(lock, 0));
+}
+
+static inline int
+ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+{
+	if (MUTEX_SKIP())
+		return (0);
+	else if ((struct thread *)SX_OWNER(lock->base.sx.sx_lock) == curthread)
+		return (-EALREADY);
+	else
+		return (linux_ww_mutex_lock_sub(lock, 1));
+}
+
+extern void linux_ww_mutex_unlock_sub(struct ww_mutex *);
+
+static inline void
+ww_mutex_unlock(struct ww_mutex *lock)
+{
+	if (MUTEX_SKIP())
+		return;
+	else
+		linux_ww_mutex_unlock_sub(lock);
+}
+
+static inline void
+ww_mutex_destroy(struct ww_mutex *lock)
+{
+	cv_destroy(&lock->condvar);
+	mutex_destroy(&lock->base);
+}
+
+static inline void
+ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *ww_class)
+{
+}
+
+static inline void
+ww_mutex_init(struct ww_mutex *lock, struct ww_class *ww_class)
+{
+	linux_mutex_init(&lock->base, ww_class->mutex_name, SX_NOWITNESS);
+	cv_init(&lock->condvar, "lkpi-ww");
+}
+
+static inline void
+ww_acquire_fini(struct ww_acquire_ctx *ctx)
+{
+}
+
+static inline void
+ww_acquire_done(struct ww_acquire_ctx *ctx)
+{
+}
+
+#endif					/* _LINUX_WW_MUTEX_H_ */

Added: head/sys/compat/linuxkpi/common/src/linux_lock.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/compat/linuxkpi/common/src/linux_lock.c	Thu Mar  9 18:33:40 2017	(r314970)
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2017 Mellanox Technologies, Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+
+#include <linux/ww_mutex.h>
+
+struct ww_mutex_thread {
+	TAILQ_ENTRY(ww_mutex_thread) entry;
+	struct thread *thread;
+	struct ww_mutex *lock;
+};
+
+static TAILQ_HEAD(, ww_mutex_thread) ww_mutex_head;
+static struct mtx ww_mutex_global;
+
+static void
+linux_ww_init(void *arg)
+{
+	TAILQ_INIT(&ww_mutex_head);
+	mtx_init(&ww_mutex_global, "lkpi-ww-mtx", NULL, MTX_DEF);
+}
+
+SYSINIT(ww_init, SI_SUB_LOCK, SI_ORDER_SECOND, linux_ww_init, NULL);
+
+static void
+linux_ww_uninit(void *arg)
+{
+	mtx_destroy(&ww_mutex_global);
+}
+
+SYSUNINIT(ww_uninit, SI_SUB_LOCK, SI_ORDER_SECOND, linux_ww_uninit, NULL);
+
+static inline void
+linux_ww_lock(void)
+{
+	mtx_lock(&ww_mutex_global);
+}
+
+static inline void
+linux_ww_unlock(void)
+{
+	mtx_unlock(&ww_mutex_global);
+}
+
+/* lock a mutex with deadlock avoidance */
+int
+linux_ww_mutex_lock_sub(struct ww_mutex *lock, int catch_signal)
+{
+	struct ww_mutex_thread entry;
+	struct ww_mutex_thread *other;
+	int retval = 0;
+
+	linux_ww_lock();
+	if (unlikely(sx_try_xlock(&lock->base.sx) == 0)) {
+		entry.thread = curthread;
+		entry.lock = lock;
+		TAILQ_INSERT_TAIL(&ww_mutex_head, &entry, entry);
+
+		do {
+			struct thread *owner = (struct thread *)
+			    SX_OWNER(lock->base.sx.sx_lock);
+
+			/* scan for deadlock */
+			TAILQ_FOREACH(other, &ww_mutex_head, entry) {
+				/* skip own thread */
+				if (other == &entry)
+					continue;
+				/*
+				 * If another thread is owning our
+				 * lock and is at the same time trying
+				 * to acquire a lock this thread owns,
+				 * that means deadlock.
+				 */
+				if (other->thread == owner &&
+				    (struct thread *)SX_OWNER(
+				    other->lock->base.sx.sx_lock) == curthread) {
+					retval = -EDEADLK;
+					goto done;
+				}
+			}
+			if (catch_signal) {
+				if (cv_wait_sig(&lock->condvar, &ww_mutex_global) != 0) {
+					retval = -EINTR;
+					goto done;
+				}
+			} else {
+				cv_wait(&lock->condvar, &ww_mutex_global);
+			}
+		} while (sx_try_xlock(&lock->base.sx) == 0);
+done:
+		TAILQ_REMOVE(&ww_mutex_head, &entry, entry);
+
+		/* if the lock is free, wakeup next lock waiter, if any */
+		if ((struct thread *)SX_OWNER(lock->base.sx.sx_lock) == NULL)
+			cv_signal(&lock->condvar);
+	}
+	linux_ww_unlock();
+	return (retval);
+}
+
+void
+linux_ww_mutex_unlock_sub(struct ww_mutex *lock)
+{
+	/* protect ww_mutex ownership change */
+	linux_ww_lock();
+	sx_xunlock(&lock->base.sx);
+	/* wakeup a lock waiter, if any */
+	cv_signal(&lock->condvar);
+	linux_ww_unlock();
+}

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Thu Mar  9 18:06:48 2017	(r314969)
+++ head/sys/conf/files	Thu Mar  9 18:33:40 2017	(r314970)
@@ -4284,6 +4284,8 @@ compat/linuxkpi/common/src/linux_current
 	compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/linux_kthread.c	optional compat_linuxkpi \
 	compile-with "${LINUXKPI_C}"
+compat/linuxkpi/common/src/linux_lock.c		optional compat_linuxkpi \
+	compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/linux_page.c		optional compat_linuxkpi \
 	compile-with "${LINUXKPI_C}"
 compat/linuxkpi/common/src/linux_pci.c		optional compat_linuxkpi pci \

Modified: head/sys/modules/linuxkpi/Makefile
==============================================================================
--- head/sys/modules/linuxkpi/Makefile	Thu Mar  9 18:06:48 2017	(r314969)
+++ head/sys/modules/linuxkpi/Makefile	Thu Mar  9 18:33:40 2017	(r314970)
@@ -6,6 +6,7 @@ SRCS=	linux_kmod.c \
 	linux_compat.c \
 	linux_current.c \
 	linux_kthread.c \
+	linux_lock.c \
 	linux_page.c \
 	linux_pci.c \
 	linux_radix.c \



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201703091833.v29IXedF092439>