aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2005-12-05 22:52:26 -0500
committerPaul Mackerras <paulus@samba.org>2006-01-08 22:52:58 -0500
commit2a911f0bb73e67826062b7d073dd7367ca449724 (patch)
treec1d8d4f340cf7571722b2d9019f158acd345cff4
parent5110459f181ef1f11200bb3dec61953f08cc49e7 (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.c4
-rw-r--r--arch/powerpc/platforms/cell/spufs/sched.c81
-rw-r--r--arch/powerpc/platforms/cell/spufs/spufs.h5
-rw-r--r--include/asm-powerpc/spu.h1
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)
49struct spu_prio_array { 51struct 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
185static inline void unbind_context(struct spu *spu, struct spu_context *ctx) 189static 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
203static struct spu *preempt_active(struct spu_runqueue *rq) 210static 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; 231static 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
245static 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
234static struct spu *get_idle_spu(struct spu_context *ctx, u64 flags) 263static 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
38struct spu_context_ops; 38struct spu_context_ops;
39 39
40#define SPU_CONTEXT_PREEMPT_nr 0UL
41#define SPU_CONTEXT_PREEMPT (1UL << SPU_CONTEXT_PREEMPT_nr)
42
40struct spu_context { 43struct 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;