示例#1
0
void* cas_thread( object_t thread, void* arg )
{
    int loop = 0;
    cas_value_t val = *(cas_value_t*)arg;

    thread_sleep( 10 );

    while( !thread_should_terminate( thread ) && ( loop < 65535 ) )
    {
        while( !atomic_cas32( &val_32, val.val_32, 0 ) )
            thread_yield();
        while( !atomic_cas32( &val_32, 0, val.val_32 ) )
            thread_yield();
        while( !atomic_cas64( &val_64, val.val_64, 0 ) )
            thread_yield();
        while( !atomic_cas64( &val_64, 0, val.val_64 ) )
            thread_yield();
        while( !atomic_cas_ptr( &val_ptr, val.val_ptr, 0 ) )
            thread_yield();
        while( !atomic_cas_ptr( &val_ptr, 0, val.val_ptr ) )
            thread_yield();

        ++loop;
        thread_yield();
    }
    return 0;
}
示例#2
0
static void*
cas_thread(void* arg) {
	unsigned int loop = 0;
	cas_value_t val = *(cas_value_t*)arg;

	thread_sleep(10);

	while (!thread_try_wait(0) && (loop < 65535)) {
		while (!atomic_cas32(&val_32, val.val_32, 0))
			thread_yield();
		while (!atomic_cas32(&val_32, 0, val.val_32))
			thread_yield();
		while (!atomic_cas64(&val_64, val.val_64, 0))
			thread_yield();
		while (!atomic_cas64(&val_64, 0, val.val_64))
			thread_yield();
		while (!atomic_cas_ptr(&val_ptr, val.val_ptr, 0))
			thread_yield();
		while (!atomic_cas_ptr(&val_ptr, 0, val.val_ptr))
			thread_yield();

		++loop;
		thread_yield();
	}
	return 0;
}
示例#3
0
        //  Perform atomic 'compare and swap' operation on the pointer.
        //  The pointer is compared to 'cmp' argument and if they are
        //  equal, its value is set to 'val'. Old value of the pointer
        //  is returned.
        inline T *cas (T *cmp_, T *val_)
        {
#if defined XS_ATOMIC_PTR_WINDOWS
            return (T*) InterlockedCompareExchangePointer (
                (volatile PVOID*) &ptr, val_, cmp_);
#elif defined XS_ATOMIC_PTR_ATOMIC_H
            return (T*) atomic_cas_ptr (&ptr, cmp_, val_);
#elif defined XS_ATOMIC_PTR_X86
            T *old;
            __asm__ volatile (
                "lock; cmpxchg %2, %3"
                : "=a" (old), "=m" (ptr)
                : "r" (val_), "m" (ptr), "0" (cmp_)
                : "cc");
            return old;
#elif defined XS_ATOMIC_PTR_MUTEX
            sync.lock ();
            T *old = (T*) ptr;
            if (ptr == cmp_)
                ptr = val_;
            sync.unlock ();
            return old;
#else
#error atomic_ptr is not implemented for this platform
#endif
        }
示例#4
0
int
mtx_enter_try(struct mutex *mtx)
{
	struct cpu_info *owner, *ci = curcpu();
	int s;
	
 	if (mtx->mtx_wantipl != IPL_NONE)
		s = splraise(mtx->mtx_wantipl);

	owner = atomic_cas_ptr(&mtx->mtx_owner, NULL, ci);
#ifdef DIAGNOSTIC
	if (__predict_false(owner == ci))
		panic("mtx %p: locking against myself", mtx);
#endif
	if (owner == NULL) {
		if (mtx->mtx_wantipl != IPL_NONE)
			mtx->mtx_oldipl = s;
#ifdef DIAGNOSTIC
		ci->ci_mutex_level++;
#endif
		membar_enter();
		return (1);
	}

	if (mtx->mtx_wantipl != IPL_NONE)
		splx(s);

	return (0);
}
示例#5
0
文件: mcs.c 项目: 7perl/akaros
/* Using the CAS style unlocks, since the usurper recovery is a real pain in the
 * ass */
