cregit-Linux how code gets into the kernel

Release 4.14 arch/tile/lib/spinlock_32.c

Directory: arch/tile/lib
/*
 * Copyright 2010 Tilera Corporation. All Rights Reserved.
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation, version 2.
 *
 *   This program is distributed in the hope that it will be useful, but
 *   WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 *   NON INFRINGEMENT.  See the GNU General Public License for
 *   more details.
 */

#include <linux/spinlock.h>
#include <linux/module.h>
#include <asm/processor.h>
#include <arch/spr_def.h>

#include "spinlock_common.h"


void arch_spin_lock(arch_spinlock_t *lock) { int my_ticket; int iterations = 0; int delta; while ((my_ticket = __insn_tns((void *)&lock->next_ticket)) & 1) delay_backoff(iterations++); /* Increment the next ticket number, implicitly releasing tns lock. */ lock->next_ticket = my_ticket + TICKET_QUANTUM; /* Wait until it's our turn. */ while ((delta = my_ticket - lock->current_ticket) != 0) relax((128 / CYCLES_PER_RELAX_LOOP) * delta); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf81100.00%1100.00%
Total81100.00%1100.00%

EXPORT_SYMBOL(arch_spin_lock);
int arch_spin_trylock(arch_spinlock_t *lock) { /* * Grab a ticket; no need to retry if it's busy, we'll just * treat that the same as "locked", since someone else * will lock it momentarily anyway. */ int my_ticket = __insn_tns((void *)&lock->next_ticket); if (my_ticket == lock->current_ticket) { /* Not currently locked, so lock it by keeping this ticket. */ lock->next_ticket = my_ticket + TICKET_QUANTUM; /* Success! */ return 1; } if (!(my_ticket & 1)) { /* Release next_ticket. */ lock->next_ticket = my_ticket; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf69100.00%1100.00%
Total69100.00%1100.00%

EXPORT_SYMBOL(arch_spin_trylock); /* * The low byte is always reserved to be the marker for a "tns" operation * since the low bit is set to "1" by a tns. The next seven bits are * zeroes. The next byte holds the "next" writer value, i.e. the ticket * available for the next task that wants to write. The third byte holds * the current writer value, i.e. the writer who holds the current ticket. * If current == next == 0, there are no interested writers. */ #define WR_NEXT_SHIFT _WR_NEXT_SHIFT #define WR_CURR_SHIFT _WR_CURR_SHIFT #define WR_WIDTH _WR_WIDTH #define WR_MASK ((1 << WR_WIDTH) - 1) /* * The last eight bits hold the active reader count. This has to be * zero before a writer can start to write. */ #define RD_COUNT_SHIFT _RD_COUNT_SHIFT #define RD_COUNT_WIDTH _RD_COUNT_WIDTH #define RD_COUNT_MASK ((1 << RD_COUNT_WIDTH) - 1) /* * We can get the read lock if everything but the reader bits (which * are in the high part of the word) is zero, i.e. no active or * waiting writers, no tns. * * We guard the tns/store-back with an interrupt critical section to * preserve the semantic that the same read lock can be acquired in an * interrupt context. */
int arch_read_trylock(arch_rwlock_t *rwlock) { u32 val; __insn_mtspr(SPR_INTERRUPT_CRITICAL_SECTION, 1); val = __insn_tns((int *)&rwlock->lock); if (likely((val << _RD_COUNT_WIDTH) == 0)) { val += 1 << RD_COUNT_SHIFT; rwlock->lock = val; __insn_mtspr(SPR_INTERRUPT_CRITICAL_SECTION, 0); BUG_ON(val == 0); /* we don't expect wraparound */ return 1; } if ((val & 1) == 0) rwlock->lock = val; __insn_mtspr(SPR_INTERRUPT_CRITICAL_SECTION, 0); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf104100.00%2100.00%
Total104100.00%2100.00%

EXPORT_SYMBOL(arch_read_trylock); /* * Spin doing arch_read_trylock() until we acquire the lock. * ISSUE: This approach can permanently starve readers. A reader who sees * a writer could instead take a ticket lock (just like a writer would), * and atomically enter read mode (with 1 reader) when it gets the ticket. * This way both readers and writers would always make forward progress * in a finite time. */
void arch_read_lock(arch_rwlock_t *rwlock) { u32 iterations = 0; while (unlikely(!arch_read_trylock(rwlock))) delay_backoff(iterations++); }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf31100.00%2100.00%
Total31100.00%2100.00%

EXPORT_SYMBOL(arch_read_lock);
void arch_read_unlock(arch_rwlock_t *rwlock) { u32 val, iterations = 0; mb(); /* guarantee anything modified under the lock is visible */ for (;;) { __insn_mtspr(SPR_INTERRUPT_CRITICAL_SECTION, 1); val = __insn_tns((int *)&rwlock->lock); if (likely((val & 1) == 0)) { rwlock->lock = val - (1 << _RD_COUNT_SHIFT); __insn_mtspr(SPR_INTERRUPT_CRITICAL_SECTION, 0); break; } __insn_mtspr(SPR_INTERRUPT_CRITICAL_SECTION, 0); delay_backoff(iterations++); } }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf9396.88%266.67%
Roel Kluin33.12%133.33%
Total96100.00%3100.00%

EXPORT_SYMBOL(arch_read_unlock); /* * We don't need an interrupt critical section here (unlike for * arch_read_lock) since we should never use a bare write lock where * it could be interrupted by code that could try to re-acquire it. */
void arch_write_lock(arch_rwlock_t *rwlock) { /* * The trailing underscore on this variable (and curr_ below) * reminds us that the high bits are garbage; we mask them out * when we compare them. */ u32 my_ticket_; u32 iterations = 0; u32 val = __insn_tns((int *)&rwlock->lock); if (likely(val == 0)) { rwlock->lock = 1 << _WR_NEXT_SHIFT; return; } /* * Wait until there are no readers, then bump up the next * field and capture the ticket value. */ for (;;) { if (!(val & 1)) { if ((val >> RD_COUNT_SHIFT) == 0) break; rwlock->lock = val; } delay_backoff(iterations++); val = __insn_tns((int *)&rwlock->lock); } /* Take out the next ticket and extract my ticket value. */ rwlock->lock = __insn_addb(val, 1 << WR_NEXT_SHIFT); my_ticket_ = val >> WR_NEXT_SHIFT; /* Wait until the "current" field matches our ticket. */ for (;;) { u32 curr_ = val >> WR_CURR_SHIFT; u32 delta = ((my_ticket_ - curr_) & WR_MASK); if (likely(delta == 0)) break; /* Delay based on how many lock-holders are still out there. */ relax((256 / CYCLES_PER_RELAX_LOOP) * delta); /* * Get a non-tns value to check; we don't need to tns * it ourselves. Since we're not tns'ing, we retry * more rapidly to get a valid value. */ while ((val = rwlock->lock) & 1) relax(4); } }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf197100.00%3100.00%
Total197100.00%3100.00%

EXPORT_SYMBOL(arch_write_lock);
int arch_write_trylock(arch_rwlock_t *rwlock) { u32 val = __insn_tns((int *)&rwlock->lock); /* * If a tns is in progress, or there's a waiting or active locker, * or active readers, we can't take the lock, so give up. */ if (unlikely(val != 0)) { if (!(val & 1)) rwlock->lock = val; return 0; } /* Set the "next" field to mark it locked. */ rwlock->lock = 1 << _WR_NEXT_SHIFT; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf66100.00%2100.00%
Total66100.00%2100.00%

EXPORT_SYMBOL(arch_write_trylock);
void arch_write_unlock(arch_rwlock_t *rwlock) { u32 val, eq, mask; mb(); /* guarantee anything modified under the lock is visible */ val = __insn_tns((int *)&rwlock->lock); if (likely(val == (1 << _WR_NEXT_SHIFT))) { rwlock->lock = 0; return; } while (unlikely(val & 1)) { /* Limited backoff since we are the highest-priority task. */ relax(4); val = __insn_tns((int *)&rwlock->lock); } mask = 1 << WR_CURR_SHIFT; val = __insn_addb(val, mask); eq = __insn_seqb(val, val << (WR_CURR_SHIFT - WR_NEXT_SHIFT)); val = __insn_mz(eq & mask, val); rwlock->lock = val; }

Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf134100.00%2100.00%
Total134100.00%2100.00%

EXPORT_SYMBOL(arch_write_unlock);

Overall Contributors

PersonTokensPropCommitsCommitProp
Chris Metcalf86499.65%375.00%
Roel Kluin30.35%125.00%
Total867100.00%4100.00%
Directory: arch/tile/lib
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.