diff options
author | Jens Axboe <axboe@suse.de> | 2005-06-27 04:55:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-27 17:33:29 -0400 |
commit | 22e2c507c301c3dbbcf91b4948b88f78842ee6c9 (patch) | |
tree | 9a97c91d1362e69703aa286021daffb8a5456f4c /include/linux | |
parent | 020f46a39eb7b99a575b9f4d105fce2b142acdf1 (diff) |
[PATCH] Update cfq io scheduler to time sliced design
This updates the CFQ io scheduler to the new time sliced design (cfq
v3). It provides full process fairness, while giving excellent
aggregate system throughput even for many competing processes. It
supports io priorities, either inherited from the cpu nice value or set
directly with the ioprio_get/set syscalls. The latter closely mimic
set/getpriority.
This import is based on my latest from -mm.
Signed-off-by: Jens Axboe <axboe@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'include/linux')
-rw-r--r-- | include/linux/bio.h | 14 | ||||
-rw-r--r-- | include/linux/blkdev.h | 25 | ||||
-rw-r--r-- | include/linux/elevator.h | 8 | ||||
-rw-r--r-- | include/linux/fs.h | 19 | ||||
-rw-r--r-- | include/linux/init_task.h | 2 | ||||
-rw-r--r-- | include/linux/ioprio.h | 87 | ||||
-rw-r--r-- | include/linux/sched.h | 6 | ||||
-rw-r--r-- | include/linux/writeback.h | 6 |
8 files changed, 152 insertions, 15 deletions
diff --git a/include/linux/bio.h b/include/linux/bio.h index 038022763f09..36ef29fa0d8b 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h | |||
@@ -22,6 +22,7 @@ | |||
22 | 22 | ||
23 | #include <linux/highmem.h> | 23 | #include <linux/highmem.h> |
24 | #include <linux/mempool.h> | 24 | #include <linux/mempool.h> |
25 | #include <linux/ioprio.h> | ||
25 | 26 | ||
26 | /* Platforms may set this to teach the BIO layer about IOMMU hardware. */ | 27 | /* Platforms may set this to teach the BIO layer about IOMMU hardware. */ |
27 | #include <asm/io.h> | 28 | #include <asm/io.h> |
@@ -150,6 +151,19 @@ struct bio { | |||
150 | #define BIO_RW_SYNC 4 | 151 | #define BIO_RW_SYNC 4 |
151 | 152 | ||
152 | /* | 153 | /* |
154 | * upper 16 bits of bi_rw define the io priority of this bio | ||
155 | */ | ||
156 | #define BIO_PRIO_SHIFT (8 * sizeof(unsigned long) - IOPRIO_BITS) | ||
157 | #define bio_prio(bio) ((bio)->bi_rw >> BIO_PRIO_SHIFT) | ||
158 | #define bio_prio_valid(bio) ioprio_valid(bio_prio(bio)) | ||
159 | |||
160 | #define bio_set_prio(bio, prio) do { \ | ||
161 | WARN_ON(prio >= (1 << IOPRIO_BITS)); \ | ||
162 | (bio)->bi_rw &= ((1UL << BIO_PRIO_SHIFT) - 1); \ | ||
163 | (bio)->bi_rw |= ((unsigned long) (prio) << BIO_PRIO_SHIFT); \ | ||
164 | } while (0) | ||
165 | |||
166 | /* | ||
153 | * various member access, note that bio_data should of course not be used | 167 | * various member access, note that bio_data should of course not be used |
154 | * on highmem page vectors | 168 | * on highmem page vectors |
155 | */ | 169 | */ |
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b54a0348a890..21a8674cd149 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h | |||
@@ -54,16 +54,23 @@ struct as_io_context { | |||
54 | 54 | ||
55 | struct cfq_queue; | 55 | struct cfq_queue; |
56 | struct cfq_io_context { | 56 | struct cfq_io_context { |
57 | void (*dtor)(struct cfq_io_context *); | ||
58 | void (*exit)(struct cfq_io_context *); | ||
59 | |||
60 | struct io_context *ioc; | ||
61 | |||
62 | /* | 57 | /* |
63 | * circular list of cfq_io_contexts belonging to a process io context | 58 | * circular list of cfq_io_contexts belonging to a process io context |
64 | */ | 59 | */ |
65 | struct list_head list; | 60 | struct list_head list; |
66 | struct cfq_queue *cfqq; | 61 | struct cfq_queue *cfqq; |
62 | void *key; | ||
63 | |||
64 | struct io_context *ioc; | ||
65 | |||
66 | unsigned long last_end_request; | ||
67 | unsigned long last_queue; | ||
68 | unsigned long ttime_total; | ||
69 | unsigned long ttime_samples; | ||
70 | unsigned long ttime_mean; | ||
71 | |||
72 | void (*dtor)(struct cfq_io_context *); | ||
73 | void (*exit)(struct cfq_io_context *); | ||
67 | }; | 74 | }; |
68 | 75 | ||
69 | /* | 76 | /* |
@@ -73,7 +80,9 @@ struct cfq_io_context { | |||
73 | */ | 80 | */ |
74 | struct io_context { | 81 | struct io_context { |
75 | atomic_t refcount; | 82 | atomic_t refcount; |
76 | pid_t pid; | 83 | struct task_struct *task; |
84 | |||
85 | int (*set_ioprio)(struct io_context *, unsigned int); | ||
77 | 86 | ||
78 | /* | 87 | /* |
79 | * For request batching | 88 | * For request batching |
@@ -81,8 +90,6 @@ struct io_context { | |||
81 | unsigned long last_waited; /* Time last woken after wait for request */ | 90 | unsigned long last_waited; /* Time last woken after wait for request */ |
82 | int nr_batch_requests; /* Number of requests left in the batch */ | 91 | int nr_batch_requests; /* Number of requests left in the batch */ |
83 | 92 | ||
84 | spinlock_t lock; | ||
85 | |||
86 | struct as_io_context *aic; | 93 | struct as_io_context *aic; |
87 | struct cfq_io_context *cic; | 94 | struct cfq_io_context *cic; |
88 | }; | 95 | }; |
@@ -134,6 +141,8 @@ struct request { | |||
134 | 141 | ||
135 | void *elevator_private; | 142 | void *elevator_private; |
136 | 143 | ||
144 | unsigned short ioprio; | ||
145 | |||
137 | int rq_status; /* should split this into a few status bits */ | 146 | int rq_status; /* should split this into a few status bits */ |
138 | struct gendisk *rq_disk; | 147 | struct gendisk *rq_disk; |
139 | int errors; | 148 | int errors; |
diff --git a/include/linux/elevator.h b/include/linux/elevator.h index ee54f81faad5..ea6bbc2d7407 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h | |||
@@ -16,9 +16,9 @@ typedef void (elevator_remove_req_fn) (request_queue_t *, struct request *); | |||
16 | typedef void (elevator_requeue_req_fn) (request_queue_t *, struct request *); | 16 | typedef void (elevator_requeue_req_fn) (request_queue_t *, struct request *); |
17 | typedef struct request *(elevator_request_list_fn) (request_queue_t *, struct request *); | 17 | typedef struct request *(elevator_request_list_fn) (request_queue_t *, struct request *); |
18 | typedef void (elevator_completed_req_fn) (request_queue_t *, struct request *); | 18 | typedef void (elevator_completed_req_fn) (request_queue_t *, struct request *); |
19 | typedef int (elevator_may_queue_fn) (request_queue_t *, int); | 19 | typedef int (elevator_may_queue_fn) (request_queue_t *, int, struct bio *); |
20 | 20 | ||
21 | typedef int (elevator_set_req_fn) (request_queue_t *, struct request *, int); | 21 | typedef int (elevator_set_req_fn) (request_queue_t *, struct request *, struct bio *, int); |
22 | typedef void (elevator_put_req_fn) (request_queue_t *, struct request *); | 22 | typedef void (elevator_put_req_fn) (request_queue_t *, struct request *); |
23 | typedef void (elevator_deactivate_req_fn) (request_queue_t *, struct request *); | 23 | typedef void (elevator_deactivate_req_fn) (request_queue_t *, struct request *); |
24 | 24 | ||
@@ -96,9 +96,9 @@ extern struct request *elv_former_request(request_queue_t *, struct request *); | |||
96 | extern struct request *elv_latter_request(request_queue_t *, struct request *); | 96 | extern struct request *elv_latter_request(request_queue_t *, struct request *); |
97 | extern int elv_register_queue(request_queue_t *q); | 97 | extern int elv_register_queue(request_queue_t *q); |
98 | extern void elv_unregister_queue(request_queue_t *q); | 98 | extern void elv_unregister_queue(request_queue_t *q); |
99 | extern int elv_may_queue(request_queue_t *, int); | 99 | extern int elv_may_queue(request_queue_t *, int, struct bio *); |
100 | extern void elv_completed_request(request_queue_t *, struct request *); | 100 | extern void elv_completed_request(request_queue_t *, struct request *); |
101 | extern int elv_set_request(request_queue_t *, struct request *, int); | 101 | extern int elv_set_request(request_queue_t *, struct request *, struct bio *, int); |
102 | extern void elv_put_request(request_queue_t *, struct request *); | 102 | extern void elv_put_request(request_queue_t *, struct request *); |
103 | 103 | ||
104 | /* | 104 | /* |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 3ae8e37bdfc8..047bde30836a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -213,6 +213,7 @@ extern int dir_notify_enable; | |||
213 | #include <linux/radix-tree.h> | 213 | #include <linux/radix-tree.h> |
214 | #include <linux/prio_tree.h> | 214 | #include <linux/prio_tree.h> |
215 | #include <linux/init.h> | 215 | #include <linux/init.h> |
216 | #include <linux/sched.h> | ||
216 | 217 | ||
217 | #include <asm/atomic.h> | 218 | #include <asm/atomic.h> |
218 | #include <asm/semaphore.h> | 219 | #include <asm/semaphore.h> |
@@ -822,16 +823,34 @@ enum { | |||
822 | #define vfs_check_frozen(sb, level) \ | 823 | #define vfs_check_frozen(sb, level) \ |
823 | wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level))) | 824 | wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level))) |
824 | 825 | ||
826 | static inline void get_fs_excl(void) | ||
827 | { | ||
828 | atomic_inc(¤t->fs_excl); | ||
829 | } | ||
830 | |||
831 | static inline void put_fs_excl(void) | ||
832 | { | ||
833 | atomic_dec(¤t->fs_excl); | ||
834 | } | ||
835 | |||
836 | static inline int has_fs_excl(void) | ||
837 | { | ||
838 | return atomic_read(¤t->fs_excl); | ||
839 | } | ||
840 | |||
841 | |||
825 | /* | 842 | /* |
826 | * Superblock locking. | 843 | * Superblock locking. |
827 | */ | 844 | */ |
828 | static inline void lock_super(struct super_block * sb) | 845 | static inline void lock_super(struct super_block * sb) |
829 | { | 846 | { |
847 | get_fs_excl(); | ||
830 | down(&sb->s_lock); | 848 | down(&sb->s_lock); |
831 | } | 849 | } |
832 | 850 | ||
833 | static inline void unlock_super(struct super_block * sb) | 851 | static inline void unlock_super(struct super_block * sb) |
834 | { | 852 | { |
853 | put_fs_excl(); | ||
835 | up(&sb->s_lock); | 854 | up(&sb->s_lock); |
836 | } | 855 | } |
837 | 856 | ||
diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 03206a425d7a..c727c195a91a 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h | |||
@@ -81,6 +81,7 @@ extern struct group_info init_groups; | |||
81 | .mm = NULL, \ | 81 | .mm = NULL, \ |
82 | .active_mm = &init_mm, \ | 82 | .active_mm = &init_mm, \ |
83 | .run_list = LIST_HEAD_INIT(tsk.run_list), \ | 83 | .run_list = LIST_HEAD_INIT(tsk.run_list), \ |
84 | .ioprio = 0, \ | ||
84 | .time_slice = HZ, \ | 85 | .time_slice = HZ, \ |
85 | .tasks = LIST_HEAD_INIT(tsk.tasks), \ | 86 | .tasks = LIST_HEAD_INIT(tsk.tasks), \ |
86 | .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \ | 87 | .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \ |
@@ -110,6 +111,7 @@ extern struct group_info init_groups; | |||
110 | .proc_lock = SPIN_LOCK_UNLOCKED, \ | 111 | .proc_lock = SPIN_LOCK_UNLOCKED, \ |
111 | .journal_info = NULL, \ | 112 | .journal_info = NULL, \ |
112 | .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \ | 113 | .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \ |
114 | .fs_excl = ATOMIC_INIT(0), \ | ||
113 | } | 115 | } |
114 | 116 | ||
115 | 117 | ||
diff --git a/include/linux/ioprio.h b/include/linux/ioprio.h new file mode 100644 index 000000000000..7811300d88ee --- /dev/null +++ b/include/linux/ioprio.h | |||
@@ -0,0 +1,87 @@ | |||
1 | #ifndef IOPRIO_H | ||
2 | #define IOPRIO_H | ||
3 | |||
4 | #include <linux/sched.h> | ||
5 | |||
6 | /* | ||
7 | * Gives us 8 prio classes with 13-bits of data for each class | ||
8 | */ | ||
9 | #define IOPRIO_BITS (16) | ||
10 | #define IOPRIO_CLASS_SHIFT (13) | ||
11 | #define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) | ||
12 | |||
13 | #define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) | ||
14 | #define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) | ||
15 | |||
16 | #define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE) | ||
17 | |||
18 | /* | ||
19 | * These are the io priority groups as implemented by CFQ. RT is the realtime | ||
20 | * class, it always gets premium service. BE is the best-effort scheduling | ||
21 | * class, the default for any process. IDLE is the idle scheduling class, it | ||
22 | * is only served when no one else is using the disk. | ||
23 | */ | ||
24 | enum { | ||
25 | IOPRIO_CLASS_NONE, | ||
26 | IOPRIO_CLASS_RT, | ||
27 | IOPRIO_CLASS_BE, | ||
28 | IOPRIO_CLASS_IDLE, | ||
29 | }; | ||
30 | |||
31 | /* | ||
32 | * 8 best effort priority levels are supported | ||
33 | */ | ||
34 | #define IOPRIO_BE_NR (8) | ||
35 | |||
36 | asmlinkage int sys_ioprio_set(int, int, int); | ||
37 | asmlinkage int sys_ioprio_get(int, int); | ||
38 | |||
39 | enum { | ||
40 | IOPRIO_WHO_PROCESS = 1, | ||
41 | IOPRIO_WHO_PGRP, | ||
42 | IOPRIO_WHO_USER, | ||
43 | }; | ||
44 | |||
45 | /* | ||
46 | * if process has set io priority explicitly, use that. if not, convert | ||
47 | * the cpu scheduler nice value to an io priority | ||
48 | */ | ||
49 | #define IOPRIO_NORM (4) | ||
50 | static inline int task_ioprio(struct task_struct *task) | ||
51 | { | ||
52 | WARN_ON(!ioprio_valid(task->ioprio)); | ||
53 | return IOPRIO_PRIO_DATA(task->ioprio); | ||
54 | } | ||
55 | |||
56 | static inline int task_nice_ioprio(struct task_struct *task) | ||
57 | { | ||
58 | return (task_nice(task) + 20) / 5; | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * For inheritance, return the highest of the two given priorities | ||
63 | */ | ||
64 | static inline int ioprio_best(unsigned short aprio, unsigned short bprio) | ||
65 | { | ||
66 | unsigned short aclass = IOPRIO_PRIO_CLASS(aprio); | ||
67 | unsigned short bclass = IOPRIO_PRIO_CLASS(bprio); | ||
68 | |||
69 | if (!ioprio_valid(aprio)) | ||
70 | return bprio; | ||
71 | if (!ioprio_valid(bprio)) | ||
72 | return aprio; | ||
73 | |||
74 | if (aclass == IOPRIO_CLASS_NONE) | ||
75 | aclass = IOPRIO_CLASS_BE; | ||
76 | if (bclass == IOPRIO_CLASS_NONE) | ||
77 | bclass = IOPRIO_CLASS_BE; | ||
78 | |||
79 | if (aclass == bclass) | ||
80 | return min(aprio, bprio); | ||
81 | if (aclass > bclass) | ||
82 | return bprio; | ||
83 | else | ||
84 | return aprio; | ||
85 | } | ||
86 | |||
87 | #endif | ||
diff --git a/include/linux/sched.h b/include/linux/sched.h index 9530b1903160..ff48815bd3a2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -608,6 +608,8 @@ struct task_struct { | |||
608 | struct list_head run_list; | 608 | struct list_head run_list; |
609 | prio_array_t *array; | 609 | prio_array_t *array; |
610 | 610 | ||
611 | unsigned short ioprio; | ||
612 | |||
611 | unsigned long sleep_avg; | 613 | unsigned long sleep_avg; |
612 | unsigned long long timestamp, last_ran; | 614 | unsigned long long timestamp, last_ran; |
613 | unsigned long long sched_time; /* sched_clock time spent running */ | 615 | unsigned long long sched_time; /* sched_clock time spent running */ |
@@ -763,6 +765,7 @@ struct task_struct { | |||
763 | nodemask_t mems_allowed; | 765 | nodemask_t mems_allowed; |
764 | int cpuset_mems_generation; | 766 | int cpuset_mems_generation; |
765 | #endif | 767 | #endif |
768 | atomic_t fs_excl; /* holding fs exclusive resources */ | ||
766 | }; | 769 | }; |
767 | 770 | ||
768 | static inline pid_t process_group(struct task_struct *tsk) | 771 | static inline pid_t process_group(struct task_struct *tsk) |
@@ -1112,7 +1115,8 @@ extern void unhash_process(struct task_struct *p); | |||
1112 | 1115 | ||
1113 | /* | 1116 | /* |
1114 | * Protects ->fs, ->files, ->mm, ->ptrace, ->group_info, ->comm, keyring | 1117 | * Protects ->fs, ->files, ->mm, ->ptrace, ->group_info, ->comm, keyring |
1115 | * subscriptions and synchronises with wait4(). Also used in procfs. | 1118 | * subscriptions and synchronises with wait4(). Also used in procfs. Also |
1119 | * pins the final release of task.io_context. | ||
1116 | * | 1120 | * |
1117 | * Nests both inside and outside of read_lock(&tasklist_lock). | 1121 | * Nests both inside and outside of read_lock(&tasklist_lock). |
1118 | * It must not be nested with write_lock_irq(&tasklist_lock), | 1122 | * It must not be nested with write_lock_irq(&tasklist_lock), |
diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 1262cb43c3ab..d5c3fe1bf33d 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h | |||
@@ -14,11 +14,13 @@ extern struct list_head inode_unused; | |||
14 | * Yes, writeback.h requires sched.h | 14 | * Yes, writeback.h requires sched.h |
15 | * No, sched.h is not included from here. | 15 | * No, sched.h is not included from here. |
16 | */ | 16 | */ |
17 | static inline int current_is_pdflush(void) | 17 | static inline int task_is_pdflush(struct task_struct *task) |
18 | { | 18 | { |
19 | return current->flags & PF_FLUSHER; | 19 | return task->flags & PF_FLUSHER; |
20 | } | 20 | } |
21 | 21 | ||
22 | #define current_is_pdflush() task_is_pdflush(current) | ||
23 | |||
22 | /* | 24 | /* |
23 | * fs/fs-writeback.c | 25 | * fs/fs-writeback.c |
24 | */ | 26 | */ |