void __mcs_pdro_unlock(struct mcs_pdro_lock *lock, struct mcs_pdro_qnode *qnode)
{
	uint32_t a_tail_vcoreid;
	/* Check if someone is already waiting on us to unlock */
	if (qnode->next == 0) {
		cmb();	/* no need for CPU mbs, since there's an atomic_cas() */
		/* If we're still the lock, just swap it with 0 (unlock) and return */
		if (atomic_cas_ptr((void**)&lock->lock, qnode, 0))
			return;
		/* Read in the tail (or someone who recently was the tail, but could now
		 * be farther up the chain), in prep for our spinning. */
		a_tail_vcoreid = ACCESS_ONCE(lock->lock->vcoreid);
		/* We failed, someone is there and we are some (maybe a different)
		 * thread's pred.  Since someone else was waiting, they should have made
		 * themselves our next.  Spin (very briefly!) til it happens. */
		while (qnode->next == 0) {
			/* We need to get our next to run, but we don't know who they are.
			 * If we make sure a tail is running, that will percolate up to make
			 * sure our qnode->next is running */
			ensure_vcore_runs(a_tail_vcoreid);
			cpu_relax();
		}
		/* Alpha wants a read_barrier_depends() here */
		/* Now that we have a next, unlock them */
		qnode->next->locked = 0;
	} else {
		/* mb()s necessary since we didn't call an atomic_swap() */
		wmb();	/* need to make sure any previous writes don't pass unlocking */
		rwmb();	/* need to make sure any reads happen before the unlocking */
		/* simply unlock whoever is next */
		qnode->next->locked = 0;
	}
}
示例#6
0
static inline uintptr_t
rw_cas(pthread_rwlock_t *ptr, uintptr_t o, uintptr_t n)
{

	return (uintptr_t)atomic_cas_ptr(&ptr->ptr_owner, (void *)o,
	    (void *)n);
}
示例#7
0
inline bool compare_and_swap(volatile PtrT *ptr, const ValueT& comp_val, const ValueT& exchange_val)
{
#if ELEVELDB_IS_SOLARIS
    return (comp_val==atomic_cas_ptr(ptr, comp_val, exchange_val));
#else
    return __sync_bool_compare_and_swap(ptr, comp_val, exchange_val);
#endif
}
static inline uintptr_t
rw_cas(krwlock_t *rw, uintptr_t o, uintptr_t n)
{

	RW_INHERITDEBUG(n, o);
	return (uintptr_t)atomic_cas_ptr((volatile void *)&rw->rw_owner,
	    (void *)o, (void *)n);
}
        //  Perform atomic 'compare and swap' operation on the pointer.
        //  The pointer is compared to 'cmp' argument and if they are
        //  equal, its value is set to 'val'. Old value of the pointer
        //  is returned.
        inline T *cas (T *cmp_, T *val_)
        {
#if defined ZMQ_ATOMIC_PTR_WINDOWS
            return (T*) InterlockedCompareExchangePointer (
                (volatile PVOID*) &ptr, val_, cmp_);
#elif defined ZMQ_ATOMIC_PTR_INTRINSIC
            T *old = cmp_;
            __atomic_compare_exchange_n (&ptr, (volatile T**) &old, val_, false,
                    __ATOMIC_RELEASE, __ATOMIC_ACQUIRE);
            return old;
#elif defined ZMQ_ATOMIC_PTR_CXX11
            ptr.compare_exchange_strong(cmp_, val_, std::memory_order_acq_rel);
            return cmp_;
#elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
            return (T*) atomic_cas_ptr (&ptr, cmp_, val_);
#elif defined ZMQ_ATOMIC_PTR_TILE
            return (T*) arch_atomic_val_compare_and_exchange (&ptr, cmp_, val_);
#elif defined ZMQ_ATOMIC_PTR_X86
            T *old;
            __asm__ volatile (
                "lock; cmpxchg %2, %3"
                : "=a" (old), "=m" (ptr)
                : "r" (val_), "m" (ptr), "0" (cmp_)
                : "cc");
            return old;
#elif defined ZMQ_ATOMIC_PTR_ARM
            T *old;
            unsigned int flag;
            __asm__ volatile (
                "       dmb     sy\n\t"
                "1:     ldrex   %1, [%3]\n\t"
                "       mov     %0, #0\n\t"
                "       teq     %1, %4\n\t"
                "       it      eq\n\t"
                "       strexeq %0, %5, [%3]\n\t"
                "       teq     %0, #0\n\t"
                "       bne     1b\n\t"
                "       dmb     sy\n\t"
                : "=&r"(flag), "=&r"(old), "+Qo"(ptr)
                : "r"(&ptr), "r"(cmp_), "r"(val_)
                : "cc");
            return old;
#elif defined ZMQ_ATOMIC_PTR_MUTEX
            sync.lock ();
            T *old = (T*) ptr;
            if (ptr == cmp_)
                ptr = val_;
            sync.unlock ();
            return old;
#else
#error atomic_ptr is not implemented for this platform
#endif
        }
示例#10
0
static void
rw_panic(char *msg, rwlock_impl_t *lp)
{
	if (panicstr)
		return;

	if (atomic_cas_ptr(&panic_rwlock_addr, NULL, lp) == NULL)
		panic_rwlock = *lp;

	panic("%s, lp=%p wwwh=%lx thread=%p",
	    msg, (void *)lp, panic_rwlock.rw_wwwh, (void *)curthread);
}
示例#11
0
void BLI_linklist_lockfree_insert(LockfreeLinkList *list, LockfreeLinkNode *node)
{
  /* Based on:
   *
   * John D. Valois
   * Implementing Lock-Free Queues
   *
   * http://people.csail.mit.edu/bushl2/rpi/portfolio/lockfree-grape/documents/lock-free-linked-lists.pdf
   */
  bool keep_working;
  LockfreeLinkNode *tail_node;
  node->next = NULL;
  do {
    tail_node = list->tail;
    keep_working = (atomic_cas_ptr((void **)&tail_node->next, NULL, node) != NULL);
    if (keep_working) {
      atomic_cas_ptr((void **)&list->tail, tail_node, tail_node->next);
    }
  } while (keep_working);
  atomic_cas_ptr((void **)&list->tail, tail_node, node);
}
示例#12
0
文件: ksyms.c 项目: bahamas10/openzfs
/* ARGSUSED */
static int
ksyms_open(dev_t *devp, int flag, int otyp, struct cred *cred)
{
	minor_t clone;
	size_t size = 0;
	size_t realsize;
	char *addr;
	void *hptr = NULL;
	ksyms_buflist_hdr_t hdr;
	bzero(&hdr, sizeof (struct ksyms_buflist_hdr));
	list_create(&hdr.blist, PAGESIZE,
	    offsetof(ksyms_buflist_t, buflist_node));

	if (getminor(*devp) != 0)
		return (ENXIO);

	for (;;) {
		realsize = ksyms_snapshot(ksyms_bcopy, hptr, size);
		if (realsize <= size)
			break;
		size = realsize;
		size = ksyms_buflist_alloc(&hdr, size);
		if (size == 0)
			return (ENOMEM);
		hptr = (void *)&hdr;
	}

	addr = ksyms_mapin(&hdr, realsize);
	ksyms_buflist_free(&hdr);
	if (addr == NULL)
		return (EOVERFLOW);

	/*
	 * Reserve a clone entry.  Note that we don't use clone 0
	 * since that's the "real" minor number.
	 */
	for (clone = 1; clone < nksyms_clones; clone++) {
		if (atomic_cas_ptr(&ksyms_clones[clone].ksyms_base, 0, addr) ==
		    0) {
			ksyms_clones[clone].ksyms_size = realsize;
			*devp = makedevice(getemajor(*devp), clone);
			(void) ddi_prop_update_int(*devp, ksyms_devi,
			    "size", realsize);
			modunload_disable();
			return (0);
		}
	}
	cmn_err(CE_NOTE, "ksyms: too many open references");
	(void) as_unmap(curproc->p_as, addr, roundup(realsize, PAGESIZE));
	return (ENXIO);
}
示例#13
0
/*
 * cms_init entry point.
 *
 * This module provides broad model-specific support for AMD families
 * 0x6, 0xf and 0x10.  Future families will have to be evaluated once their
 * documentation is available.
 */
