19760ac0aSHans Petter Selasky /*- 29760ac0aSHans Petter Selasky * Copyright (c) 2017 Mellanox Technologies, Ltd. 39760ac0aSHans Petter Selasky * All rights reserved. 49760ac0aSHans Petter Selasky * 59760ac0aSHans Petter Selasky * Redistribution and use in source and binary forms, with or without 69760ac0aSHans Petter Selasky * modification, are permitted provided that the following conditions 79760ac0aSHans Petter Selasky * are met: 89760ac0aSHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 99760ac0aSHans Petter Selasky * notice unmodified, this list of conditions, and the following 109760ac0aSHans Petter Selasky * disclaimer. 119760ac0aSHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 129760ac0aSHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 139760ac0aSHans Petter Selasky * documentation and/or other materials provided with the distribution. 149760ac0aSHans Petter Selasky * 159760ac0aSHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 169760ac0aSHans Petter Selasky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 179760ac0aSHans Petter Selasky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 189760ac0aSHans Petter Selasky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 199760ac0aSHans Petter Selasky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 209760ac0aSHans Petter Selasky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 219760ac0aSHans Petter Selasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 229760ac0aSHans Petter Selasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 239760ac0aSHans Petter Selasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 249760ac0aSHans Petter Selasky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 259760ac0aSHans Petter Selasky * 269760ac0aSHans Petter Selasky * $FreeBSD$ 279760ac0aSHans Petter Selasky */ 289760ac0aSHans Petter Selasky 299760ac0aSHans Petter Selasky #include <sys/queue.h> 309760ac0aSHans Petter Selasky 31*94944062SHans Petter Selasky #include <linux/sched.h> 329760ac0aSHans Petter Selasky #include <linux/ww_mutex.h> 339760ac0aSHans Petter Selasky 349760ac0aSHans Petter Selasky struct ww_mutex_thread { 359760ac0aSHans Petter Selasky TAILQ_ENTRY(ww_mutex_thread) entry; 369760ac0aSHans Petter Selasky struct thread *thread; 379760ac0aSHans Petter Selasky struct ww_mutex *lock; 389760ac0aSHans Petter Selasky }; 399760ac0aSHans Petter Selasky 409760ac0aSHans Petter Selasky static TAILQ_HEAD(, ww_mutex_thread) ww_mutex_head; 419760ac0aSHans Petter Selasky static struct mtx ww_mutex_global; 429760ac0aSHans Petter Selasky 439760ac0aSHans Petter Selasky static void 449760ac0aSHans Petter Selasky linux_ww_init(void *arg) 459760ac0aSHans Petter Selasky { 469760ac0aSHans Petter Selasky TAILQ_INIT(&ww_mutex_head); 479760ac0aSHans Petter Selasky mtx_init(&ww_mutex_global, "lkpi-ww-mtx", NULL, MTX_DEF); 489760ac0aSHans Petter Selasky } 499760ac0aSHans Petter Selasky 509760ac0aSHans Petter Selasky SYSINIT(ww_init, SI_SUB_LOCK, SI_ORDER_SECOND, linux_ww_init, NULL); 519760ac0aSHans Petter Selasky 529760ac0aSHans Petter Selasky static void 539760ac0aSHans Petter Selasky linux_ww_uninit(void *arg) 549760ac0aSHans Petter Selasky { 559760ac0aSHans Petter Selasky mtx_destroy(&ww_mutex_global); 569760ac0aSHans Petter Selasky } 579760ac0aSHans Petter Selasky 589760ac0aSHans Petter Selasky SYSUNINIT(ww_uninit, SI_SUB_LOCK, SI_ORDER_SECOND, linux_ww_uninit, NULL); 599760ac0aSHans Petter Selasky 609760ac0aSHans Petter Selasky static inline void 619760ac0aSHans Petter Selasky linux_ww_lock(void) 629760ac0aSHans Petter Selasky { 639760ac0aSHans Petter Selasky mtx_lock(&ww_mutex_global); 649760ac0aSHans Petter Selasky } 659760ac0aSHans Petter Selasky 669760ac0aSHans Petter Selasky static inline void 679760ac0aSHans Petter Selasky linux_ww_unlock(void) 689760ac0aSHans Petter Selasky { 699760ac0aSHans Petter Selasky mtx_unlock(&ww_mutex_global); 709760ac0aSHans Petter Selasky } 719760ac0aSHans Petter Selasky 729760ac0aSHans Petter Selasky /* lock a mutex with deadlock avoidance */ 739760ac0aSHans Petter Selasky int 749760ac0aSHans Petter Selasky linux_ww_mutex_lock_sub(struct ww_mutex *lock, int catch_signal) 759760ac0aSHans Petter Selasky { 76*94944062SHans Petter Selasky struct task_struct *task; 779760ac0aSHans Petter Selasky struct ww_mutex_thread entry; 789760ac0aSHans Petter Selasky struct ww_mutex_thread *other; 799760ac0aSHans Petter Selasky int retval = 0; 809760ac0aSHans Petter Selasky 81*94944062SHans Petter Selasky task = current; 82*94944062SHans Petter Selasky 839760ac0aSHans Petter Selasky linux_ww_lock(); 849760ac0aSHans Petter Selasky if (unlikely(sx_try_xlock(&lock->base.sx) == 0)) { 859760ac0aSHans Petter Selasky entry.thread = curthread; 869760ac0aSHans Petter Selasky entry.lock = lock; 879760ac0aSHans Petter Selasky TAILQ_INSERT_TAIL(&ww_mutex_head, &entry, entry); 889760ac0aSHans Petter Selasky 899760ac0aSHans Petter Selasky do { 909760ac0aSHans Petter Selasky struct thread *owner = (struct thread *) 919760ac0aSHans Petter Selasky SX_OWNER(lock->base.sx.sx_lock); 929760ac0aSHans Petter Selasky 939760ac0aSHans Petter Selasky /* scan for deadlock */ 949760ac0aSHans Petter Selasky TAILQ_FOREACH(other, &ww_mutex_head, entry) { 959760ac0aSHans Petter Selasky /* skip own thread */ 969760ac0aSHans Petter Selasky if (other == &entry) 979760ac0aSHans Petter Selasky continue; 989760ac0aSHans Petter Selasky /* 999760ac0aSHans Petter Selasky * If another thread is owning our 1009760ac0aSHans Petter Selasky * lock and is at the same time trying 1019760ac0aSHans Petter Selasky * to acquire a lock this thread owns, 1029760ac0aSHans Petter Selasky * that means deadlock. 1039760ac0aSHans Petter Selasky */ 1049760ac0aSHans Petter Selasky if (other->thread == owner && 1059760ac0aSHans Petter Selasky (struct thread *)SX_OWNER( 1069760ac0aSHans Petter Selasky other->lock->base.sx.sx_lock) == curthread) { 1079760ac0aSHans Petter Selasky retval = -EDEADLK; 1089760ac0aSHans Petter Selasky goto done; 1099760ac0aSHans Petter Selasky } 1109760ac0aSHans Petter Selasky } 1119760ac0aSHans Petter Selasky if (catch_signal) { 112*94944062SHans Petter Selasky retval = -cv_wait_sig(&lock->condvar, &ww_mutex_global); 113*94944062SHans Petter Selasky if (retval != 0) { 114*94944062SHans Petter Selasky linux_schedule_save_interrupt_value(task, retval); 1159760ac0aSHans Petter Selasky retval = -EINTR; 1169760ac0aSHans Petter Selasky goto done; 1179760ac0aSHans Petter Selasky } 1189760ac0aSHans Petter Selasky } else { 1199760ac0aSHans Petter Selasky cv_wait(&lock->condvar, &ww_mutex_global); 1209760ac0aSHans Petter Selasky } 1219760ac0aSHans Petter Selasky } while (sx_try_xlock(&lock->base.sx) == 0); 1229760ac0aSHans Petter Selasky done: 1239760ac0aSHans Petter Selasky TAILQ_REMOVE(&ww_mutex_head, &entry, entry); 1249760ac0aSHans Petter Selasky 1259760ac0aSHans Petter Selasky /* if the lock is free, wakeup next lock waiter, if any */ 1269760ac0aSHans Petter Selasky if ((struct thread *)SX_OWNER(lock->base.sx.sx_lock) == NULL) 1279760ac0aSHans Petter Selasky cv_signal(&lock->condvar); 1289760ac0aSHans Petter Selasky } 1299760ac0aSHans Petter Selasky linux_ww_unlock(); 1309760ac0aSHans Petter Selasky return (retval); 1319760ac0aSHans Petter Selasky } 1329760ac0aSHans Petter Selasky 1339760ac0aSHans Petter Selasky void 1349760ac0aSHans Petter Selasky linux_ww_mutex_unlock_sub(struct ww_mutex *lock) 1359760ac0aSHans Petter Selasky { 1369760ac0aSHans Petter Selasky /* protect ww_mutex ownership change */ 1379760ac0aSHans Petter Selasky linux_ww_lock(); 1389760ac0aSHans Petter Selasky sx_xunlock(&lock->base.sx); 1399760ac0aSHans Petter Selasky /* wakeup a lock waiter, if any */ 1409760ac0aSHans Petter Selasky cv_signal(&lock->condvar); 1419760ac0aSHans Petter Selasky linux_ww_unlock(); 1429760ac0aSHans Petter Selasky } 143*94944062SHans Petter Selasky 144*94944062SHans Petter Selasky int 145*94944062SHans Petter Selasky linux_mutex_lock_interruptible(mutex_t *m) 146*94944062SHans Petter Selasky { 147*94944062SHans Petter Selasky int error; 148*94944062SHans Petter Selasky 149*94944062SHans Petter Selasky error = -sx_xlock_sig(&m->sx); 150*94944062SHans Petter Selasky if (error != 0) { 151*94944062SHans Petter Selasky linux_schedule_save_interrupt_value(current, error); 152*94944062SHans Petter Selasky error = -EINTR; 153*94944062SHans Petter Selasky } 154*94944062SHans Petter Selasky return (error); 155*94944062SHans Petter Selasky } 156*94944062SHans Petter Selasky 157*94944062SHans Petter Selasky int 158*94944062SHans Petter Selasky linux_down_write_killable(struct rw_semaphore *rw) 159*94944062SHans Petter Selasky { 160*94944062SHans Petter Selasky int error; 161*94944062SHans Petter Selasky 162*94944062SHans Petter Selasky error = -sx_xlock_sig(&rw->sx); 163*94944062SHans Petter Selasky if (error != 0) { 164*94944062SHans Petter Selasky linux_schedule_save_interrupt_value(current, error); 165*94944062SHans Petter Selasky error = -EINTR; 166*94944062SHans Petter Selasky } 167*94944062SHans Petter Selasky return (error); 168*94944062SHans Petter Selasky } 169