initialize a mutex into locked state?

From: Oleg Drokin
Date: Wed Jun 15 2016 - 14:24:01 EST


Hello!

To my surprise I found out that it's not possible to initialise a mutex into
a locked state.
I discussed it with Arjan and apparently there's no fundamental reason
not to allow this.

A typical use would be when you have a structure that you need to init and then
add to some list (under a spinlock), but with a possibility of a racing thread
adding something there that you might want to reuse and not init your own copy
of it to save cpu cycles. Yet you still want this structure to remain
internally locked for other purposes before letting other threads do stuff
with it (hence the mutex initialized to a locked state).

There's just a case in nfsd that I met that would benefit from it that
causes me to move mutex init + lock into the general "do always" area
instead of doing it only when really necessary along with other init code
(see the patch at http://marc.info/?l=linux-nfs&m=146596158830108&w=2 if
interested).

So I wonder if such a functionality should be considered?

Something like this (only compile-tested):

[PATCH] mutex: Add mutex_init_locked()

This is useful when you want to init it to this state under a spinlock.

Signed-off-by: Oleg Drokin <green@xxxxxxxxxxxxxx>
---
drivers/base/bus.c | 2 +-
drivers/base/class.c | 2 +-
drivers/gpu/drm/nouveau/nvkm/core/subdev.c | 2 +-
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c | 2 +-
include/linux/mutex-debug.h | 11 ++++++++++-
include/linux/mutex.h | 22 +++++++++++++++++++---
include/linux/ww_mutex.h | 2 +-
kernel/locking/mutex.c | 10 +++++++---
net/netfilter/ipvs/ip_vs_sync.c | 2 +-
9 files changed, 42 insertions(+), 13 deletions(-)