int
authamd_init(cmi_hdl_t hdl, void **datap)
{
	uint_t chipid = cmi_hdl_chipid(hdl);
	uint_t procnodeid = cmi_hdl_procnodeid(hdl);
	struct authamd_nodeshared *sp, *osp;
	uint_t family = cmi_hdl_family(hdl);
	uint32_t rev = cmi_hdl_chiprev(hdl);
	authamd_data_t *authamd;
	uint64_t cap;

	if (authamd_ms_support_disable ||
	    !authamd_supported(hdl))
		return (ENOTSUP);

	if (!is_x86_feature(x86_featureset, X86FSET_MCA))
		return (ENOTSUP);

	if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS)
		return (ENOTSUP);

	if (!(cap & MCG_CAP_CTL_P))
		return (ENOTSUP);

	authamd = *datap = kmem_zalloc(sizeof (authamd_data_t), KM_SLEEP);
	cmi_hdl_hold(hdl);	/* release in fini */
	authamd->amd_hdl = hdl;

	if ((sp = authamd_shared[procnodeid]) == NULL) {
		sp = kmem_zalloc(sizeof (struct authamd_nodeshared), KM_SLEEP);
		sp->ans_chipid = chipid;
		sp->ans_procnodeid = procnodeid;
		sp->ans_family = family;
		sp->ans_rev = rev;
		membar_producer();

		osp = atomic_cas_ptr(&authamd_shared[procnodeid], NULL, sp);
		if (osp != NULL) {
			kmem_free(sp, sizeof (struct authamd_nodeshared));
			sp = osp;
		}
	}
	authamd->amd_shared = sp;

	return (0);
}
示例#14
0
文件: mcs.c 项目: 7perl/akaros
void __mcs_pdr_unlock(struct mcs_pdr_lock *lock, struct mcs_pdr_qnode *qnode)
{
	uint32_t a_tail_vcoreid;
	struct mcs_pdr_qnode *qnode0 = qnode - vcore_id();
	/* Check if someone is already waiting on us to unlock */
	if (qnode->next == 0) {
		cmb();	/* no need for CPU mbs, since there's an atomic_cas() */
		/* If we're still the lock, just swap it with 0 (unlock) and return */
		if (atomic_cas_ptr((void**)&lock->lock, qnode, 0)) {
			/* This is racy with the new lockholder.  it's possible that we'll
			 * clobber their legit write, though it doesn't actually hurt
			 * correctness.  it'll get sorted out on the next unlock. */
			lock->lockholder_vcoreid = MCSPDR_NO_LOCKHOLDER;
			return;
		}
		/* Get the tail (or someone who recently was the tail, but could now
		 * be farther up the chain), in prep for our spinning.  Could do an
		 * ACCESS_ONCE on lock->lock */
		a_tail_vcoreid = lock->lock - qnode0;
		/* We failed, someone is there and we are some (maybe a different)
		 * thread's pred.  Since someone else was waiting, they should have made
		 * themselves our next.  Spin (very briefly!) til it happens. */
		while (qnode->next == 0) {
			/* We need to get our next to run, but we don't know who they are.
			 * If we make sure a tail is running, that will percolate up to make
			 * sure our qnode->next is running */
			ensure_vcore_runs(a_tail_vcoreid);
			cpu_relax();
		}
		/* Alpha wants a read_barrier_depends() here */
		lock->lockholder_vcoreid = qnode->next - qnode0;
		wmb();	/* order the vcoreid write before the unlock */
		qnode->next->locked = 0;
	} else {
		/* Note we're saying someone else is the lockholder, though we still are
		 * the lockholder until we unlock the next qnode.  Our next knows that
		 * if it sees itself is the lockholder, that it needs to make sure we
		 * run. */
		lock->lockholder_vcoreid = qnode->next - qnode0;
		/* mb()s necessary since we didn't call an atomic_swap() */
		wmb();	/* need to make sure any previous writes don't pass unlocking */
		rwmb();	/* need to make sure any reads happen before the unlocking */
		/* simply unlock whoever is next */
		qnode->next->locked = 0;
	}
}
示例#15
0
void*
_allocate_thread_local_block(size_t size) {
	void* block = memory_allocate(0, size, 0, MEMORY_PERSISTENT | MEMORY_ZERO_INITIALIZED);

	for (int i = 0; i < 1024; ++i) {
		if (!atomic_loadptr(&_thread_local_blocks[i].block)) {
			if (atomic_cas_ptr(&_thread_local_blocks[i].block, block, 0)) {
				_thread_local_blocks[i].thread = thread_id();
				return block;
			}
		}
	}

	log_warnf(0, WARNING_MEMORY,
	          STRING_CONST("Unable to locate thread local memory block slot, will leak %" PRIsize " bytes"),
	          size);
	return block;
}
示例#16
0
static evchan_t *
bind_channel(boolean_t priv, fmev_pri_t pri)
{
	evchan_t **evcpp = &CHAN_BINDING(priv, pri);
	evchan_t *evc;

	if (*evcpp != NULL)
		return (*evcpp);

	if (sysevent_evc_bind(CHAN_NAME(priv, pri), &evc,
	    EVCH_CREAT | CHAN_FLAGS(priv, pri)) != 0)
		return (NULL);

	if (atomic_cas_ptr(evcpp, NULL, evc) != NULL)
		(void) sysevent_evc_unbind(evc);

	return (*evcpp);
}
/*
 * Fetch a /dev/u?random context's CPRNG, or create and save one if
 * necessary.
 */
static struct cprng_strong *
rnd_ctx_cprng(struct rnd_ctx *ctx)
{
	struct cprng_strong *cprng, *tmp = NULL;

	/* Fast path: if someone has already allocated a CPRNG, use it.  */
	cprng = ctx->rc_cprng;
	if (__predict_true(cprng != NULL)) {
		/* Make sure the CPU hasn't prefetched cprng's guts.  */
		membar_consumer();
		goto out;
	}

	/* Slow path: create a CPRNG.  Allocate before taking locks.  */
	char name[64];
	struct lwp *const l = curlwp;
	(void)snprintf(name, sizeof(name), "%d %"PRIu64" %u",
	    (int)l->l_proc->p_pid, l->l_ncsw, l->l_cpticks);
	const int flags = (ctx->rc_hard? (CPRNG_USE_CV | CPRNG_HARD) :
	    (CPRNG_INIT_ANY | CPRNG_REKEY_ANY));
	tmp = cprng_strong_create(name, IPL_NONE, flags);

	/* Publish cprng's guts before the pointer to them.  */
	membar_producer();

	/* Attempt to publish tmp, unless someone beat us.  */
	cprng = atomic_cas_ptr(&ctx->rc_cprng, NULL, tmp);
	if (__predict_false(cprng != NULL)) {
		/* Make sure the CPU hasn't prefetched cprng's guts.  */
		membar_consumer();
		goto out;
	}

	/* Published.  Commit tmp.  */
	cprng = tmp;
	tmp = NULL;

out:	if (tmp != NULL)
		cprng_strong_destroy(tmp);
	KASSERT(cprng != NULL);
	return cprng;
}
示例#18
0
static void _memory_tracker_track( void* addr, uint64_t size )
{
	if( addr ) do
	{
		int32_t tag = atomic_exchange_and_add32( &_memory_tag_next, 1 );
		if( tag >= MAX_CONCURRENT_ALLOCATIONS )
		{
			int32_t newtag = tag % MAX_CONCURRENT_ALLOCATIONS;
			atomic_cas32( &_memory_tag_next, newtag, tag + 1 );
			tag = newtag;
		}
		if( !_memory_tags[ tag ].address && atomic_cas_ptr( &_memory_tags[ tag ].address, addr, 0 ) )
		{
			_memory_tags[ tag ].size = (uintptr_t)size;
			stacktrace_capture( _memory_tags[ tag ].trace, 14, 3 );
			hashtable_set( _memory_table, (uintptr_t)addr, (uintptr_t)( tag + 1 ) );
			return;
		}
	} while( true );
}
示例#19
0
/*
 * Our cmi_init entry point, called during startup of each cpu instance.
 */
int
gcpu_init(cmi_hdl_t hdl, void **datap)
{
	uint_t chipid = cmi_hdl_chipid(hdl);
	struct gcpu_chipshared *sp, *osp;
	gcpu_data_t *gcpu;

	if (gcpu_disable || chipid >= GCPU_MAX_CHIPID)
		return (ENOTSUP);

	/*
	 * Allocate the state structure for this cpu.  We will only
	 * allocate the bank logout areas in gcpu_mca_init once we
	 * know how many banks there are.
	 */
	gcpu = *datap = kmem_zalloc(sizeof (gcpu_data_t), KM_SLEEP);
	cmi_hdl_hold(hdl);	/* release in gcpu_fini */
	gcpu->gcpu_hdl = hdl;

	/*
	 * Allocate a chipshared structure if no sibling cpu has already
	 * allocated it, but allow for the fact that a sibling core may
	 * be starting up in parallel.
	 */
	if ((sp = gcpu_shared[chipid]) == NULL) {
		sp = kmem_zalloc(sizeof (struct gcpu_chipshared), KM_SLEEP);
		osp = atomic_cas_ptr(&gcpu_shared[chipid], NULL, sp);
		if (osp == NULL) {
			mutex_init(&sp->gcpus_poll_lock, NULL, MUTEX_DRIVER,
			    NULL);
			mutex_init(&sp->gcpus_cfglock, NULL, MUTEX_DRIVER,
			    NULL);
		} else {
			kmem_free(sp, sizeof (struct gcpu_chipshared));
			sp = osp;
		}
	}
	gcpu->gcpu_shared = sp;

	return (0);
}
示例#20
0
static void*
_atomic_allocate_linear(size_t chunksize) {
	void* old_head;
	void* new_head;
	void* return_pointer = 0;

	do {
		old_head = atomic_loadptr(&_memory_temporary.head);
		new_head = pointer_offset(old_head, chunksize);

		return_pointer = old_head;

		if (new_head > _memory_temporary.end) {
			new_head = pointer_offset(_memory_temporary.storage, chunksize);
			return_pointer = _memory_temporary.storage;
		}
	}
	while (!atomic_cas_ptr(&_memory_temporary.head, new_head, old_head));

	return return_pointer;
}
示例#21
0
struct uidinfo *
uid_find(uid_t uid)
{
	struct uidinfo *uip, *uip_first, *newuip;
	struct uihashhead *uipp;

	uipp = UIHASH(uid);
	newuip = NULL;

	/*
	 * To make insertion atomic, abstraction of SLIST will be violated.
	 */
	uip_first = uipp->slh_first;
 again:
	SLIST_FOREACH(uip, uipp, ui_hash) {
		if (uip->ui_uid != uid)
			continue;
		if (newuip != NULL)
			kmem_free(newuip, sizeof(*newuip));
		return uip;
	}
	if (newuip == NULL)
		newuip = kmem_zalloc(sizeof(*newuip), KM_SLEEP);
	newuip->ui_uid = uid;

	/*
	 * If atomic insert is unsuccessful, another thread might be
	 * allocated this 'uid', thus full re-check is needed.
	 */
	newuip->ui_hash.sle_next = uip_first;
	membar_producer();
	uip = atomic_cas_ptr(&uipp->slh_first, uip_first, newuip);
	if (uip != uip_first) {
		uip_first = uip;
		goto again;
	}

	return newuip;
}
示例#22
0
void
vpanic(const char *fmt, va_list ap)
{
	CPU_INFO_ITERATOR cii;
	struct cpu_info *ci, *oci;
	int bootopt;
	static char scratchstr[256]; /* stores panic message */

	spldebug_stop();

	if (lwp0.l_cpu && curlwp) {
		/*
		 * Disable preemption.  If already panicing on another CPU, sit
		 * here and spin until the system is rebooted.  Allow the CPU that
		 * first paniced to panic again.
		 */
		kpreempt_disable();
		ci = curcpu();
		oci = atomic_cas_ptr((void *)&paniccpu, NULL, ci);
		if (oci != NULL && oci != ci) {
			/* Give interrupts a chance to try and prevent deadlock. */
			for (;;) {
#ifndef _RUMPKERNEL /* XXXpooka: temporary build fix, see kern/40505 */
				DELAY(10);
#endif /* _RUMPKERNEL */
			}
		}

		/*
		 * Convert the current thread to a bound thread and prevent all
		 * CPUs from scheduling unbound jobs.  Do so without taking any
		 * locks.
		 */
		curlwp->l_pflag |= LP_BOUND;
		for (CPU_INFO_FOREACH(cii, ci)) {
			ci->ci_schedstate.spc_flags |= SPCF_OFFLINE;
		}
	}
示例#23
0
文件: mcs.c 项目: 7perl/akaros
/* CAS style mcs locks, kept around til we use them.  We're using the
 * usurper-style, since RISCV doesn't have a real CAS (yet?). */
void mcs_lock_unlock_cas(struct mcs_lock *lock, struct mcs_lock_qnode *qnode)
{
	/* Check if someone is already waiting on us to unlock */
	if (qnode->next == 0) {
		cmb();	/* no need for CPU mbs, since there's an atomic_cas() */
		/* If we're still the lock, just swap it with 0 (unlock) and return */
		if (atomic_cas_ptr((void**)&lock->lock, qnode, 0))
			return;
		/* We failed, someone is there and we are some (maybe a different)
		 * thread's pred.  Since someone else was waiting, they should have made
		 * themselves our next.  Spin (very briefly!) til it happens. */
		while (qnode->next == 0)
			cpu_relax();
		/* Alpha wants a read_barrier_depends() here */
		/* Now that we have a next, unlock them */
		qnode->next->locked = 0;
	} else {
		/* mb()s necessary since we didn't call an atomic_swap() */
		wmb();	/* need to make sure any previous writes don't pass unlocking */
		rwmb();	/* need to make sure any reads happen before the unlocking */
		/* simply unlock whoever is next */
		qnode->next->locked = 0;
	}
}
示例#24
0
/*
 * Schedule a CPU.  This optimizes for the case where we schedule
 * the same thread often, and we have nCPU >= nFrequently-Running-Thread
 * (where CPU is virtual rump cpu, not host CPU).
 */
void
rump_schedule_cpu_interlock(struct lwp *l, void *interlock)
{
    struct rumpcpu *rcpu;
    struct cpu_info *ci;
    void *old;
    bool domigrate;
    bool bound = l->l_pflag & LP_BOUND;

    l->l_stat = LSRUN;

    /*
     * First, try fastpath: if we were the previous user of the
     * CPU, everything is in order cachewise and we can just
     * proceed to use it.
     *
     * If we are a different thread (i.e. CAS fails), we must go
     * through a memory barrier to ensure we get a truthful
     * view of the world.
     */

    KASSERT(l->l_target_cpu != NULL);
    rcpu = cpuinfo_to_rumpcpu(l->l_target_cpu);
    if (atomic_cas_ptr(&rcpu->rcpu_prevlwp, l, RCPULWP_BUSY) == l) {
        if (interlock == rcpu->rcpu_mtx)
            rumpuser_mutex_exit(rcpu->rcpu_mtx);
        SCHED_FASTPATH(rcpu);
        /* jones, you're the man */
        goto fastlane;
    }

    /*
     * Else, it's the slowpath for us.  First, determine if we
     * can migrate.
     */
    if (ncpu == 1)
        domigrate = false;
    else
        domigrate = true;

    /* Take lock.  This acts as a load barrier too. */
    if (interlock != rcpu->rcpu_mtx)
        rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);

    for (;;) {
        SCHED_SLOWPATH(rcpu);
        old = atomic_swap_ptr(&rcpu->rcpu_prevlwp, RCPULWP_WANTED);

        /* CPU is free? */
        if (old != RCPULWP_BUSY && old != RCPULWP_WANTED) {
            if (atomic_cas_ptr(&rcpu->rcpu_prevlwp,
                               RCPULWP_WANTED, RCPULWP_BUSY) == RCPULWP_WANTED) {
                break;
            }
        }

        /*
         * Do we want to migrate once?
         * This may need a slightly better algorithm, or we
         * might cache pingpong eternally for non-frequent
         * threads.
         */
        if (domigrate && !bound) {
            domigrate = false;
            SCHED_MIGRATED(rcpu);
            rumpuser_mutex_exit(rcpu->rcpu_mtx);
            rcpu = getnextcpu();
            rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);
            continue;
        }

        /* Want CPU, wait until it's released an retry */
        rcpu->rcpu_wanted++;
        rumpuser_cv_wait_nowrap(rcpu->rcpu_cv, rcpu->rcpu_mtx);
        rcpu->rcpu_wanted--;
    }
    rumpuser_mutex_exit(rcpu->rcpu_mtx);

fastlane:
    ci = rcpu->rcpu_ci;
    l->l_cpu = l->l_target_cpu = ci;
    l->l_mutex = rcpu->rcpu_ci->ci_schedstate.spc_mutex;
    l->l_ncsw++;
    l->l_stat = LSONPROC;

    /*
     * No interrupts, so ci_curlwp === cpu_onproc.
     * Okay, we could make an attempt to not set cpu_onproc
     * in the case that an interrupt is scheduled immediately
     * after a user proc, but leave that for later.
     */
    ci->ci_curlwp = ci->ci_data.cpu_onproc = l;
}
示例#25
0
void
kern_preprom(void)
{
	for (;;) {
		/*
		 * Load the current CPU pointer and examine the mutex_ready bit.
		 * It doesn't matter if we are preempted here because we are
		 * only trying to determine if we are in the *set* of mutex
		 * ready CPUs.  We cannot disable preemption until we confirm
		 * that we are running on a CPU in this set, since a call to
		 * kpreempt_disable() requires access to curthread.
		 */
		processorid_t cpuid = getprocessorid();
		cpu_t *cp = cpu[cpuid];
		cpu_t *prcp;

		if (panicstr)
			return; /* just return if we are currently panicking */

		if (CPU_IN_SET(cpu_ready_set, cpuid) && cp->cpu_m.mutex_ready) {
			/*
			 * Disable premption, and reload the current CPU.  We
			 * can't move from a mutex_ready cpu to a non-ready cpu
			 * so we don't need to re-check cp->cpu_m.mutex_ready.
			 */
			kpreempt_disable();
			cp = CPU;
			ASSERT(cp->cpu_m.mutex_ready);

			/*
			 * Try the lock.  If we don't get the lock, re-enable
			 * preemption and see if we should sleep.  If we are
			 * already the lock holder, remove the effect of the
			 * previous kpreempt_disable() before returning since
			 * preemption was disabled by an earlier kern_preprom.
			 */
			prcp = atomic_cas_ptr((void *)&prom_cpu, NULL, cp);
			if (prcp == NULL ||
			    (prcp == cp && prom_thread == curthread)) {
				if (prcp == cp)
					kpreempt_enable();
				break;
			}

			kpreempt_enable();

			/*
			 * We have to be very careful here since both prom_cpu
			 * and prcp->cpu_m.mutex_ready can be changed at any
			 * time by a non mutex_ready cpu holding the lock.
			 * If the owner is mutex_ready, holding prom_mutex
			 * prevents kern_postprom() from completing.  If the
			 * owner isn't mutex_ready, we only know it will clear
			 * prom_cpu before changing cpu_m.mutex_ready, so we
			 * issue a membar after checking mutex_ready and then
			 * re-verify that prom_cpu is still held by the same
			 * cpu before actually proceeding to cv_wait().
			 */
			mutex_enter(&prom_mutex);
			prcp = prom_cpu;
			if (prcp != NULL && prcp->cpu_m.mutex_ready != 0) {
				membar_consumer();
				if (prcp == prom_cpu)
					cv_wait(&prom_cv, &prom_mutex);
			}
			mutex_exit(&prom_mutex);

		} else {
			/*
			 * If we are not yet mutex_ready, just attempt to grab
			 * the lock.  If we get it or already hold it, break.
			 */
			ASSERT(getpil() == PIL_MAX);
			prcp = atomic_cas_ptr((void *)&prom_cpu, NULL, cp);
			if (prcp == NULL || prcp == cp)
				break;
		}
	}

	/*
	 * We now hold the prom_cpu lock.  Increment the hold count by one
	 * and assert our current state before returning to the caller.
	 */
	atomic_inc_32(&prom_holdcnt);
	ASSERT(prom_holdcnt >= 1);
	prom_thread = curthread;
}
示例#26
0
void* atomicCompareExchange(void* volatile& val, void* exch, void* comp)
{
    return atomic_cas_ptr(&val, comp, exch);
}
示例#27
0
/**
 * Set new data on a thread-safe global variable
 *
 * @param [in] var Pointer to thread-safe global variable
 * @param [in] cfdata New value for the thread-safe global variable
 * @param [out] new_version New version number
 *
 * @return 0 on success, or a system error such as ENOMEM.
 */
