diff options
author | Arnd Bergmann <arnd@arndb.de> | 2005-12-05 22:52:26 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-01-08 22:52:58 -0500 |
commit | 2a911f0bb73e67826062b7d073dd7367ca449724 (patch) | |
tree | c1d8d4f340cf7571722b2d9019f158acd345cff4 | |
parent | 5110459f181ef1f11200bb3dec61953f08cc49e7 (diff) |
[PATCH] spufs: Improved SPU preemptability [part 2].
This patch reduces lock complexity of SPU scheduler, particularly
for involuntary preemptive switches. As a result the new code
does a better job of mapping the highest priority tasks to SPUs.
Lock complexity is reduced by using the system default workqueue
to perform involuntary saves. In this way we avoid nasty lock
ordering problems that the previous code had. A "minimum timeslice"
for SPU contexts is also introduced. The intent here is to avoid
thrashing.
While the new scheduler does a better job at prioritization it
still does nothing for fairness.
From: Mark Nutter <mnutter@us.ibm.com>
Signed-off-by: Arnd Bergmann <arndb@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/context.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/sched.c | 81 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/spufs.h | 5 | ||||
-rw-r--r-- | include/asm-powerpc/spu.h | 1 |
4 files changed, 64 insertions, 27 deletions
diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 0d88a1c24f67..1758cec58bc7 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c | |||
@@ -116,8 +116,10 @@ int spu_acquire_runnable(struct spu_context *ctx) | |||
116 | int ret = 0; | 116 | int ret = 0; |
117 | 117 | ||
118 | down_read(&ctx->state_sema); | 118 | down_read(&ctx->state_sema); |
119 | if (ctx->state == SPU_STATE_RUNNABLE) | 119 | if (ctx->state == SPU_STATE_RUNNABLE) { |
120 | ctx->spu->prio = current->prio; | ||
120 | return 0; | 121 | return 0; |
122 | } | ||
121 | /* ctx is about to be freed, can't acquire any more */ | 123 | /* ctx is about to be freed, can't acquire any more */ |
122 | if (!ctx->owner) { | 124 | if (!ctx->owner) { |
123 | ret = -EINVAL; | 125 | ret = -EINVAL; |
diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index e2f10b5b8a6a..fccc7709adbe 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c | |||
@@ -45,6 +45,8 @@ | |||
45 | #include <asm/spu_csa.h> | 45 | #include <asm/spu_csa.h> |
46 | #include "spufs.h" | 46 | #include "spufs.h" |
47 | 47 | ||
48 | #define SPU_MIN_TIMESLICE (100 * HZ / 1000)) | ||
49 | |||
48 | #define SPU_BITMAP_SIZE (((MAX_PRIO+BITS_PER_LONG)/BITS_PER_LONG)+1) | 50 | #define SPU_BITMAP_SIZE (((MAX_PRIO+BITS_PER_LONG)/BITS_PER_LONG)+1) |
49 | struct spu_prio_array { | 51 | struct spu_prio_array { |
50 | atomic_t nr_blocked; | 52 | atomic_t nr_blocked; |
@@ -168,6 +170,7 @@ static inline void bind_context(struct spu *spu, struct spu_context *ctx) | |||
168 | spu->number); | 170 | spu->number); |
169 | spu->ctx = ctx; | 171 | spu->ctx = ctx; |
170 | spu->flags = 0; | 172 | spu->flags = 0; |
173 | ctx->flags = 0; | ||
171 | ctx->spu = spu; | 174 | ctx->spu = spu; |
172 | ctx->ops = &spu_hw_ops; | 175 | ctx->ops = &spu_hw_ops; |
173 | spu->pid = current->pid; | 176 | spu->pid = current->pid; |
@@ -180,6 +183,7 @@ static inline void bind_context(struct spu *spu, struct spu_context *ctx) | |||
180 | mb(); | 183 | mb(); |
181 | spu_unmap_mappings(ctx); | 184 | spu_unmap_mappings(ctx); |
182 | spu_restore(&ctx->csa, spu); | 185 | spu_restore(&ctx->csa, spu); |
186 | spu->timestamp = jiffies; | ||
183 | } | 187 | } |
184 | 188 | ||
185 | static inline void unbind_context(struct spu *spu, struct spu_context *ctx) | 189 | static inline void unbind_context(struct spu *spu, struct spu_context *ctx) |
@@ -188,6 +192,7 @@ static inline void unbind_context(struct spu *spu, struct spu_context *ctx) | |||
188 | spu->pid, spu->number); | 192 | spu->pid, spu->number); |
189 | spu_unmap_mappings(ctx); | 193 | spu_unmap_mappings(ctx); |
190 | spu_save(&ctx->csa, spu); | 194 | spu_save(&ctx->csa, spu); |
195 | spu->timestamp = jiffies; | ||
191 | ctx->state = SPU_STATE_SAVED; | 196 | ctx->state = SPU_STATE_SAVED; |
192 | spu->ibox_callback = NULL; | 197 | spu->ibox_callback = NULL; |
193 | spu->wbox_callback = NULL; | 198 | spu->wbox_callback = NULL; |
@@ -197,38 +202,62 @@ static inline void unbind_context(struct spu *spu, struct spu_context *ctx) | |||
197 | spu->prio = MAX_PRIO; | 202 | spu->prio = MAX_PRIO; |
198 | ctx->ops = &spu_backing_ops; | 203 | ctx->ops = &spu_backing_ops; |
199 | ctx->spu = NULL; | 204 | ctx->spu = NULL; |
205 | ctx->flags = 0; | ||
206 | spu->flags = 0; | ||
200 | spu->ctx = NULL; | 207 | spu->ctx = NULL; |
201 | } | 208 | } |
202 | 209 | ||
203 | static struct spu *preempt_active(struct spu_runqueue *rq) | 210 | static void spu_reaper(void *data) |
204 | { | 211 | { |
205 | struct list_head *p; | 212 | struct spu_context *ctx = data; |
206 | struct spu *worst, *spu; | 213 | struct spu *spu; |
207 | 214 | ||
208 | worst = list_entry(rq->active_list.next, struct spu, sched_list); | 215 | down_write(&ctx->state_sema); |
209 | list_for_each(p, &rq->active_list) { | 216 | spu = ctx->spu; |
210 | spu = list_entry(p, struct spu, sched_list); | 217 | if (spu && (ctx->flags & SPU_CONTEXT_PREEMPT)) { |
211 | if (spu->prio > worst->prio) { | 218 | if (atomic_read(&spu->rq->prio.nr_blocked)) { |
212 | worst = spu; | 219 | pr_debug("%s: spu=%d\n", __func__, spu->number); |
220 | ctx->ops->runcntl_stop(ctx); | ||
221 | spu_deactivate(ctx); | ||
222 | wake_up_all(&ctx->stop_wq); | ||
223 | } else { | ||
224 | clear_bit(SPU_CONTEXT_PREEMPT_nr, &ctx->flags); | ||
213 | } | 225 | } |
214 | } | 226 | } |
215 | if (current->prio < worst->prio) { | 227 | up_write(&ctx->state_sema); |
216 | struct spu_context *ctx = worst->ctx; | 228 | put_spu_context(ctx); |
229 | } | ||
217 | 230 | ||
218 | spu = worst; | 231 | static void schedule_spu_reaper(struct spu_runqueue *rq, struct spu *spu) |
219 | if (down_write_trylock(&ctx->state_sema)) { | 232 | { |
220 | pr_debug("%s: booting pid=%d from SPU %d\n", | 233 | struct spu_context *ctx = get_spu_context(spu->ctx); |
221 | __FUNCTION__, spu->pid, spu->number); | 234 | unsigned long now = jiffies; |
222 | del_active(rq, spu); | 235 | unsigned long expire = spu->timestamp + SPU_MIN_TIMESLICE; |
223 | up(&rq->sem); | 236 | |
224 | wake_up_all(&ctx->stop_wq); | 237 | set_bit(SPU_CONTEXT_PREEMPT_nr, &ctx->flags); |
225 | ctx->ops->runcntl_stop(ctx); | 238 | INIT_WORK(&ctx->reap_work, spu_reaper, ctx); |
226 | unbind_context(spu, ctx); | 239 | if (time_after(now, expire)) |
227 | up_write(&ctx->state_sema); | 240 | schedule_work(&ctx->reap_work); |
228 | return spu; | 241 | else |
242 | schedule_delayed_work(&ctx->reap_work, expire - now); | ||
243 | } | ||
244 | |||
245 | static void check_preempt_active(struct spu_runqueue *rq) | ||
246 | { | ||
247 | struct list_head *p; | ||
248 | struct spu *worst = NULL; | ||
249 | |||
250 | list_for_each(p, &rq->active_list) { | ||
251 | struct spu *spu = list_entry(p, struct spu, sched_list); | ||
252 | struct spu_context *ctx = spu->ctx; | ||
253 | if (!(ctx->flags & SPU_CONTEXT_PREEMPT)) { | ||
254 | if (!worst || (spu->prio > worst->prio)) { | ||
255 | worst = spu; | ||
256 | } | ||
229 | } | 257 | } |
230 | } | 258 | } |
231 | return NULL; | 259 | if (worst && (current->prio < worst->prio)) |
260 | schedule_spu_reaper(rq, worst); | ||
232 | } | 261 | } |
233 | 262 | ||
234 | static struct spu *get_idle_spu(struct spu_context *ctx, u64 flags) | 263 | static struct spu *get_idle_spu(struct spu_context *ctx, u64 flags) |
@@ -256,10 +285,7 @@ static struct spu *get_idle_spu(struct spu_context *ctx, u64 flags) | |||
256 | continue; | 285 | continue; |
257 | } | 286 | } |
258 | } else { | 287 | } else { |
259 | if (is_best_prio(rq)) { | 288 | check_preempt_active(rq); |
260 | if ((spu = preempt_active(rq)) != NULL) | ||
261 | return spu; | ||
262 | } | ||
263 | prio_wait(rq, ctx, flags); | 289 | prio_wait(rq, ctx, flags); |
264 | if (signal_pending(current)) { | 290 | if (signal_pending(current)) { |
265 | prio_wakeup(rq); | 291 | prio_wakeup(rq); |
@@ -361,6 +387,8 @@ void spu_yield(struct spu_context *ctx) | |||
361 | spu_deactivate(ctx); | 387 | spu_deactivate(ctx); |
362 | ctx->state = SPU_STATE_SAVED; | 388 | ctx->state = SPU_STATE_SAVED; |
363 | need_yield = 1; | 389 | need_yield = 1; |
390 | } else if (spu) { | ||
391 | spu->prio = MAX_PRIO; | ||
364 | } | 392 | } |
365 | up_write(&ctx->state_sema); | 393 | up_write(&ctx->state_sema); |
366 | if (unlikely(need_yield)) | 394 | if (unlikely(need_yield)) |
@@ -399,6 +427,7 @@ int __init spu_sched_init(void) | |||
399 | pr_debug("%s: adding SPU[%d]\n", __FUNCTION__, spu->number); | 427 | pr_debug("%s: adding SPU[%d]\n", __FUNCTION__, spu->number); |
400 | add_idle(rq, spu); | 428 | add_idle(rq, spu); |
401 | spu->rq = rq; | 429 | spu->rq = rq; |
430 | spu->timestamp = jiffies; | ||
402 | } | 431 | } |
403 | if (!rq->nr_idle) { | 432 | if (!rq->nr_idle) { |
404 | printk(KERN_WARNING "%s: No available SPUs.\n", __FUNCTION__); | 433 | printk(KERN_WARNING "%s: No available SPUs.\n", __FUNCTION__); |
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 20f4e51d1069..5bb75f22f722 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h | |||
@@ -37,6 +37,9 @@ enum { | |||
37 | 37 | ||
38 | struct spu_context_ops; | 38 | struct spu_context_ops; |
39 | 39 | ||
40 | #define SPU_CONTEXT_PREEMPT_nr 0UL | ||
41 | #define SPU_CONTEXT_PREEMPT (1UL << SPU_CONTEXT_PREEMPT_nr) | ||
42 | |||
40 | struct spu_context { | 43 | struct spu_context { |
41 | struct spu *spu; /* pointer to a physical SPU */ | 44 | struct spu *spu; /* pointer to a physical SPU */ |
42 | struct spu_state csa; /* SPU context save area. */ | 45 | struct spu_state csa; /* SPU context save area. */ |
@@ -55,6 +58,8 @@ struct spu_context { | |||
55 | struct fasync_struct *ibox_fasync; | 58 | struct fasync_struct *ibox_fasync; |
56 | struct fasync_struct *wbox_fasync; | 59 | struct fasync_struct *wbox_fasync; |
57 | struct spu_context_ops *ops; | 60 | struct spu_context_ops *ops; |
61 | struct work_struct reap_work; | ||
62 | u64 flags; | ||
58 | }; | 63 | }; |
59 | 64 | ||
60 | /* SPU context query/set operations. */ | 65 | /* SPU context query/set operations. */ |
diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h index dd91ed8563d2..698c4cb08c60 100644 --- a/include/asm-powerpc/spu.h +++ b/include/asm-powerpc/spu.h | |||
@@ -129,6 +129,7 @@ struct spu { | |||
129 | struct mm_struct *mm; | 129 | struct mm_struct *mm; |
130 | struct spu_context *ctx; | 130 | struct spu_context *ctx; |
131 | struct spu_runqueue *rq; | 131 | struct spu_runqueue *rq; |
132 | unsigned long long timestamp; | ||
132 | pid_t pid; | 133 | pid_t pid; |
133 | int prio; | 134 | int prio; |
134 | int class_0_pending; | 135 | int class_0_pending; |