Path: blob/main/sys/contrib/ck/include/spinlock/ticket.h
48375 views
/*1* Copyright 2010-2015 Samy Al Bahra.2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#ifndef CK_SPINLOCK_TICKET_H27#define CK_SPINLOCK_TICKET_H2829#include <ck_backoff.h>30#include <ck_cc.h>31#include <ck_elide.h>32#include <ck_md.h>33#include <ck_pr.h>34#include <ck_stdbool.h>3536#ifndef CK_F_SPINLOCK_TICKET37#define CK_F_SPINLOCK_TICKET38/*39* If 16-bit or 32-bit increment is supported, implement support for40* trylock functionality on availability of 32-bit or 64-bit fetch-and-add41* and compare-and-swap. This code path is only applied to x86*.42*/43#if defined(CK_MD_TSO) && (defined(__x86__) || defined(__x86_64__))44#if defined(CK_F_PR_FAA_32) && defined(CK_F_PR_INC_16) && defined(CK_F_PR_CAS_32)45#define CK_SPINLOCK_TICKET_TYPE uint32_t46#define CK_SPINLOCK_TICKET_TYPE_BASE uint16_t47#define CK_SPINLOCK_TICKET_INC(x) ck_pr_inc_16(x)48#define CK_SPINLOCK_TICKET_CAS(x, y, z) ck_pr_cas_32(x, y, z)49#define CK_SPINLOCK_TICKET_FAA(x, y) ck_pr_faa_32(x, y)50#define CK_SPINLOCK_TICKET_LOAD(x) ck_pr_load_32(x)51#define CK_SPINLOCK_TICKET_INCREMENT (0x00010000UL)52#define CK_SPINLOCK_TICKET_MASK (0xFFFFUL)53#define CK_SPINLOCK_TICKET_SHIFT (16)54#elif defined(CK_F_PR_FAA_64) && defined(CK_F_PR_INC_32) && defined(CK_F_PR_CAS_64)55#define CK_SPINLOCK_TICKET_TYPE uint64_t56#define CK_SPINLOCK_TICKET_TYPE_BASE uint32_t57#define CK_SPINLOCK_TICKET_INC(x) ck_pr_inc_32(x)58#define CK_SPINLOCK_TICKET_CAS(x, y, z) ck_pr_cas_64(x, y, z)59#define CK_SPINLOCK_TICKET_FAA(x, y) ck_pr_faa_64(x, y)60#define CK_SPINLOCK_TICKET_LOAD(x) ck_pr_load_64(x)61#define CK_SPINLOCK_TICKET_INCREMENT (0x0000000100000000ULL)62#define CK_SPINLOCK_TICKET_MASK (0xFFFFFFFFULL)63#define CK_SPINLOCK_TICKET_SHIFT (32)64#endif65#endif /* CK_MD_TSO */6667#if defined(CK_SPINLOCK_TICKET_TYPE)68#define CK_F_SPINLOCK_TICKET_TRYLOCK6970struct ck_spinlock_ticket {71CK_SPINLOCK_TICKET_TYPE value;72};73typedef struct ck_spinlock_ticket ck_spinlock_ticket_t;74#define CK_SPINLOCK_TICKET_INITIALIZER { .value = 0 }7576CK_CC_INLINE static void77ck_spinlock_ticket_init(struct ck_spinlock_ticket *ticket)78{7980ticket->value = 0;81ck_pr_barrier();82return;83}8485CK_CC_INLINE static bool86ck_spinlock_ticket_locked(struct ck_spinlock_ticket *ticket)87{88CK_SPINLOCK_TICKET_TYPE request, position;8990request = CK_SPINLOCK_TICKET_LOAD(&ticket->value);91position = request & CK_SPINLOCK_TICKET_MASK;92request >>= CK_SPINLOCK_TICKET_SHIFT;9394ck_pr_fence_acquire();95return request != position;96}9798CK_CC_INLINE static void99ck_spinlock_ticket_lock(struct ck_spinlock_ticket *ticket)100{101CK_SPINLOCK_TICKET_TYPE request, position;102103/* Get our ticket number and set next ticket number. */104request = CK_SPINLOCK_TICKET_FAA(&ticket->value,105CK_SPINLOCK_TICKET_INCREMENT);106107position = request & CK_SPINLOCK_TICKET_MASK;108request >>= CK_SPINLOCK_TICKET_SHIFT;109110while (request != position) {111ck_pr_stall();112position = CK_SPINLOCK_TICKET_LOAD(&ticket->value) &113CK_SPINLOCK_TICKET_MASK;114}115116ck_pr_fence_lock();117return;118}119120CK_CC_INLINE static void121ck_spinlock_ticket_lock_pb(struct ck_spinlock_ticket *ticket, unsigned int c)122{123CK_SPINLOCK_TICKET_TYPE request, position;124ck_backoff_t backoff;125126/* Get our ticket number and set next ticket number. */127request = CK_SPINLOCK_TICKET_FAA(&ticket->value,128CK_SPINLOCK_TICKET_INCREMENT);129130position = request & CK_SPINLOCK_TICKET_MASK;131request >>= CK_SPINLOCK_TICKET_SHIFT;132133while (request != position) {134ck_pr_stall();135position = CK_SPINLOCK_TICKET_LOAD(&ticket->value) &136CK_SPINLOCK_TICKET_MASK;137138backoff = (request - position) & CK_SPINLOCK_TICKET_MASK;139backoff <<= c;140ck_backoff_eb(&backoff);141}142143ck_pr_fence_lock();144return;145}146147CK_CC_INLINE static bool148ck_spinlock_ticket_trylock(struct ck_spinlock_ticket *ticket)149{150CK_SPINLOCK_TICKET_TYPE snapshot, request, position;151152snapshot = CK_SPINLOCK_TICKET_LOAD(&ticket->value);153position = snapshot & CK_SPINLOCK_TICKET_MASK;154request = snapshot >> CK_SPINLOCK_TICKET_SHIFT;155156if (position != request)157return false;158159if (CK_SPINLOCK_TICKET_CAS(&ticket->value,160snapshot, snapshot + CK_SPINLOCK_TICKET_INCREMENT) == false) {161return false;162}163164ck_pr_fence_lock();165return true;166}167168CK_CC_INLINE static void169ck_spinlock_ticket_unlock(struct ck_spinlock_ticket *ticket)170{171172ck_pr_fence_unlock();173CK_SPINLOCK_TICKET_INC((CK_SPINLOCK_TICKET_TYPE_BASE *)(void *)&ticket->value);174return;175}176177#undef CK_SPINLOCK_TICKET_TYPE178#undef CK_SPINLOCK_TICKET_TYPE_BASE179#undef CK_SPINLOCK_TICKET_INC180#undef CK_SPINLOCK_TICKET_FAA181#undef CK_SPINLOCK_TICKET_LOAD182#undef CK_SPINLOCK_TICKET_INCREMENT183#undef CK_SPINLOCK_TICKET_MASK184#undef CK_SPINLOCK_TICKET_SHIFT185#else186/*187* MESI benefits from cacheline padding between next and current. This avoids188* invalidation of current from the cache due to incoming lock requests.189*/190struct ck_spinlock_ticket {191unsigned int next;192unsigned int position;193};194typedef struct ck_spinlock_ticket ck_spinlock_ticket_t;195196#define CK_SPINLOCK_TICKET_INITIALIZER {.next = 0, .position = 0}197198CK_CC_INLINE static void199ck_spinlock_ticket_init(struct ck_spinlock_ticket *ticket)200{201202ticket->next = 0;203ticket->position = 0;204ck_pr_barrier();205206return;207}208209CK_CC_INLINE static bool210ck_spinlock_ticket_locked(struct ck_spinlock_ticket *ticket)211{212bool r;213214r = ck_pr_load_uint(&ticket->position) !=215ck_pr_load_uint(&ticket->next);216ck_pr_fence_acquire();217return r;218}219220CK_CC_INLINE static void221ck_spinlock_ticket_lock(struct ck_spinlock_ticket *ticket)222{223unsigned int request;224225/* Get our ticket number and set next ticket number. */226request = ck_pr_faa_uint(&ticket->next, 1);227228/*229* Busy-wait until our ticket number is current.230* We can get away without a fence here assuming231* our position counter does not overflow.232*/233while (ck_pr_load_uint(&ticket->position) != request)234ck_pr_stall();235236ck_pr_fence_lock();237return;238}239240CK_CC_INLINE static void241ck_spinlock_ticket_lock_pb(struct ck_spinlock_ticket *ticket, unsigned int c)242{243ck_backoff_t backoff;244unsigned int request, position;245246request = ck_pr_faa_uint(&ticket->next, 1);247248for (;;) {249position = ck_pr_load_uint(&ticket->position);250if (position == request)251break;252253backoff = request - position;254backoff <<= c;255256/*257* Ideally, back-off from generating cache traffic for at least258* the amount of time necessary for the number of pending lock259* acquisition and relinquish operations (assuming an empty260* critical section).261*/262ck_backoff_eb(&backoff);263}264265ck_pr_fence_lock();266return;267}268269CK_CC_INLINE static void270ck_spinlock_ticket_unlock(struct ck_spinlock_ticket *ticket)271{272unsigned int update;273274ck_pr_fence_unlock();275276/*277* Update current ticket value so next lock request can proceed.278* Overflow behavior is assumed to be roll-over, in which case,279* it is only an issue if there are 2^32 pending lock requests.280*/281update = ck_pr_load_uint(&ticket->position);282ck_pr_store_uint(&ticket->position, update + 1);283return;284}285#endif /* !CK_F_SPINLOCK_TICKET_TRYLOCK */286287CK_ELIDE_PROTOTYPE(ck_spinlock_ticket, ck_spinlock_ticket_t,288ck_spinlock_ticket_locked, ck_spinlock_ticket_lock,289ck_spinlock_ticket_locked, ck_spinlock_ticket_unlock)290291CK_ELIDE_TRYLOCK_PROTOTYPE(ck_spinlock_ticket, ck_spinlock_ticket_t,292ck_spinlock_ticket_locked, ck_spinlock_ticket_trylock)293294#endif /* CK_F_SPINLOCK_TICKET */295#endif /* CK_SPINLOCK_TICKET_H */296297298