#ifndef _LINUX_WW_MUTEX_H_
#define _LINUX_WW_MUTEX_H_
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/mutex.h>
#include <machine/intr.h>
#include <linux/mutex.h>
struct ww_class {
volatile u_long stamp;
const char *name;
};
struct ww_acquire_ctx {
u_long stamp;
struct ww_class *ww_class;
};
struct ww_mutex {
struct mutex base;
volatile int acquired;
struct ww_acquire_ctx *ctx;
volatile struct proc *owner;
};
#define DEFINE_WW_CLASS(classname) \
struct ww_class classname = { \
.stamp = 0, \
.name = #classname \
}
#define DEFINE_WD_CLASS(classname) \
struct ww_class classname = { \
.stamp = 0, \
.name = #classname \
}
static inline void
ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *ww_class) {
ctx->stamp = __sync_fetch_and_add(&ww_class->stamp, 1);
ctx->ww_class = ww_class;
}
static inline void
ww_acquire_done(__unused struct ww_acquire_ctx *ctx) {
}
static inline void
ww_acquire_fini(__unused struct ww_acquire_ctx *ctx) {
}
static inline void
ww_mutex_init(struct ww_mutex *lock, struct ww_class *ww_class) {
mtx_init(&lock->base, IPL_NONE);
lock->acquired = 0;
lock->ctx = NULL;
lock->owner = NULL;
}
static inline bool
ww_mutex_is_locked(struct ww_mutex *lock) {
bool res = false;
mtx_enter(&lock->base);
if (lock->acquired > 0) res = true;
mtx_leave(&lock->base);
return res;
}
static inline int
ww_mutex_trylock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) {
int res = 0;
mtx_enter(&lock->base);
if (lock->acquired == 0) {
KASSERT(lock->ctx == NULL);
lock->acquired = 1;
lock->owner = curproc;
res = 1;
}
mtx_leave(&lock->base);
return res;
}
static inline int
__ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx, bool slow, bool intr) {
int err;
mtx_enter(&lock->base);
for (;;) {
if (lock->acquired == 0) {
KASSERT(lock->ctx == NULL);
lock->acquired = 1;
lock->ctx = ctx;
lock->owner = curproc;
err = 0;
break;
}
else if (lock->owner == curproc) {
err = -EALREADY;
break;
}
else {
if (slow || ctx == NULL ||
(lock->ctx && ctx->stamp < lock->ctx->stamp)) {
KASSERT(!cold);
int s = msleep_nsec(lock, &lock->base,
intr ? PCATCH : 0,
ctx ? ctx->ww_class->name : "ww_mutex_lock",
INFSLP);
if (intr && (s == EINTR || s == ERESTART)) {
err = -EINTR;
break;
}
}
else {
err = -EDEADLK;
break;
}
}
}
mtx_leave(&lock->base);
return err;
}
static inline int
ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) {
return __ww_mutex_lock(lock, ctx, false, false);
}
static inline void
ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) {
(void)__ww_mutex_lock(lock, ctx, true, false);
}
static inline int
ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) {
return __ww_mutex_lock(lock, ctx, false, true);
}
static inline int __must_check
ww_mutex_lock_slow_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) {
return __ww_mutex_lock(lock, ctx, true, true);
}
static inline void
ww_mutex_unlock(struct ww_mutex *lock) {
mtx_enter(&lock->base);
KASSERT(lock->owner == curproc);
KASSERT(lock->acquired == 1);
lock->acquired = 0;
lock->ctx = NULL;
lock->owner = NULL;
mtx_leave(&lock->base);
wakeup(lock);
}
static inline void
ww_mutex_destroy(struct ww_mutex *lock) {
KASSERT(lock->acquired == 0);
KASSERT(lock->ctx == NULL);
KASSERT(lock->owner == NULL);
}
#endif