Commit d4928196 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'cfq-ioc-share' of git://git.kernel.dk/linux-2.6-block

* 'cfq-ioc-share' of git://git.kernel.dk/linux-2.6-block:
  cfq-iosched: kill some big inlines
  cfq-iosched: relax IOPRIO_CLASS_IDLE restrictions
  kernel: add CLONE_IO to specifically request sharing of IO contexts
  io_context sharing - anticipatory changes
  block: cfq: make the io contect sharing lockless
  io_context sharing - cfq changes
  io context sharing: preliminary support
  ioprio: move io priority from task_struct to io_context
parents bb04af0e febffd61
...@@ -170,9 +170,11 @@ static void free_as_io_context(struct as_io_context *aic) ...@@ -170,9 +170,11 @@ static void free_as_io_context(struct as_io_context *aic)
static void as_trim(struct io_context *ioc) static void as_trim(struct io_context *ioc)
{ {
spin_lock(&ioc->lock);
if (ioc->aic) if (ioc->aic)
free_as_io_context(ioc->aic); free_as_io_context(ioc->aic);
ioc->aic = NULL; ioc->aic = NULL;
spin_unlock(&ioc->lock);
} }
/* Called when the task exits */ /* Called when the task exits */
...@@ -462,7 +464,9 @@ static void as_antic_timeout(unsigned long data) ...@@ -462,7 +464,9 @@ static void as_antic_timeout(unsigned long data)
spin_lock_irqsave(q->queue_lock, flags); spin_lock_irqsave(q->queue_lock, flags);
if (ad->antic_status == ANTIC_WAIT_REQ if (ad->antic_status == ANTIC_WAIT_REQ
|| ad->antic_status == ANTIC_WAIT_NEXT) { || ad->antic_status == ANTIC_WAIT_NEXT) {
struct as_io_context *aic = ad->io_context->aic; struct as_io_context *aic;
spin_lock(&ad->io_context->lock);
aic = ad->io_context->aic;
ad->antic_status = ANTIC_FINISHED; ad->antic_status = ANTIC_FINISHED;
kblockd_schedule_work(&ad->antic_work); kblockd_schedule_work(&ad->antic_work);
...@@ -475,6 +479,7 @@ static void as_antic_timeout(unsigned long data) ...@@ -475,6 +479,7 @@ static void as_antic_timeout(unsigned long data)
/* process not "saved" by a cooperating request */ /* process not "saved" by a cooperating request */
ad->exit_no_coop = (7*ad->exit_no_coop + 256)/8; ad->exit_no_coop = (7*ad->exit_no_coop + 256)/8;
} }
spin_unlock(&ad->io_context->lock);
} }
spin_unlock_irqrestore(q->queue_lock, flags); spin_unlock_irqrestore(q->queue_lock, flags);
} }
...@@ -635,9 +640,11 @@ static int as_can_break_anticipation(struct as_data *ad, struct request *rq) ...@@ -635,9 +640,11 @@ static int as_can_break_anticipation(struct as_data *ad, struct request *rq)
ioc = ad->io_context; ioc = ad->io_context;
BUG_ON(!ioc); BUG_ON(!ioc);
spin_lock(&ioc->lock);
if (rq && ioc == RQ_IOC(rq)) { if (rq && ioc == RQ_IOC(rq)) {
/* request from same process */ /* request from same process */
spin_unlock(&ioc->lock);
return 1; return 1;
} }
...@@ -646,20 +653,25 @@ static int as_can_break_anticipation(struct as_data *ad, struct request *rq) ...@@ -646,20 +653,25 @@ static int as_can_break_anticipation(struct as_data *ad, struct request *rq)
* In this situation status should really be FINISHED, * In this situation status should really be FINISHED,
* however the timer hasn't had the chance to run yet. * however the timer hasn't had the chance to run yet.
*/ */
spin_unlock(&ioc->lock);
return 1; return 1;
} }
aic = ioc->aic; aic = ioc->aic;
if (!aic) if (!aic) {
spin_unlock(&ioc->lock);
return 0; return 0;
}
if (atomic_read(&aic->nr_queued) > 0) { if (atomic_read(&aic->nr_queued) > 0) {
/* process has more requests queued */ /* process has more requests queued */
spin_unlock(&ioc->lock);
return 1; return 1;
} }
if (atomic_read(&aic->nr_dispatched) > 0) { if (atomic_read(&aic->nr_dispatched) > 0) {
/* process has more requests dispatched */ /* process has more requests dispatched */
spin_unlock(&ioc->lock);
return 1; return 1;
} }
...@@ -680,6 +692,7 @@ static int as_can_break_anticipation(struct as_data *ad, struct request *rq) ...@@ -680,6 +692,7 @@ static int as_can_break_anticipation(struct as_data *ad, struct request *rq)
} }
as_update_iohist(ad, aic, rq); as_update_iohist(ad, aic, rq);
spin_unlock(&ioc->lock);
return 1; return 1;
} }
...@@ -688,20 +701,27 @@ static int as_can_break_anticipation(struct as_data *ad, struct request *rq) ...@@ -688,20 +701,27 @@ static int as_can_break_anticipation(struct as_data *ad, struct request *rq)
if (aic->ttime_samples == 0) if (aic->ttime_samples == 0)
ad->exit_prob = (7*ad->exit_prob + 256)/8; ad->exit_prob = (7*ad->exit_prob + 256)/8;
if (ad->exit_no_coop > 128) if (ad->exit_no_coop > 128) {
spin_unlock(&ioc->lock);
return 1; return 1;
}
} }
if (aic->ttime_samples == 0) { if (aic->ttime_samples == 0) {
if (ad->new_ttime_mean > ad->antic_expire) if (ad->new_ttime_mean > ad->antic_expire) {
spin_unlock(&ioc->lock);
return 1; return 1;
if (ad->exit_prob * ad->exit_no_coop > 128*256) }
if (ad->exit_prob * ad->exit_no_coop > 128*256) {
spin_unlock(&ioc->lock);
return 1; return 1;
}
} else if (aic->ttime_mean > ad->antic_expire) { } else if (aic->ttime_mean > ad->antic_expire) {
/* the process thinks too much between requests */ /* the process thinks too much between requests */
spin_unlock(&ioc->lock);
return 1; return 1;
} }
spin_unlock(&ioc->lock);
return 0; return 0;
} }
...@@ -1255,7 +1275,9 @@ static void as_merged_requests(struct request_queue *q, struct request *req, ...@@ -1255,7 +1275,9 @@ static void as_merged_requests(struct request_queue *q, struct request *req,
* Don't copy here but swap, because when anext is * Don't copy here but swap, because when anext is
* removed below, it must contain the unused context * removed below, it must contain the unused context
*/ */
double_spin_lock(&rioc->lock, &nioc->lock, rioc < nioc);
swap_io_context(&rioc, &nioc); swap_io_context(&rioc, &nioc);
double_spin_unlock(&rioc->lock, &nioc->lock, rioc < nioc);
} }
} }
......
This diff is collapsed.
...@@ -3853,55 +3853,100 @@ int __init blk_dev_init(void) ...@@ -3853,55 +3853,100 @@ int __init blk_dev_init(void)
return 0; return 0;
} }
static void cfq_dtor(struct io_context *ioc)
{
struct cfq_io_context *cic[1];
int r;
/*
* We don't have a specific key to lookup with, so use the gang
* lookup to just retrieve the first item stored. The cfq exit
* function will iterate the full tree, so any member will do.
*/
r = radix_tree_gang_lookup(&ioc->radix_root, (void **) cic, 0, 1);
if (r > 0)
cic[0]->dtor(ioc);
}
/* /*
* IO Context helper functions * IO Context helper functions. put_io_context() returns 1 if there are no
* more users of this io context, 0 otherwise.
*/ */
void put_io_context(struct io_context *ioc) int put_io_context(struct io_context *ioc)
{ {
if (ioc == NULL) if (ioc == NULL)
return; return 1;
BUG_ON(atomic_read(&ioc->refcount) == 0); BUG_ON(atomic_read(&ioc->refcount) == 0);
if (atomic_dec_and_test(&ioc->refcount)) { if (atomic_dec_and_test(&ioc->refcount)) {
struct cfq_io_context *cic;
rcu_read_lock(); rcu_read_lock();
if (ioc->aic && ioc->aic->dtor) if (ioc->aic && ioc->aic->dtor)
ioc->aic->dtor(ioc->aic); ioc->aic->dtor(ioc->aic);
if (ioc->cic_root.rb_node != NULL) {
struct rb_node *n = rb_first(&ioc->cic_root);
cic = rb_entry(n, struct cfq_io_context, rb_node);
cic->dtor(ioc);
}
rcu_read_unlock(); rcu_read_unlock();
cfq_dtor(ioc);
kmem_cache_free(iocontext_cachep, ioc); kmem_cache_free(iocontext_cachep, ioc);
return 1;
} }
return 0;
} }
EXPORT_SYMBOL(put_io_context); EXPORT_SYMBOL(put_io_context);
static void cfq_exit(struct io_context *ioc)
{
struct cfq_io_context *cic[1];
int r;
rcu_read_lock();
/*
* See comment for cfq_dtor()
*/
r = radix_tree_gang_lookup(&ioc->radix_root, (void **) cic, 0, 1);
rcu_read_unlock();
if (r > 0)
cic[0]->exit(ioc);
}
/* Called by the exitting task */ /* Called by the exitting task */
void exit_io_context(void) void exit_io_context(void)
{ {
struct io_context *ioc; struct io_context *ioc;
struct cfq_io_context *cic;
task_lock(current); task_lock(current);
ioc = current->io_context; ioc = current->io_context;
current->io_context = NULL; current->io_context = NULL;
task_unlock(current); task_unlock(current);
ioc->task = NULL; if (atomic_dec_and_test(&ioc->nr_tasks)) {
if (ioc->aic && ioc->aic->exit) if (ioc->aic && ioc->aic->exit)
ioc->aic->exit(ioc->aic); ioc->aic->exit(ioc->aic);
if (ioc->cic_root.rb_node != NULL) { cfq_exit(ioc);
cic = rb_entry(rb_first(&ioc->cic_root), struct cfq_io_context, rb_node);
cic->exit(ioc); put_io_context(ioc);
} }
}
struct io_context *alloc_io_context(gfp_t gfp_flags, int node)
{
struct io_context *ret;
put_io_context(ioc); ret = kmem_cache_alloc_node(iocontext_cachep, gfp_flags, node);
if (ret) {
atomic_set(&ret->refcount, 1);
atomic_set(&ret->nr_tasks, 1);
spin_lock_init(&ret->lock);
ret->ioprio_changed = 0;
ret->ioprio = 0;
ret->last_waited = jiffies; /* doesn't matter... */
ret->nr_batch_requests = 0; /* because this is 0 */
ret->aic = NULL;
INIT_RADIX_TREE(&ret->radix_root, GFP_ATOMIC | __GFP_HIGH);
ret->ioc_data = NULL;
}
return ret;
} }
/* /*
...@@ -3921,16 +3966,8 @@ static struct io_context *current_io_context(gfp_t gfp_flags, int node) ...@@ -3921,16 +3966,8 @@ static struct io_context *current_io_context(gfp_t gfp_flags, int node)
if (likely(ret)) if (likely(ret))
return ret; return ret;
ret = kmem_cache_alloc_node(iocontext_cachep, gfp_flags, node); ret = alloc_io_context(gfp_flags, node);
if (ret) { if (ret) {
atomic_set(&ret->refcount, 1);
ret->task = current;
ret->ioprio_changed = 0;
ret->last_waited = jiffies; /* doesn't matter... */
ret->nr_batch_requests = 0; /* because this is 0 */
ret->aic = NULL;
ret->cic_root.rb_node = NULL;
ret->ioc_data = NULL;
/* make sure set_task_ioprio() sees the settings above */ /* make sure set_task_ioprio() sees the settings above */
smp_wmb(); smp_wmb();
tsk->io_context = ret; tsk->io_context = ret;
...@@ -3947,10 +3984,18 @@ static struct io_context *current_io_context(gfp_t gfp_flags, int node) ...@@ -3947,10 +3984,18 @@ static struct io_context *current_io_context(gfp_t gfp_flags, int node)
*/ */
struct io_context *get_io_context(gfp_t gfp_flags, int node) struct io_context *get_io_context(gfp_t gfp_flags, int node)
{ {
struct io_context *ret; struct io_context *ret = NULL;
ret = current_io_context(gfp_flags, node);
if (likely(ret)) /*
atomic_inc(&ret->refcount); * Check for unlikely race with exiting task. ioc ref count is
* zero when ioc is being detached.
*/
do {
ret = current_io_context(gfp_flags, node);
if (unlikely(!ret))
break;
} while (!atomic_inc_not_zero(&ret->refcount));
return ret; return ret;
} }
EXPORT_SYMBOL(get_io_context); EXPORT_SYMBOL(get_io_context);
......
...@@ -41,18 +41,28 @@ static int set_task_ioprio(struct task_struct *task, int ioprio) ...@@ -41,18 +41,28 @@ static int set_task_ioprio(struct task_struct *task, int ioprio)
return err; return err;
task_lock(task); task_lock(task);
do {
ioc = task->io_context;
/* see wmb() in current_io_context() */
smp_read_barrier_depends();
if (ioc)
break;
task->ioprio = ioprio; ioc = alloc_io_context(GFP_ATOMIC, -1);
if (!ioc) {
ioc = task->io_context; err = -ENOMEM;
/* see wmb() in current_io_context() */ break;
smp_read_barrier_depends(); }
task->io_context = ioc;
} while (1);
if (ioc) if (!err) {
ioc->ioprio = ioprio;
ioc->ioprio_changed = 1; ioc->ioprio_changed = 1;
}
task_unlock(task); task_unlock(task);
return 0; return err;
} }
asmlinkage long sys_ioprio_set(int which, int who, int ioprio) asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
...@@ -75,8 +85,6 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio) ...@@ -75,8 +85,6 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
break; break;
case IOPRIO_CLASS_IDLE: case IOPRIO_CLASS_IDLE:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
break; break;
case IOPRIO_CLASS_NONE: case IOPRIO_CLASS_NONE:
if (data) if (data)
...@@ -148,7 +156,9 @@ static int get_task_ioprio(struct task_struct *p) ...@@ -148,7 +156,9 @@ static int get_task_ioprio(struct task_struct *p)
ret = security_task_getioprio(p); ret = security_task_getioprio(p);
if (ret) if (ret)
goto out; goto out;
ret = p->ioprio; ret = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, IOPRIO_NORM);
if (p->io_context)
ret = p->io_context->ioprio;
out: out:
return ret; return ret;
} }
......
...@@ -34,83 +34,10 @@ struct sg_io_hdr; ...@@ -34,83 +34,10 @@ struct sg_io_hdr;
#define BLKDEV_MIN_RQ 4 #define BLKDEV_MIN_RQ 4
#define BLKDEV_MAX_RQ 128 /* Default maximum */ #define BLKDEV_MAX_RQ 128 /* Default maximum */
/* int put_io_context(struct io_context *ioc);
* This is the per-process anticipatory I/O scheduler state.
*/
struct as_io_context {
spinlock_t lock;
void (*dtor)(struct as_io_context *aic); /* destructor */
void (*exit)(struct as_io_context *aic); /* called on task exit */
unsigned long state;
atomic_t nr_queued; /* queued reads & sync writes */
atomic_t nr_dispatched; /* number of requests gone to the drivers */
/* IO History tracking */
/* Thinktime */
unsigned long last_end_request;
unsigned long ttime_total;
unsigned long ttime_samples;
unsigned long ttime_mean;
/* Layout pattern */
unsigned int seek_samples;
sector_t last_request_pos;
u64 seek_total;
sector_t seek_mean;
};
struct cfq_queue;
struct cfq_io_context {
struct rb_node rb_node;
void *key;
struct cfq_queue *cfqq[2];
struct io_context *ioc;
unsigned long last_end_request;
sector_t last_request_pos;
unsigned long ttime_total;
unsigned long ttime_samples;
unsigned long ttime_mean;
unsigned int seek_samples;
u64 seek_total;
sector_t seek_mean;
struct list_head queue_list;
void (*dtor)(struct io_context *); /* destructor */
void (*exit)(struct io_context *); /* called on task exit */
};
/*
* This is the per-process I/O subsystem state. It is refcounted and
* kmalloc'ed. Currently all fields are modified in process io context
* (apart from the atomic refcount), so require no locking.
*/
struct io_context {
atomic_t refcount;
struct task_struct *task;
unsigned int ioprio_changed;
/*
* For request batching
*/
unsigned long last_waited; /* Time last woken after wait for request */
int nr_batch_requests; /* Number of requests left in the batch */
struct as_io_context *aic;
struct rb_root cic_root;
void *ioc_data;
};
void put_io_context(struct io_context *ioc);
void exit_io_context(void); void exit_io_context(void);
struct io_context *get_io_context(gfp_t gfp_flags, int node); struct io_context *get_io_context(gfp_t gfp_flags, int node);
struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
void copy_io_context(struct io_context **pdst, struct io_context **psrc); void copy_io_context(struct io_context **pdst, struct io_context **psrc);
void swap_io_context(struct io_context **ioc1, struct io_context **ioc2); void swap_io_context(struct io_context **ioc1, struct io_context **ioc2);
...@@ -894,6 +821,12 @@ static inline void exit_io_context(void) ...@@ -894,6 +821,12 @@ static inline void exit_io_context(void)
{ {
} }
static inline int put_io_context(struct io_context *ioc)
{
return 1;
}
#endif /* CONFIG_BLOCK */ #endif /* CONFIG_BLOCK */
#endif #endif
...@@ -137,7 +137,6 @@ extern struct group_info init_groups; ...@@ -137,7 +137,6 @@ extern struct group_info init_groups;
.time_slice = HZ, \ .time_slice = HZ, \
.nr_cpus_allowed = NR_CPUS, \ .nr_cpus_allowed = NR_CPUS, \
}, \ }, \
.ioprio = 0, \
.tasks = LIST_HEAD_INIT(tsk.tasks), \ .tasks = LIST_HEAD_INIT(tsk.tasks), \
.ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \ .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \
.ptrace_list = LIST_HEAD_INIT(tsk.ptrace_list), \ .ptrace_list = LIST_HEAD_INIT(tsk.ptrace_list), \
......
#ifndef IOCONTEXT_H
#define IOCONTEXT_H
#include <linux/radix-tree.h>
/*
* This is the per-process anticipatory I/O scheduler state.
*/
struct as_io_context {
spinlock_t lock;
void (*dtor)(struct as_io_context *aic); /* destructor */
void (*exit)(struct as_io_context *aic); /* called on task exit */
unsigned long state;
atomic_t nr_queued; /* queued reads & sync writes */
atomic_t nr_dispatched; /* number of requests gone to the drivers */
/* IO History tracking */
/* Thinktime */
unsigned long last_end_request;
unsigned long ttime_total;
unsigned long ttime_samples;
unsigned long ttime_mean;
/* Layout pattern */
unsigned int seek_samples;
sector_t last_request_pos;
u64 seek_total;
sector_t seek_mean;
};
struct cfq_queue;
struct cfq_io_context {
void *key;
unsigned long dead_key;
struct cfq_queue *cfqq[2];
struct io_context *ioc;
unsigned long last_end_request;
sector_t last_request_pos;
unsigned long ttime_total;
unsigned long ttime_samples;
unsigned long ttime_mean;
unsigned int seek_samples;
u64 seek_total;
sector_t seek_mean;
struct list_head queue_list;
void (*dtor)(struct io_context *); /* destructor */
void (*exit)(struct io_context *); /* called on task exit */
};
/*
* I/O subsystem state of the associated processes. It is refcounted
* and kmalloc'ed. These could be shared between processes.
*/
struct io_context {
atomic_t refcount;
atomic_t nr_tasks;
/* all the fields below are protected by this lock */
spinlock_t lock;
unsigned short ioprio;
unsigned short ioprio_changed;
/*
* For request batching
*/
unsigned long last_waited; /* Time last woken after wait for request */
int nr_batch_requests; /* Number of requests left in the batch */
struct as_io_context *aic;
struct radix_tree_root radix_root;
void *ioc_data;
};
static inline struct io_context *ioc_task_link(struct io_context *ioc)
{
/*
* if ref count is zero, don't allow sharing (ioc is going away, it's
* a race).
*/
if (ioc && atomic_inc_not_zero(&ioc->refcount))
return ioc;
return NULL;
}
#endif
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define IOPRIO_H #define IOPRIO_H
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/iocontext.h>
/* /*
* Gives us 8 prio classes with 13-bits of data for each class * Gives us 8 prio classes with 13-bits of data for each class
...@@ -45,18 +46,18 @@ enum { ...@@ -45,18 +46,18 @@ enum {
* the cpu scheduler nice value to an io priority * the cpu scheduler nice value to an io priority
*/ */
#define IOPRIO_NORM (4) #define IOPRIO_NORM (4)
static inline int task_ioprio(struct task_struct *task) static inline int task_ioprio(struct io_context *ioc)
{ {
if (ioprio_valid(task->ioprio)) if (ioprio_valid(ioc->ioprio))
return IOPRIO_PRIO_DATA(task->ioprio); return IOPRIO_PRIO_DATA(ioc->ioprio);
return IOPRIO_NORM; return IOPRIO_NORM;
} }
static inline int task_ioprio_class(struct task_struct *task) static inline int task_ioprio_class(struct io_context *ioc)
{ {
if (ioprio_valid(task->ioprio)) if (ioprio_valid(ioc->ioprio))
return IOPRIO_PRIO_CLASS(task->ioprio); return IOPRIO_PRIO_CLASS(ioc->ioprio);
return IOPRIO_CLASS_BE; return IOPRIO_CLASS_BE;
} }
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#define CLONE_NEWUSER 0x10000000 /* New user namespace */ #define CLONE_NEWUSER 0x10000000 /* New user namespace */
#define CLONE_NEWPID 0x20000000 /* New pid namespace */ #define CLONE_NEWPID 0x20000000 /* New pid namespace */
#define CLONE_NEWNET 0x40000000 /* New network namespace */ #define CLONE_NEWNET 0x40000000 /* New network namespace */
#define CLONE_IO 0x80000000 /* Clone io context */
/* /*
* Scheduling policies * Scheduling policies
...@@ -975,7 +976,6 @@ struct task_struct { ...@@ -975,7 +976,6 @@ struct task_struct {
struct hlist_head preempt_notifiers; struct hlist_head preempt_notifiers;
#endif #endif
unsigned short ioprio;
/* /*
* fpu_counter contains the number of consecutive context switches * fpu_counter contains the number of consecutive context switches
* that the FPU is used. If this is over a threshold, the lazy fpu * that the FPU is used. If this is over a threshold, the lazy fpu
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <linux/random.h> #include <linux/random.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/blkdev.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
...@@ -791,6 +792,31 @@ out: ...@@ -791,6 +792,31 @@ out:
return error; return error;
} }
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;
}
/* /*
* Helper to unshare the files of the current task. * Helper to unshare the files of the current task.
* We don't want to expose copy_files internals to * We don't want to expose copy_files internals to
...@@ -1156,15 +1182,17 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1156,15 +1182,17 @@ static struct task_struct *copy_process(unsigned long clone_flags,
goto bad_fork_cleanup_mm; goto bad_fork_cleanup_mm;
if ((retval = copy_namespaces(clone_flags, p))) if ((retval = copy_namespaces(clone_flags, p)))
goto bad_fork_cleanup_keys; goto bad_fork_cleanup_keys;
if ((retval = copy_io(clone_flags, p)))
goto bad_fork_cleanup_namespaces;
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
if (retval) if (retval)
goto bad_fork_cleanup_namespaces; goto bad_fork_cleanup_io;
if (pid != &init_struct_pid) { if (pid != &init_struct_pid) {
retval = -ENOMEM; retval = -ENOMEM;
pid = alloc_pid(task_active_pid_ns(p)); pid = alloc_pid(task_active_pid_ns(p));
if (!pid) if (!pid)
goto bad_fork_cleanup_namespaces; goto bad_fork_cleanup_io;
if (clone_flags & CLONE_NEWPID) { if (clone_flags & CLONE_NEWPID) {
retval = pid_ns_prepare_proc(task_active_pid_ns(p)); retval = pid_ns_prepare_proc(task_active_pid_ns(p));
...@@ -1234,9 +1262,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1234,9 +1262,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
/* Need tasklist lock for parent etc handling! */ /* Need tasklist lock for parent etc handling! */
write_lock_irq(&tasklist_lock); write_lock_irq(&tasklist_lock);
/* for sys_ioprio_set(IOPRIO_WHO_PGRP) */
p->ioprio = current->ioprio;
/* /*
* The task hasn't been attached yet, so its cpus_allowed mask will * The task hasn't been attached yet, so its cpus_allowed mask will
* not be changed, nor will its assigned CPU. * not be changed, nor will its assigned CPU.
...@@ -1328,6 +1353,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1328,6 +1353,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
bad_fork_free_pid: bad_fork_free_pid:
if (pid != &init_struct_pid) if (pid != &init_struct_pid)
free_pid(pid); free_pid(pid);
bad_fork_cleanup_io:
put_io_context(p->io_context);
bad_fork_cleanup_namespaces: bad_fork_cleanup_namespaces:
exit_task_namespaces(p); exit_task_namespaces(p);
bad_fork_cleanup_keys: bad_fork_cleanup_keys:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment