[prev in list] [next in list] [prev in thread] [next in thread] 

List:       linux-kernel
Subject:    Re: [Patch] shm bug introduced with pagecache in 2.3.11
From:       Steve Lord <lord () sgi ! com>
Date:       1999-11-19 21:11:40
[Download RAW message or body]


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/

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic