On Sat,  8 Jan 2022 08:46:17 -0800
> From: Tim Murray <timmur...@google.com>
> 
> f2fs rw_semaphores work better if writers can starve readers,
> especially for the checkpoint thread, because writers are strictly
> more important than reader threads. This prevents significant priority
> inversion between low-priority readers that blocked while trying to
> acquire the read lock and a second acquisition of the write lock that
> might be blocking high priority work.
> 
> Signed-off-by: Tim Murray <timmur...@google.com>
> Signed-off-by: Jaegeuk Kim <jaeg...@kernel.org>
> ---

...

> +/*
> + * An implementation of an rwsem that is explicitly unfair to readers. This
> + * prevents priority inversion when a low-priority reader acquires the read 
> lock
> + * while sleeping on the write lock but the write lock is needed by
> + * higher-priority clients.
> + */
> +
> +struct f2fs_rwsem {
> +        struct rw_semaphore internal_rwsem;
> +        wait_queue_head_t read_waiters;
> +};

...

> +static inline void f2fs_down_read(struct f2fs_rwsem *sem)
> +{
> +     wait_event(sem->read_waiters, down_read_trylock(&sem->internal_rwsem));
> +}
> +
> +static inline int f2fs_down_read_trylock(struct f2fs_rwsem *sem)
> +{
> +     return down_read_trylock(&sem->internal_rwsem);
> +}
> +
> +static inline void f2fs_up_read(struct f2fs_rwsem *sem)
> +{
> +     up_read(&sem->internal_rwsem);
> +}
> +
> +static inline void f2fs_down_write(struct f2fs_rwsem *sem)
> +{
> +     down_write(&sem->internal_rwsem);
> +}
> +
> +static inline int f2fs_down_write_trylock(struct f2fs_rwsem *sem)
> +{
> +     return down_write_trylock(&sem->internal_rwsem);
> +}
> +
> +static inline void f2fs_up_write(struct f2fs_rwsem *sem)
> +{
> +     up_write(&sem->internal_rwsem);
> +     wake_up_all(&sem->read_waiters);
> +}
> +

Here is my two cents, the unfair rwsem derived from lock_sock(), which has
no link to rwsem.

Only for thoughts now.

Hillf

struct unfair_rwsem {
        spinlock_t      lock;
        int             owner; /* < 0 writer, > 0 readers */

        struct list_head reader, writer; /* read/write waiters */

#ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map dep_map;
#endif
};

struct unfair_rwsem_wait {
        struct list_head        node;
        struct task_struct      *task;
};

static void lock_unfair_rwsem(struct unfair_rwsem *us, int read)
{
        struct unfair_rwsem_wait wait;

        mutex_acquire(&us->dep_map, 0, 0, _RET_IP_);
        might_sleep();
        wait.task = current;
        for (;;) {
                spin_lock(&us->lock);
                if (read) {
                        if (us->owner >= 0) {
                                us->owner++;
                                spin_unlock(&us->lock);
                                return;
                        }
                        list_add_tail(&wait.node, &us->reader);
                } else {
                        if (us->owner == 0) {
                                us->owner--;
                                spin_unlock(&us->lock);
                                return;
                        }
                        list_add_tail(&wait.node, &us->writer);
                }
                set_current_state(TASK_UNINTERRUPTIBLE);
                spin_unlock(&us->lock);
                schedule();
        }
}

void down_read_unfair_rwsem(struct unfair_rwsem *us)
{
        lock_unfair_rwsem(us, 1);
}

void down_write_unfair_rwsem(struct unfair_rwsem *us)
{
        lock_unfair_rwsem(us, 0);
}

static void unlock_unfair_rwsem(struct unfair_rwsem *us, int read)
{
        struct list_head *head = NULL;
        int all = 0;

        spin_lock(&us->lock);
        if (us->owner < 0) {
                BUG_ON(read);
                us->owner++;
                BUG_ON(0 != us->owner);

                if (!list_empty(&us->writer))
                        head = &us->writer;
                else if (!list_empty(&us->reader)) {
                        head = &us->reader;
                        all = 1;
                }
        } else if (us->owner > 0) {
                BUG_ON(!read);
                BUG_ON(!list_empty(&us->reader));
                us->owner--;
                if (us->owner == 0)
                        if (!list_empty(&us->writer))
                                head = &us->writer;
        } else
                BUG_ON(1);

        mutex_release(&us->dep_map, _RET_IP_);
        if (head) {
                struct unfair_rwsem_wait *wait;
                do {
                        wait = list_first_entry(head, struct unfair_rwsem_wait, 
node);
                        list_del(&wait->node);
                        wake_up_process(wait->task);
                } while (all && !list_empty(head));
        }
        spin_unlock(&us->lock);
}

void up_write_unfair_rwsem(struct unfair_rwsem *us)
{
        unlock_unfair_rwsem(us, 0);
}

void up_read_unfair_rwsem(struct unfair_rwsem *us)
{
        unlock_unfair_rwsem(us, 1);
}



_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to