Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 07 Dec 2000 10:50:50 -0800
From:      Julian Elischer <julian@elischer.org>
To:        Chuck Paterson <cp@bsdi.com>
Cc:        Jason Evans <jasone@canonware.com>, smp@FreeBSD.ORG, Archie@packetdesign.com
Subject:   Re: Mutex types.
Message-ID:  <3A2FDC0A.9F628C4B@elischer.org>
References:  <200012052202.eB5M2mm04439@berserker.bsdi.com>

next in thread | previous in thread | raw e-mail | index | archive | help
Chuck Paterson wrote:
> 
> }Building SIX locks on top of mutexes would IMO be the correct way to go.
> 
>         BSD/OS doesn't have low level reader/write locks because
> the lock manager had the basic functionality and we haven't got to
> the point were chasing the performance has come close to the top
> of the queue. Having said this, there is a case for actually building
> low level reader/writer locks. They can be made to as fast in the
> uncontested case as a mutex, at least on hardware like Sparc,
> Intel and Alpha. Reader/Write Locks built on top of mutices will
> be roughly half as fast as a mutex because a mutex acquire and
> release is needed for both acquiring and releasing the reader/write
> locks. For this reason I believe we should just stick with lock
> manager locks until we have a chance to build real reader write
> locks.

I am tempted to write some simple reader-writer locks (low level) 
just for the fun of it..
> 
>         There are tricks which can be done by mixing atomic counting
> and swap and compare to gate entry. I pretty clearly understand
> how this can be done, but its still unclear to me how well this
> maps onto standard counting semaphores.

> 
>         If the protection for the netgraph is encapsulated in netgraph
> specific macros we ought to be able to switch this out to whatever
> mechanism we choose when we have time to design/implement one or more
> of the alternatives.
> 
> Chuck


Since netgraph users are NOT generaly allowed to sleep we could make it
a SPIN type lock.
It would be up to the writing callers (probably netgraph Macros) to
use these to quickly do their damage, and release asap.
I think that we could almost get away with one oe two of these
per netgraph system. (As mike smith suggested). (around the boundaries
for packets in the system, and some for critical datastructures)

Just for fun..
Here's what the unoptimised pseudocode might look like.
Active writer sets the LSB in the writers count.
each waiting writer increments it by 2.

Having looked at the present mutex stuff,
I think I know how these would assemble and they 
probably wouldn't be too slow to be run on a 'per packet' basis.

These are designed to be used by a higher level framework that 
knows what to do when the request fails. It might 
sleep the thread or it might do something else and retry....

Struct foo {
	tid_t	mtx;
	int readers;
	int writers;
}

/* try get read permission.. only fails if there writers running or waiting */
request_read_try(struct foo *mtx) {
	int retval
	push_and_disable_ints();
	while (atomic_test_and_set(&mtx->mtx, MTX_UNOWNED, MTX_OWNED) == FAIL) {
		/* DO SOMETHING THAT LEAVES THE BUS FREE FOR A FEW CYCLES */
	}
	if ((retval = mtx->writers) == 0) {
		mtx->readers++;
	}
	mtx->mtx = MTX_UNOWNED;
	pop_ints();
	return(retval);	/* cif !0, caller should check for a waiting writer */
}

/* try get permission to write. Say if we already reserved a table */
request_write_try(struct foo *mtx, int first) {
	push_and_disable_ints();
	while (atomic_test_and_set(&mtx->mtx, MTX_UNOWNED, MTX_OWNED) == FAIL) {
		/* DO SOMETHING THAT LEAVES THE BUS FREE FOR A FEW CYCLES */
	}
	if ((mtx->readers == 0) && ((mtx_writers & 1) == 0) {
		if (!first) /* -2 + 1 == -1 */
			mtx->writers--; /* no longer waiting */
		else
			mtx->writers++;		
		mtx->mtx = MTX_UNOWNED;
		restore_ints(stored_flags);
		return (SUCCEEDED)
	} else {
	if (first)
		mtx->writers += 2;
	mtx->mtx = MTX_UNOWNED;
	pop_ints();
	return(FAILED);
}

release_read(struct foo mtx) {
	push_and_disable_ints();
	while (atomic_test_and_set(&mtx->mtx, MTX_UNOWNED, MTX_OWNED) == FAIL) {
		/* DO SOMETHING THAT LEAVES THE BUS FREE FOR A FEW CYCLES */
	}
	mtx->readers--;
	mtx->mtx = MTX_UNOWNED;
	pop_ints();
	return (mtx_readers) /* if 0 caller should check for sleeping writers */
}

release_write(struct foo mtx) {
	push_and_disable_ints();
	while (atomic_test_and_set(&mtx->mtx, MTX_UNOWNED, MTX_OWNED) == FAIL) {
		/* DO SOMETHING THAT LEAVES THE BUS FREE FOR A FEW CYCLES */
	}
	mtx->writers--;
	mtx->mtx = MTX_UNOWNED;
	pop_ints();
} /* caller should always check if readers or writers sleeping */

/* Give up re-trying for while, remove our reservation, */
release_pending_write(struct foo mtx) {
	push_and_disable_ints();
	while (atomic_test_and_set(&mtx->mtx, MTX_UNOWNED, MTX_OWNED) == FAIL) {
		/* DO SOMETHING THAT LEAVES THE BUS FREE FOR A FEW CYCLES */
	}
	mtx->writers -= 2;
	mtx->mtx = MTX_UNOWNED;
	pop_ints();
}

-- 
      __--_|\  Julian Elischer
     /       \ julian@elischer.org
    (   OZ    ) World tour 2000
---> X_.---._/  presently in:  Budapest
            v



To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-smp" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3A2FDC0A.9F628C4B>