diff --git a/include/linux/mutex-debug.h b/include/linux/mutex-debug.h
index 4ac8b19..ee54a3d 100644
--- a/include/linux/mutex-debug.h
+++ b/include/linux/mutex-debug.h
@@ -16,9 +16,18 @@
do { \
static struct lock_class_key __key; \
\
- __mutex_init((mutex), #mutex, &__key); \
+ __mutex_init((mutex), #mutex, &__key, 0); \
} while (0)

+#define mutex_init_locked(mutex) \
+do { \
+ static struct lock_class_key __key; \
+ \
+ __mutex_init((mutex), #mutex, &__key, 1); \
+} while (0)
+
+
+
extern void mutex_destroy(struct mutex *lock);

#endif
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index 2cb7531..43faca4 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -88,14 +88,30 @@ struct mutex_waiter {
*
* Initialize the mutex to unlocked state.
*
- * It is not allowed to initialize an already locked mutex.
+ * It is not allowed to re-initialize an already locked mutex.
*/
# define mutex_init(mutex) \
do { \
static struct lock_class_key __key; \
\
- __mutex_init((mutex), #mutex, &__key); \
+ __mutex_init((mutex), #mutex, &__key, 0); \
} while (0)
+
+/**
+ * mutex_init_locked - initialize the mutex
+ * @mutex: the mutex to be initialized
+ *
+ * Initialize the mutex to locked state.
+ *
+ * It is not allowed to re-initialize an already locked mutex.
+ */
+# define mutex_init_locked(mutex) \
+do { \
+ static struct lock_class_key __key; \
+ \
+ __mutex_init((mutex), #mutex, &__key, 1); \
+} while (0)
+
static inline void mutex_destroy(struct mutex *lock) {}
#endif

@@ -117,7 +133,7 @@ static inline void mutex_destroy(struct mutex *lock) {}
struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)

extern void __mutex_init(struct mutex *lock, const char *name,
- struct lock_class_key *key);
+ struct lock_class_key *key, int locked);

/**
* mutex_is_locked - is the mutex locked
diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c
index e364b42..a9cc497 100644
--- a/kernel/locking/mutex.c
+++ b/kernel/locking/mutex.c
@@ -47,12 +47,16 @@
#endif

void
-__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
+__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key,
+ int locked)
{
- atomic_set(&lock->count, 1);
+ atomic_set(&lock->count, !lock);
spin_lock_init(&lock->wait_lock);
INIT_LIST_HEAD(&lock->wait_list);
- mutex_clear_owner(lock);
+ if (locked)
+ mutex_set_owner(lock);
+ else
+ mutex_clear_owner(lock);
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
osq_lock_init(&lock->osq);
#endif
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 6470eb8..6df6bb3 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -930,7 +930,7 @@ int bus_register(struct bus_type *bus)
}

INIT_LIST_HEAD(&priv->interfaces);
- __mutex_init(&priv->mutex, "subsys mutex", key);
+ __mutex_init(&priv->mutex, "subsys mutex", key, 0);
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);

diff --git a/drivers/base/class.c b/drivers/base/class.c
index 71059e3..a7e8031 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -176,7 +176,7 @@ int __class_register(struct class *cls, struct lock_class_key *key)
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
INIT_LIST_HEAD(&cp->interfaces);
kset_init(&cp->glue_dirs);
- __mutex_init(&cp->mutex, "subsys mutex", key);
+ __mutex_init(&cp->mutex, "subsys mutex", key, 0);
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
if (error) {
kfree(cp);
diff --git a/drivers/gpu/drm/nouveau/nvkm/core/subdev.c b/drivers/gpu/drm/nouveau/nvkm/core/subdev.c
index b185578..7b0b022 100644
--- a/drivers/gpu/drm/nouveau/nvkm/core/subdev.c
+++ b/drivers/gpu/drm/nouveau/nvkm/core/subdev.c
@@ -198,6 +198,6 @@ nvkm_subdev_ctor(const struct nvkm_subdev_func *func,
subdev->device = device;
subdev->index = index;

- __mutex_init(&subdev->mutex, name, &nvkm_subdev_lock_class[index]);
+ __mutex_init(&subdev->mutex, name, &nvkm_subdev_lock_class[index], 0);
subdev->debug = nvkm_dbgopt(device->dbgopt, name);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c
index 5df9669..57fa64e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c
@@ -372,7 +372,7 @@ nvkm_vm_create(struct nvkm_mmu *mmu, u64 offset, u64 length, u64 mm_offset,
if (!vm)
return -ENOMEM;

- __mutex_init(&vm->mutex, "&vm->mutex", key ? key : &_key);
+ __mutex_init(&vm->mutex, "&vm->mutex", key ? key : &_key, 0);
INIT_LIST_HEAD(&vm->pgd_list);
vm->mmu = mmu;
kref_init(&vm->refcount);
diff --git a/include/linux/ww_mutex.h b/include/linux/ww_mutex.h
index 760399a..22bc051 100644
--- a/include/linux/ww_mutex.h
+++ b/include/linux/ww_mutex.h
@@ -85,7 +85,7 @@ struct ww_mutex {
static inline void ww_mutex_init(struct ww_mutex *lock,
struct ww_class *ww_class)
{
- __mutex_init(&lock->base, ww_class->mutex_name, &ww_class->mutex_key);
+ __mutex_init(&lock->base, ww_class->mutex_name, &ww_class->mutex_key, 0);
lock->ctx = NULL;
#ifdef CONFIG_DEBUG_MUTEXES
lock->ww_class = ww_class;
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 803001a..b9f87a3 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -2009,7 +2009,7 @@ int stop_sync_thread(struct netns_ipvs *ipvs, int state)
*/
int __net_init ip_vs_sync_net_init(struct netns_ipvs *ipvs)
{
- __mutex_init(&ipvs->sync_mutex, "ipvs->sync_mutex", &__ipvs_sync_key);
+ __mutex_init(&ipvs->sync_mutex, "ipvs->sync_mutex", &__ipvs_sync_key, 0);
spin_lock_init(&ipvs->sync_lock);
spin_lock_init(&ipvs->sync_buff_lock);
return 0;
--
2.7.4