1*1fb62fb0SOlivier Houchard /* 2*1fb62fb0SOlivier Houchard * Copyright 2010-2015 Samy Al Bahra. 3*1fb62fb0SOlivier Houchard * All rights reserved. 4*1fb62fb0SOlivier Houchard * 5*1fb62fb0SOlivier Houchard * Redistribution and use in source and binary forms, with or without 6*1fb62fb0SOlivier Houchard * modification, are permitted provided that the following conditions 7*1fb62fb0SOlivier Houchard * are met: 8*1fb62fb0SOlivier Houchard * 1. Redistributions of source code must retain the above copyright 9*1fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer. 10*1fb62fb0SOlivier Houchard * 2. Redistributions in binary form must reproduce the above copyright 11*1fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer in the 12*1fb62fb0SOlivier Houchard * documentation and/or other materials provided with the distribution. 13*1fb62fb0SOlivier Houchard * 14*1fb62fb0SOlivier Houchard * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*1fb62fb0SOlivier Houchard * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*1fb62fb0SOlivier Houchard * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*1fb62fb0SOlivier Houchard * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*1fb62fb0SOlivier Houchard * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*1fb62fb0SOlivier Houchard * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*1fb62fb0SOlivier Houchard * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*1fb62fb0SOlivier Houchard * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*1fb62fb0SOlivier Houchard * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*1fb62fb0SOlivier Houchard * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*1fb62fb0SOlivier Houchard * SUCH DAMAGE. 25*1fb62fb0SOlivier Houchard */ 26*1fb62fb0SOlivier Houchard 27*1fb62fb0SOlivier Houchard #ifndef CK_SPINLOCK_MCS_H 28*1fb62fb0SOlivier Houchard #define CK_SPINLOCK_MCS_H 29*1fb62fb0SOlivier Houchard 30*1fb62fb0SOlivier Houchard #include <ck_cc.h> 31*1fb62fb0SOlivier Houchard #include <ck_pr.h> 32*1fb62fb0SOlivier Houchard #include <ck_stdbool.h> 33*1fb62fb0SOlivier Houchard #include <ck_stddef.h> 34*1fb62fb0SOlivier Houchard 35*1fb62fb0SOlivier Houchard #ifndef CK_F_SPINLOCK_MCS 36*1fb62fb0SOlivier Houchard #define CK_F_SPINLOCK_MCS 37*1fb62fb0SOlivier Houchard 38*1fb62fb0SOlivier Houchard struct ck_spinlock_mcs { 39*1fb62fb0SOlivier Houchard unsigned int locked; 40*1fb62fb0SOlivier Houchard struct ck_spinlock_mcs *next; 41*1fb62fb0SOlivier Houchard }; 42*1fb62fb0SOlivier Houchard typedef struct ck_spinlock_mcs * ck_spinlock_mcs_t; 43*1fb62fb0SOlivier Houchard typedef struct ck_spinlock_mcs ck_spinlock_mcs_context_t; 44*1fb62fb0SOlivier Houchard 45*1fb62fb0SOlivier Houchard #define CK_SPINLOCK_MCS_INITIALIZER (NULL) 46*1fb62fb0SOlivier Houchard 47*1fb62fb0SOlivier Houchard CK_CC_INLINE static void 48*1fb62fb0SOlivier Houchard ck_spinlock_mcs_init(struct ck_spinlock_mcs **queue) 49*1fb62fb0SOlivier Houchard { 50*1fb62fb0SOlivier Houchard 51*1fb62fb0SOlivier Houchard *queue = NULL; 52*1fb62fb0SOlivier Houchard ck_pr_barrier(); 53*1fb62fb0SOlivier Houchard return; 54*1fb62fb0SOlivier Houchard } 55*1fb62fb0SOlivier Houchard 56*1fb62fb0SOlivier Houchard CK_CC_INLINE static bool 57*1fb62fb0SOlivier Houchard ck_spinlock_mcs_trylock(struct ck_spinlock_mcs **queue, 58*1fb62fb0SOlivier Houchard struct ck_spinlock_mcs *node) 59*1fb62fb0SOlivier Houchard { 60*1fb62fb0SOlivier Houchard bool r; 61*1fb62fb0SOlivier Houchard 62*1fb62fb0SOlivier Houchard node->locked = true; 63*1fb62fb0SOlivier Houchard node->next = NULL; 64*1fb62fb0SOlivier Houchard ck_pr_fence_store_atomic(); 65*1fb62fb0SOlivier Houchard 66*1fb62fb0SOlivier Houchard r = ck_pr_cas_ptr(queue, NULL, node); 67*1fb62fb0SOlivier Houchard ck_pr_fence_lock(); 68*1fb62fb0SOlivier Houchard return r; 69*1fb62fb0SOlivier Houchard } 70*1fb62fb0SOlivier Houchard 71*1fb62fb0SOlivier Houchard CK_CC_INLINE static bool 72*1fb62fb0SOlivier Houchard ck_spinlock_mcs_locked(struct ck_spinlock_mcs **queue) 73*1fb62fb0SOlivier Houchard { 74*1fb62fb0SOlivier Houchard bool r; 75*1fb62fb0SOlivier Houchard 76*1fb62fb0SOlivier Houchard r = ck_pr_load_ptr(queue) != NULL; 77*1fb62fb0SOlivier Houchard ck_pr_fence_acquire(); 78*1fb62fb0SOlivier Houchard return r; 79*1fb62fb0SOlivier Houchard } 80*1fb62fb0SOlivier Houchard 81*1fb62fb0SOlivier Houchard CK_CC_INLINE static void 82*1fb62fb0SOlivier Houchard ck_spinlock_mcs_lock(struct ck_spinlock_mcs **queue, 83*1fb62fb0SOlivier Houchard struct ck_spinlock_mcs *node) 84*1fb62fb0SOlivier Houchard { 85*1fb62fb0SOlivier Houchard struct ck_spinlock_mcs *previous; 86*1fb62fb0SOlivier Houchard 87*1fb62fb0SOlivier Houchard /* 88*1fb62fb0SOlivier Houchard * In the case that there is a successor, let them know they must 89*1fb62fb0SOlivier Houchard * wait for us to unlock. 90*1fb62fb0SOlivier Houchard */ 91*1fb62fb0SOlivier Houchard node->locked = true; 92*1fb62fb0SOlivier Houchard node->next = NULL; 93*1fb62fb0SOlivier Houchard ck_pr_fence_store_atomic(); 94*1fb62fb0SOlivier Houchard 95*1fb62fb0SOlivier Houchard /* 96*1fb62fb0SOlivier Houchard * Swap current tail with current lock request. If the swap operation 97*1fb62fb0SOlivier Houchard * returns NULL, it means the queue was empty. If the queue was empty, 98*1fb62fb0SOlivier Houchard * then the operation is complete. 99*1fb62fb0SOlivier Houchard */ 100*1fb62fb0SOlivier Houchard previous = ck_pr_fas_ptr(queue, node); 101*1fb62fb0SOlivier Houchard if (previous != NULL) { 102*1fb62fb0SOlivier Houchard /* 103*1fb62fb0SOlivier Houchard * Let the previous lock holder know that we are waiting on 104*1fb62fb0SOlivier Houchard * them. 105*1fb62fb0SOlivier Houchard */ 106*1fb62fb0SOlivier Houchard ck_pr_store_ptr(&previous->next, node); 107*1fb62fb0SOlivier Houchard while (ck_pr_load_uint(&node->locked) == true) 108*1fb62fb0SOlivier Houchard ck_pr_stall(); 109*1fb62fb0SOlivier Houchard } 110*1fb62fb0SOlivier Houchard 111*1fb62fb0SOlivier Houchard ck_pr_fence_lock(); 112*1fb62fb0SOlivier Houchard return; 113*1fb62fb0SOlivier Houchard } 114*1fb62fb0SOlivier Houchard 115*1fb62fb0SOlivier Houchard CK_CC_INLINE static void 116*1fb62fb0SOlivier Houchard ck_spinlock_mcs_unlock(struct ck_spinlock_mcs **queue, 117*1fb62fb0SOlivier Houchard struct ck_spinlock_mcs *node) 118*1fb62fb0SOlivier Houchard { 119*1fb62fb0SOlivier Houchard struct ck_spinlock_mcs *next; 120*1fb62fb0SOlivier Houchard 121*1fb62fb0SOlivier Houchard ck_pr_fence_unlock(); 122*1fb62fb0SOlivier Houchard 123*1fb62fb0SOlivier Houchard next = ck_pr_load_ptr(&node->next); 124*1fb62fb0SOlivier Houchard if (next == NULL) { 125*1fb62fb0SOlivier Houchard /* 126*1fb62fb0SOlivier Houchard * If there is no request following us then it is a possibilty 127*1fb62fb0SOlivier Houchard * that we are the current tail. In this case, we may just 128*1fb62fb0SOlivier Houchard * mark the spinlock queue as empty. 129*1fb62fb0SOlivier Houchard */ 130*1fb62fb0SOlivier Houchard if (ck_pr_load_ptr(queue) == node && 131*1fb62fb0SOlivier Houchard ck_pr_cas_ptr(queue, node, NULL) == true) { 132*1fb62fb0SOlivier Houchard return; 133*1fb62fb0SOlivier Houchard } 134*1fb62fb0SOlivier Houchard 135*1fb62fb0SOlivier Houchard /* 136*1fb62fb0SOlivier Houchard * If the node is not the current tail then a lock operation 137*1fb62fb0SOlivier Houchard * is in-progress. In this case, busy-wait until the queue is 138*1fb62fb0SOlivier Houchard * in a consistent state to wake up the incoming lock 139*1fb62fb0SOlivier Houchard * request. 140*1fb62fb0SOlivier Houchard */ 141*1fb62fb0SOlivier Houchard for (;;) { 142*1fb62fb0SOlivier Houchard next = ck_pr_load_ptr(&node->next); 143*1fb62fb0SOlivier Houchard if (next != NULL) 144*1fb62fb0SOlivier Houchard break; 145*1fb62fb0SOlivier Houchard 146*1fb62fb0SOlivier Houchard ck_pr_stall(); 147*1fb62fb0SOlivier Houchard } 148*1fb62fb0SOlivier Houchard } 149*1fb62fb0SOlivier Houchard 150*1fb62fb0SOlivier Houchard /* Allow the next lock operation to complete. */ 151*1fb62fb0SOlivier Houchard ck_pr_store_uint(&next->locked, false); 152*1fb62fb0SOlivier Houchard return; 153*1fb62fb0SOlivier Houchard } 154*1fb62fb0SOlivier Houchard #endif /* CK_F_SPINLOCK_MCS */ 155*1fb62fb0SOlivier Houchard #endif /* CK_SPINLOCK_MCS_H */ 156