int
pthread_var_set_np(pthread_var_np_t vp, void *cfdata,
                     uint64_t *new_version)
{
    int err;
    size_t i;
    struct var *v;
    struct vwrapper *old_wrapper = NULL;
    struct vwrapper *wrapper;
    struct vwrapper *tmp;
    uint64_t vers;
    uint64_t tmp_version;
    uint64_t nref;

    if (cfdata == NULL)
        return EINVAL;

    if (new_version == NULL)
        new_version = &vers;

    *new_version = 0;

    /* Build a wrapper for the new value */
    if ((wrapper = calloc(1, sizeof(*wrapper))) == NULL)
        return errno;

    /*
     * The var itself holds a reference to the current value, thus its
     * nref starts at 1, but that is made so further below.
     */
    wrapper->dtor = vp->dtor;
    wrapper->nref = 0;
    wrapper->ptr = cfdata;

    if ((err = pthread_mutex_lock(&vp->write_lock)) != 0) {
        free(wrapper);
        return err;
    }

    /* vp->next_version is stable because we hold the write_lock */
    *new_version = wrapper->version = atomic_read_64(&vp->next_version);

    /* Grab the next slot */
    v = vp->vars[(*new_version + 1) & 0x1].other;
    old_wrapper = atomic_read_ptr((volatile void **)&v->wrapper);

    if (*new_version == 0) {
        /* This is the first write; set wrapper on both slots */

        for (i = 0; i < sizeof(vp->vars)/sizeof(vp->vars[0]); i++) {
            v = &vp->vars[i];
            nref = atomic_inc_32_nv(&wrapper->nref);
            v->version = 0;
            tmp = atomic_cas_ptr((volatile void **)&v->wrapper,
                                 old_wrapper, wrapper);
            assert(tmp == old_wrapper && tmp == NULL);
        }

        assert(nref > 1);

        tmp_version = atomic_inc_64_nv(&vp->next_version);
        assert(tmp_version == 1);

        /* Signal waiters */
        (void) pthread_mutex_lock(&vp->waiter_lock);
        (void) pthread_cond_signal(&vp->waiter_cv); /* no thundering herd */
        (void) pthread_mutex_unlock(&vp->waiter_lock);
        return pthread_mutex_unlock(&vp->write_lock);
    }

    nref = atomic_inc_32_nv(&wrapper->nref);
    assert(nref == 1);

    assert(old_wrapper != NULL && old_wrapper->nref > 0);

    /* Wait until that slot is quiescent before mutating it */
    if ((err = pthread_mutex_lock(&vp->cv_lock)) != 0) {
        (void) pthread_mutex_unlock(&vp->write_lock);
        free(wrapper);
        return err;
    }
    while (atomic_read_32(&v->nreaders) > 0) {
        /*
         * We have a separate lock for writing vs. waiting so that no
         * other writer can steal our march.  All writers will enter,
         * all writers will finish.  We got here by winning the race for
         * the writer lock, so we'll hold onto it, and thus avoid having
         * to restart here.
         */
        if ((err = pthread_cond_wait(&vp->cv, &vp->cv_lock)) != 0) {
            (void) pthread_mutex_unlock(&vp->cv_lock);
            (void) pthread_mutex_unlock(&vp->write_lock);
            free(wrapper);
            return err;
        }
    }
    if ((err = pthread_mutex_unlock(&vp->cv_lock)) != 0) {
        (void) pthread_mutex_unlock(&vp->write_lock);
        free(wrapper);
        return err;
    }

    /* Update that now quiescent slot; these are the release operations */
    tmp = atomic_cas_ptr((volatile void **)&v->wrapper, old_wrapper, wrapper);
    assert(tmp == old_wrapper);
    v->version = *new_version;
    tmp_version = atomic_inc_64_nv(&vp->next_version);
    assert(tmp_version == *new_version + 1);
    assert(v->version > v->other->version);

    /* Release the old cf */
    assert(old_wrapper != NULL && old_wrapper->nref > 0);
    wrapper_free(old_wrapper);

    /* Done */
    return pthread_mutex_unlock(&vp->write_lock);
}
示例#28
0
/* Lock-less utility to grow the logical slot array */
static int
grow_slots(pthread_var_np_t vp, uint32_t slot_idx, int tries)
{
    uint32_t nslots = 0;
    uint32_t additions;
    uint32_t i;
    volatile struct slots **slotsp;
    struct slots *new_slots;

    for (slotsp = &vp->slots;
         atomic_read_ptr((volatile void **)slotsp) != NULL;
         slotsp = &((struct slots *)atomic_read_ptr((volatile void **)slotsp))->next)
        nslots += (*slotsp)->slot_count;

    if (nslots > slot_idx)
        return 0;

    if (tries < 1)
        return EAGAIN; /* shouldn't happen; XXX assert? */

    if ((new_slots = calloc(1, sizeof(*new_slots))) == NULL)
        return errno;

    additions = (nslots == 0) ? 4 : nslots + nslots / 2;
    while (slot_idx >= nslots + additions) {
        /*
         * In this case we're racing with other readers to grow the slot
         * list, but if we lose the race then our slot_idx may not be
         * covered in the list as grown by the winner.  We may have to
         * try again.
         *
         * There's always a winner, so eventually we won't need to try
         * again.
         */
        additions += additions + additions / 2;
        tries++;
    }
    assert(slot_idx - nslots < additions);

    new_slots->slot_array = calloc(additions, sizeof(*new_slots->slot_array));
    if (new_slots->slot_array == NULL) {
        free(new_slots);
        return errno;
    }
    new_slots->slot_count = additions;
    new_slots->slot_base = nslots;
    for (i = 0; i < additions; i++) {
        new_slots->slot_array[i].in_use = 0;
        new_slots->slot_array[i].value = 0;
        new_slots->slot_array[i].vp = vp;
    }

    /* Reserve the slot we wanted (new slots not added yet) */
    atomic_write_32(&new_slots->slot_array[slot_idx - nslots].in_use, 1);

    /* Add new slots to logical array of slots */
    if (atomic_cas_ptr((volatile void **)slotsp, NULL, new_slots) != NULL) {
        /*
         * We lost the race to grow the array.  The index we wanted is
         * not guaranteed to be covered by the array as grown by the
         * winner.  We fall through to recurse to repeat.
         *
         * See commentary above where tries is incremented.
         */
        free(new_slots->slot_array);
        free(new_slots);
    }

    /*
     * If we won the race to grow the array then the index we wanted is
     * guaranteed to be present and recursing here is cheap.  If we lost
     * the race we need to retry.  We could goto the top of the function
     * though, just in case there's no tail call optimization.
     */
    return grow_slots(vp, slot_idx, tries - 1);
}
示例#29
0
/*
 * Add a global or local attribute to a Multidata.  Global attribute
 * association is specified by a NULL packet descriptor.
 */
pattr_t *
mmd_addpattr(multidata_t *mmd, pdesc_t *pd, pattrinfo_t *pai,
    boolean_t persistent, int kmflags)
{
	patbkt_t **tbl_p;
	patbkt_t *tbl, *o_tbl;
	patbkt_t *bkt;
	pattr_t *pa;
	uint_t size;

	ASSERT(mmd != NULL);
	ASSERT(mmd->mmd_magic == MULTIDATA_MAGIC);
	ASSERT(pd == NULL || pd->pd_magic == PDESC_MAGIC);
	ASSERT(pai != NULL);

	/* pointer to the attribute hash table (local or global) */
	tbl_p = pd != NULL ? &(pd->pd_pattbl) : &(mmd->mmd_pattbl);

	/*
	 * See if the hash table has not yet been created; if so,
	 * we create the table and store its address atomically.
	 */
	if ((tbl = *tbl_p) == NULL) {
		tbl = kmem_cache_alloc(pattbl_cache, kmflags);
		if (tbl == NULL)
			return (NULL);

		/* if someone got there first, use his table instead */
		if ((o_tbl = atomic_cas_ptr(tbl_p, NULL, tbl)) != NULL) {
			kmem_cache_free(pattbl_cache, tbl);
			tbl = o_tbl;
		}
	}

	ASSERT(tbl->pbkt_tbl_sz > 0);
	bkt = &(tbl[PATTBL_HASH(pai->type, tbl->pbkt_tbl_sz)]);

	/* attribute of the same type already exists? */
	if ((pa = mmd_find_pattr(bkt, pai->type)) != NULL)
		return (NULL);

	size = sizeof (*pa) + pai->len;
	if ((pa = kmem_zalloc(size, kmflags)) == NULL)
		return (NULL);

	pa->pat_magic = PATTR_MAGIC;
	pa->pat_lock = &(bkt->pbkt_lock);
	pa->pat_mmd = mmd;
	pa->pat_buflen = size;
	pa->pat_type = pai->type;
	pai->buf = pai->len > 0 ? ((uchar_t *)(pa + 1)) : NULL;

	if (persistent)
		pa->pat_flags = PATTR_PERSIST;

	/* insert attribute at end of hash chain */
	mutex_enter(&(bkt->pbkt_lock));
	insque(&(pa->pat_next), bkt->pbkt_pattr_q.ql_prev);
	mutex_exit(&(bkt->pbkt_lock));

	return (pa);
}