11fb62fb0SOlivier Houchard /* 21fb62fb0SOlivier Houchard * Copyright 2013-2015 Olivier Houchard 31fb62fb0SOlivier Houchard * Copyright 2010-2015 Samy Al Bahra. 41fb62fb0SOlivier Houchard * All rights reserved. 51fb62fb0SOlivier Houchard * 61fb62fb0SOlivier Houchard * Redistribution and use in source and binary forms, with or without 71fb62fb0SOlivier Houchard * modification, are permitted provided that the following conditions 81fb62fb0SOlivier Houchard * are met: 91fb62fb0SOlivier Houchard * 1. Redistributions of source code must retain the above copyright 101fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer. 111fb62fb0SOlivier Houchard * 2. Redistributions in binary form must reproduce the above copyright 121fb62fb0SOlivier Houchard * notice, this list of conditions and the following disclaimer in the 131fb62fb0SOlivier Houchard * documentation and/or other materials provided with the distribution. 141fb62fb0SOlivier Houchard * 151fb62fb0SOlivier Houchard * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 161fb62fb0SOlivier Houchard * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 171fb62fb0SOlivier Houchard * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 181fb62fb0SOlivier Houchard * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 191fb62fb0SOlivier Houchard * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 201fb62fb0SOlivier Houchard * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 211fb62fb0SOlivier Houchard * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 221fb62fb0SOlivier Houchard * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 231fb62fb0SOlivier Houchard * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 241fb62fb0SOlivier Houchard * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 251fb62fb0SOlivier Houchard * SUCH DAMAGE. 261fb62fb0SOlivier Houchard */ 271fb62fb0SOlivier Houchard 281fb62fb0SOlivier Houchard #ifndef CK_SPINLOCK_HCLH_H 291fb62fb0SOlivier Houchard #define CK_SPINLOCK_HCLH_H 301fb62fb0SOlivier Houchard 311fb62fb0SOlivier Houchard #include <ck_cc.h> 321fb62fb0SOlivier Houchard #include <ck_pr.h> 331fb62fb0SOlivier Houchard #include <ck_stdbool.h> 341fb62fb0SOlivier Houchard #include <ck_stddef.h> 351fb62fb0SOlivier Houchard 361fb62fb0SOlivier Houchard #ifndef CK_F_SPINLOCK_HCLH 371fb62fb0SOlivier Houchard #define CK_F_SPINLOCK_HCLH 381fb62fb0SOlivier Houchard struct ck_spinlock_hclh { 391fb62fb0SOlivier Houchard unsigned int wait; 401fb62fb0SOlivier Houchard unsigned int splice; 411fb62fb0SOlivier Houchard int cluster_id; 421fb62fb0SOlivier Houchard struct ck_spinlock_hclh *previous; 431fb62fb0SOlivier Houchard }; 441fb62fb0SOlivier Houchard typedef struct ck_spinlock_hclh ck_spinlock_hclh_t; 451fb62fb0SOlivier Houchard 461fb62fb0SOlivier Houchard CK_CC_INLINE static void 471fb62fb0SOlivier Houchard ck_spinlock_hclh_init(struct ck_spinlock_hclh **lock, 481fb62fb0SOlivier Houchard struct ck_spinlock_hclh *unowned, 491fb62fb0SOlivier Houchard int cluster_id) 501fb62fb0SOlivier Houchard { 511fb62fb0SOlivier Houchard 521fb62fb0SOlivier Houchard unowned->previous = NULL; 531fb62fb0SOlivier Houchard unowned->wait = false; 541fb62fb0SOlivier Houchard unowned->splice = false; 551fb62fb0SOlivier Houchard unowned->cluster_id = cluster_id; 561fb62fb0SOlivier Houchard *lock = unowned; 571fb62fb0SOlivier Houchard ck_pr_barrier(); 581fb62fb0SOlivier Houchard return; 591fb62fb0SOlivier Houchard } 601fb62fb0SOlivier Houchard 611fb62fb0SOlivier Houchard CK_CC_INLINE static bool 621fb62fb0SOlivier Houchard ck_spinlock_hclh_locked(struct ck_spinlock_hclh **queue) 631fb62fb0SOlivier Houchard { 641fb62fb0SOlivier Houchard struct ck_spinlock_hclh *head; 651fb62fb0SOlivier Houchard bool r; 661fb62fb0SOlivier Houchard 671fb62fb0SOlivier Houchard head = ck_pr_load_ptr(queue); 681fb62fb0SOlivier Houchard r = ck_pr_load_uint(&head->wait); 691fb62fb0SOlivier Houchard ck_pr_fence_acquire(); 701fb62fb0SOlivier Houchard return r; 711fb62fb0SOlivier Houchard } 721fb62fb0SOlivier Houchard 731fb62fb0SOlivier Houchard CK_CC_INLINE static void 741fb62fb0SOlivier Houchard ck_spinlock_hclh_lock(struct ck_spinlock_hclh **glob_queue, 751fb62fb0SOlivier Houchard struct ck_spinlock_hclh **local_queue, 761fb62fb0SOlivier Houchard struct ck_spinlock_hclh *thread) 771fb62fb0SOlivier Houchard { 781fb62fb0SOlivier Houchard struct ck_spinlock_hclh *previous, *local_tail; 791fb62fb0SOlivier Houchard 801fb62fb0SOlivier Houchard /* Indicate to the next thread on queue that they will have to block. */ 811fb62fb0SOlivier Houchard thread->wait = true; 821fb62fb0SOlivier Houchard thread->splice = false; 831fb62fb0SOlivier Houchard thread->cluster_id = (*local_queue)->cluster_id; 84*d8f1ed8dSOlivier Houchard /* Make sure previous->previous doesn't appear to be NULL */ 85*d8f1ed8dSOlivier Houchard thread->previous = *local_queue; 861fb62fb0SOlivier Houchard 871fb62fb0SOlivier Houchard /* Serialize with respect to update of local queue. */ 881fb62fb0SOlivier Houchard ck_pr_fence_store_atomic(); 891fb62fb0SOlivier Houchard 901fb62fb0SOlivier Houchard /* Mark current request as last request. Save reference to previous request. */ 911fb62fb0SOlivier Houchard previous = ck_pr_fas_ptr(local_queue, thread); 921fb62fb0SOlivier Houchard thread->previous = previous; 931fb62fb0SOlivier Houchard 941fb62fb0SOlivier Houchard /* Wait until previous thread from the local queue is done with lock. */ 951fb62fb0SOlivier Houchard ck_pr_fence_load(); 96*d8f1ed8dSOlivier Houchard if (previous->previous != NULL) { 97*d8f1ed8dSOlivier Houchard while (ck_pr_load_uint(&previous->wait) == true && 98*d8f1ed8dSOlivier Houchard ck_pr_load_int(&previous->cluster_id) == thread->cluster_id && 99*d8f1ed8dSOlivier Houchard ck_pr_load_uint(&previous->splice) == false) 1001fb62fb0SOlivier Houchard ck_pr_stall(); 1011fb62fb0SOlivier Houchard 1021fb62fb0SOlivier Houchard /* We're head of the global queue, we're done */ 103*d8f1ed8dSOlivier Houchard if (ck_pr_load_int(&previous->cluster_id) == thread->cluster_id && 104*d8f1ed8dSOlivier Houchard ck_pr_load_uint(&previous->splice) == false) 1051fb62fb0SOlivier Houchard return; 1061fb62fb0SOlivier Houchard } 1071fb62fb0SOlivier Houchard 1081fb62fb0SOlivier Houchard /* Now we need to splice the local queue into the global queue. */ 1091fb62fb0SOlivier Houchard local_tail = ck_pr_load_ptr(local_queue); 1101fb62fb0SOlivier Houchard previous = ck_pr_fas_ptr(glob_queue, local_tail); 1111fb62fb0SOlivier Houchard 1121fb62fb0SOlivier Houchard ck_pr_store_uint(&local_tail->splice, true); 1131fb62fb0SOlivier Houchard 1141fb62fb0SOlivier Houchard /* Wait until previous thread from the global queue is done with lock. */ 1151fb62fb0SOlivier Houchard while (ck_pr_load_uint(&previous->wait) == true) 1161fb62fb0SOlivier Houchard ck_pr_stall(); 1171fb62fb0SOlivier Houchard 1181fb62fb0SOlivier Houchard ck_pr_fence_lock(); 1191fb62fb0SOlivier Houchard return; 1201fb62fb0SOlivier Houchard } 1211fb62fb0SOlivier Houchard 1221fb62fb0SOlivier Houchard CK_CC_INLINE static void 1231fb62fb0SOlivier Houchard ck_spinlock_hclh_unlock(struct ck_spinlock_hclh **thread) 1241fb62fb0SOlivier Houchard { 1251fb62fb0SOlivier Houchard struct ck_spinlock_hclh *previous; 1261fb62fb0SOlivier Houchard 1271fb62fb0SOlivier Houchard /* 1281fb62fb0SOlivier Houchard * If there are waiters, they are spinning on the current node wait 1291fb62fb0SOlivier Houchard * flag. The flag is cleared so that the successor may complete an 1301fb62fb0SOlivier Houchard * acquisition. If the caller is pre-empted then the predecessor field 1311fb62fb0SOlivier Houchard * may be updated by a successor's lock operation. In order to avoid 1321fb62fb0SOlivier Houchard * this, save a copy of the predecessor before setting the flag. 1331fb62fb0SOlivier Houchard */ 1341fb62fb0SOlivier Houchard previous = thread[0]->previous; 1351fb62fb0SOlivier Houchard 1361fb62fb0SOlivier Houchard /* We have to pay this cost anyways, use it as a compiler barrier too. */ 1371fb62fb0SOlivier Houchard ck_pr_fence_unlock(); 1381fb62fb0SOlivier Houchard ck_pr_store_uint(&(*thread)->wait, false); 1391fb62fb0SOlivier Houchard 1401fb62fb0SOlivier Houchard /* 1411fb62fb0SOlivier Houchard * Predecessor is guaranteed not to be spinning on previous request, 1421fb62fb0SOlivier Houchard * so update caller to use previous structure. This allows successor 1431fb62fb0SOlivier Houchard * all the time in the world to successfully read updated wait flag. 1441fb62fb0SOlivier Houchard */ 1451fb62fb0SOlivier Houchard *thread = previous; 1461fb62fb0SOlivier Houchard return; 1471fb62fb0SOlivier Houchard } 1481fb62fb0SOlivier Houchard #endif /* CK_F_SPINLOCK_HCLH */ 1491fb62fb0SOlivier Houchard #endif /* CK_SPINLOCK_HCLH_H */ 150