int set_task_ioprio(struct task_struct *task, int ioprio) { #ifdef CONFIG_IOSCHED_BFQ int err, i; #else int err; #endif struct io_context *ioc; const struct cred *cred = current_cred(), *tcred; rcu_read_lock(); tcred = __task_cred(task); if (tcred->uid != cred->euid && tcred->uid != cred->uid && !capable(CAP_SYS_NICE)) { rcu_read_unlock(); return -EPERM; } rcu_read_unlock(); err = security_task_setioprio(task, ioprio); if (err) return err; task_lock(task); do { ioc = task->io_context; /* see wmb() in current_io_context() */ smp_read_barrier_depends(); if (ioc) break; ioc = alloc_io_context(GFP_ATOMIC, -1); if (!ioc) { err = -ENOMEM; break; } #ifdef CONFIG_IOSCHED_BFQ /* let other ioc users see the new values */ smp_wmb(); #endif task->io_context = ioc; } while (1); if (!err) { ioc->ioprio = ioprio; #ifdef CONFIG_IOSCHED_BFQ /* make sure schedulers see the new ioprio value */ wmb(); for (i = 0; i < IOC_IOPRIO_CHANGED_BITS; i++) set_bit(i, ioc->ioprio_changed); #else ioc->ioprio_changed = 1; #endif } task_unlock(task); return err; }
int set_task_ioprio(struct task_struct *task, int ioprio) { int err, i; struct io_context *ioc; const struct cred *cred = current_cred(), *tcred; rcu_read_lock(); tcred = __task_cred(task); if (tcred->uid != cred->euid && tcred->uid != cred->uid && !capable(CAP_SYS_NICE)) { rcu_read_unlock(); return -EPERM; } rcu_read_unlock(); err = security_task_setioprio(task, ioprio); if (err) return err; task_lock(task); do { ioc = task->io_context; /* see wmb() in current_io_context() */ smp_read_barrier_depends(); if (ioc) break; ioc = alloc_io_context(GFP_ATOMIC, -1); if (!ioc) { err = -ENOMEM; break; } smp_wmb(); task->io_context = ioc; } while (1); if (!err) { ioc->ioprio = ioprio; wmb(); for (i = 0; i < IOC_IOPRIO_CHANGED_BITS; i++) set_bit(i, ioc->ioprio_changed); } task_unlock(task); return err; }
/* * If the current task has no IO context then create one and initialise it. * Otherwise, return its existing IO context. * * This returned IO context doesn't have a specifically elevated refcount, * but since the current task itself holds a reference, the context can be * used in general code, so long as it stays within `current` context. */ struct io_context *current_io_context(gfp_t gfp_flags, int node) { struct task_struct *tsk = current; struct io_context *ret; ret = tsk->io_context; if (likely(ret)) return ret; ret = alloc_io_context(gfp_flags, node); if (ret) { /* make sure set_task_ioprio() sees the settings above */ smp_wmb(); tsk->io_context = ret; } return ret; }
static int copy_io(unsigned long clone_flags, struct task_struct *tsk) { #ifdef CONFIG_BLOCK struct io_context *ioc = current->io_context; if (!ioc) return 0; /* * Share io context with parent, if CLONE_IO is set */ if (clone_flags & CLONE_IO) { tsk->io_context = ioc_task_link(ioc); if (unlikely(!tsk->io_context)) return -ENOMEM; } else if (ioprio_valid(ioc->ioprio)) { tsk->io_context = alloc_io_context(GFP_KERNEL, -1); if (unlikely(!tsk->io_context)) return -ENOMEM; tsk->io_context->ioprio = ioc->ioprio; } #endif return 0; }