Files
scoutfs/kmod/src/per_task.c
Zach Brown cfa563a4a4 scoutfs: expand the per_task API
This adds some minor functionality to the per_task API for use by the
upcoming offline waiting work.

Add scoutfs_per_task_add_excl() so that a caller can tell if their task
was already put on a per-task list by their caller.

Make scoutfs_per_task_del() return a bool to indicate if the entry was
found on a list and was in fact deleted, or not.

Add scoutfs_per_task_init_entry() for initializing entries that aren't
declared on the stack.

Signed-off-by: Zach Brown <zab@versity.com>
2019-05-21 11:33:26 -07:00

112 lines
2.8 KiB
C

/*
* Copyright (C) 2017 Versity Software, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include "per_task.h"
/*
* There are times when we'd like to pass data from a caller to its
* callee but we're bouncing through functions and callbacks that don't
* provide per-task storage. We add a trivial little locked list that
* lets a caller store a pointer for callees. The lists are put in the
* scope of the sharing so the contention is rare and limited to real
* concurrency -- imagine, for example, concurrent file reading on an
* inode.
*/
/*
* Return the pointer that our caller added for us on the given list.
* The expected promise is that the pointer is valid until we return to
* the caller who will remove it from the list.
*/
void *scoutfs_per_task_get(struct scoutfs_per_task *pt)
{
const struct task_struct *task = current;
struct scoutfs_per_task_entry *ent;
void *ret = NULL;
spin_lock(&pt->lock);
list_for_each_entry(ent, &pt->list, head) {
if (ent->task == task){
ret = ent->ptr;
break;
}
}
spin_unlock(&pt->lock);
return ret;
}
void scoutfs_per_task_add(struct scoutfs_per_task *pt,
struct scoutfs_per_task_entry *ent, void *ptr)
{
ent->task = current;
ent->ptr = ptr;
spin_lock(&pt->lock);
list_add(&ent->head, &pt->list);
spin_unlock(&pt->lock);
}
/*
* Add the entry to the per-task list if the task didn't already have an
* entry on the list. Returns true if the entry was added, false if it
* wasn't.
*/
bool scoutfs_per_task_add_excl(struct scoutfs_per_task *pt,
struct scoutfs_per_task_entry *ent, void *ptr)
{
if (!scoutfs_per_task_get(pt)) {
scoutfs_per_task_add(pt, ent, ptr);
return true;
}
return false;
}
/*
* Return true if the entry was found on the list and was deleted,
* returns false if the entry wasn't present on a list.
*/
bool scoutfs_per_task_del(struct scoutfs_per_task *pt,
struct scoutfs_per_task_entry *ent)
{
BUG_ON(!list_empty(&ent->head) && ent->task != current);
if (!list_empty(&ent->head)) {
spin_lock(&pt->lock);
list_del_init(&ent->head);
spin_unlock(&pt->lock);
return true;
}
return false;
}
void scoutfs_per_task_init(struct scoutfs_per_task *pt)
{
spin_lock_init(&pt->lock);
INIT_LIST_HEAD(&pt->list);
}
void scoutfs_per_task_init_entry(struct scoutfs_per_task_entry *ent)
{
INIT_LIST_HEAD(&ent->head);
ent->task = NULL;
ent->ptr = NULL;
}