Re: [Patch] shm bug introduced with pagecache in 2.3.11

Steve Lord (lord@sgi.com)
Fri, 19 Nov 1999 15:11:40 -0600


So presuming that read/write semaphores means a sleeping read/write lock,
here is a starting point from the XFS port to Linux, excuse the type names.
These can obviously be made more efficient, this is left as an execise
for the reader. Probably overkill for the general case, but....

Steve

#define MR_ACCESS 1
#define MR_UPDATE 2

typedef struct mrlock_s {
int mr_count;
uint mr_reads_waiting;
uint mr_writes_waiting;
wait_queue_head_t mr_readerq;
wait_queue_head_t mr_writerq;
spinlock_t mr_lock;
} mrlock_t;

void
mrlock_init(mrlock_t *mrp)
{
mrp->mr_count = 0;
mrp->mr_reads_waiting = 0;
mrp->mr_writes_waiting = 0;
init_waitqueue_head(&mrp->mr_readerq);
init_waitqueue_head(&mrp->mr_writerq);
mrp->mr_lock = SPIN_LOCK_UNLOCKED;
}

/*
* Macros to lock/unlock the mrlock_t. If they are ever needed in an
* interrupt thread, add irq stuff.
*/

#define MRLOCK(m) spin_lock(&(m)->mr_lock);
#define MRUNLOCK(m) spin_unlock(&(m)->mr_lock);

/*
* lock_wait should never be called in an interrupt thread.
*
* mrlocks can sleep (i.e. call schedule) and so they can't ever
* be called from an interrupt thread.
*/

/* ARGSUSED */
void
lock_wait(wait_queue_head_t *q, spinlock_t *lock, int rw)
{
DECLARE_WAITQUEUE( wait, current );
long flags;

if (rw) {
set_current_state(TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE);
} else {
set_current_state(TASK_UNINTERRUPTIBLE);
}

wq_write_lock_irqsave(&q->lock, flags);
if (rw) {
__add_wait_queue_tail(q, &wait);
} else {
__add_wait_queue(q, &wait);
}

wq_write_unlock(&q->lock);

spin_unlock_irqrestore(lock, flags);

schedule();

set_current_state(TASK_RUNNING);

wq_write_lock_irqsave(&q->lock, flags);
__remove_wait_queue(q, &wait);
wq_write_unlock_irqrestore(&q->lock, flags);

spin_lock(lock);
}

void
mrlock(mrlock_t *mrp, int type)
{
if (type == MR_ACCESS)
mraccess(mrp);
else
mrupdate(mrp);
}

void
mraccess(mrlock_t *mrp)
{
if(mrp->mr_writes_waiting > 0) {
mrp->mr_reads_waiting++;
lock_wait(&mrp->mr_readerq, &mrp->mr_lock, 0);
mrp->mr_reads_waiting--;
}
while (mrp->mr_count < 0) {
mrp->mr_reads_waiting++;
lock_wait(&mrp->mr_readerq, &mrp->mr_lock, 0);
mrp->mr_reads_waiting--;
}
mrp->mr_count++;
MRUNLOCK(mrp);
}

void
mrupdatef(mrlock_t *mrp)
MRLOCK(mrp);
while(mrp->mr_count) {
mrp->mr_writes_waiting++;
lock_wait(&mrp->mr_writerq, &mrp->mr_lock, 1);
mrp->mr_writes_waiting--;
}

mrp->mr_count = -1; /* writer on it */
MRUNLOCK(mrp);
}

int
mrtryaccess(mrlock_t *mrp)
{
MRLOCK(mrp);
/*
* If anyone is waiting for update access or the lock is held for
update
* fail the request.
*/
if(mrp->mr_writes_waiting > 0 || mrp->mr_count < 0) {
MRUNLOCK(mrp);
return 0;
}
mrp->mr_count++;
MRUNLOCK(mrp);
return 1;
}

int
mrtrypromote(mrlock_t *mrp)
{
MRLOCK(mrp);
if(mrp->mr_count == 1) { /* We are the only thread with the lock */
mrp->mr_count = -1; /* writer on it */
MRUNLOCK(mrp);
return 1;
}

MRUNLOCK(mrp);
return 0;
}

void
mraccunlock(mrlock_t *mrp)
{
MRLOCK(mrp);
mrp->mr_count--;
mrwake(mrp);
MRUNLOCK(mrp);
}

void
mrunlock(mrlock_t *mrp)
{
MRLOCK(mrp);
if (mrp->mr_count < 0) {
mrp->mr_count = 0;
} else {
mrp->mr_count--;
}
mrwake(mrp);
MRUNLOCK(mrp);
}

int
ismrlocked(mrlock_t *mrp, int type)
{
if (type == MR_ACCESS)
return (mrp->mr_count > 0); /* Read lock */
else if (type == MR_UPDATE)
return (mrp->mr_count < 0); /* Write lock */
else if (type == (MR_UPDATE | MR_ACCESS))
return (mrp->mr_count); /* Any type of lock held */
else /* Any waiters */
return (mrp->mr_reads_waiting | mrp->mr_writes_waiting);
}

void
mrdemote(mrlock_t *mrp)
{
MRLOCK(mrp);
mrp->mr_count = 1;
if (mrp->mr_reads_waiting) { /* Wakeup all readers waiting */
wake_up(&mrp->mr_readerq);
}
MRUNLOCK(mrp);
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/