| 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | /* |
| 3 | * SPU file system -- SPU context management |
| 4 | * |
| 5 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 |
| 6 | * |
| 7 | * Author: Arnd Bergmann <arndb@de.ibm.com> |
| 8 | */ |
| 9 | |
| 10 | #include <linux/fs.h> |
| 11 | #include <linux/mm.h> |
| 12 | #include <linux/slab.h> |
| 13 | #include <linux/atomic.h> |
| 14 | #include <linux/sched.h> |
| 15 | #include <linux/sched/mm.h> |
| 16 | |
| 17 | #include <asm/spu.h> |
| 18 | #include <asm/spu_csa.h> |
| 19 | #include "spufs.h" |
| 20 | #include "sputrace.h" |
| 21 | |
| 22 | |
| 23 | atomic_t nr_spu_contexts = ATOMIC_INIT(0); |
| 24 | |
| 25 | struct spu_context *alloc_spu_context(struct spu_gang *gang) |
| 26 | { |
| 27 | struct spu_context *ctx; |
| 28 | |
| 29 | ctx = kzalloc(sizeof *ctx, GFP_KERNEL); |
| 30 | if (!ctx) |
| 31 | goto out; |
| 32 | /* Binding to physical processor deferred |
| 33 | * until spu_activate(). |
| 34 | */ |
| 35 | if (spu_init_csa(csa: &ctx->csa)) |
| 36 | goto out_free; |
| 37 | spin_lock_init(&ctx->mmio_lock); |
| 38 | mutex_init(&ctx->mapping_lock); |
| 39 | kref_init(kref: &ctx->kref); |
| 40 | mutex_init(&ctx->state_mutex); |
| 41 | mutex_init(&ctx->run_mutex); |
| 42 | init_waitqueue_head(&ctx->ibox_wq); |
| 43 | init_waitqueue_head(&ctx->wbox_wq); |
| 44 | init_waitqueue_head(&ctx->stop_wq); |
| 45 | init_waitqueue_head(&ctx->mfc_wq); |
| 46 | init_waitqueue_head(&ctx->run_wq); |
| 47 | ctx->state = SPU_STATE_SAVED; |
| 48 | ctx->ops = &spu_backing_ops; |
| 49 | ctx->owner = get_task_mm(current); |
| 50 | INIT_LIST_HEAD(list: &ctx->rq); |
| 51 | INIT_LIST_HEAD(list: &ctx->aff_list); |
| 52 | if (gang) |
| 53 | spu_gang_add_ctx(gang, ctx); |
| 54 | |
| 55 | __spu_update_sched_info(ctx); |
| 56 | spu_set_timeslice(ctx); |
| 57 | ctx->stats.util_state = SPU_UTIL_IDLE_LOADED; |
| 58 | ctx->stats.tstamp = ktime_get_ns(); |
| 59 | |
| 60 | atomic_inc(v: &nr_spu_contexts); |
| 61 | goto out; |
| 62 | out_free: |
| 63 | kfree(objp: ctx); |
| 64 | ctx = NULL; |
| 65 | out: |
| 66 | return ctx; |
| 67 | } |
| 68 | |
| 69 | void destroy_spu_context(struct kref *kref) |
| 70 | { |
| 71 | struct spu_context *ctx; |
| 72 | ctx = container_of(kref, struct spu_context, kref); |
| 73 | spu_context_nospu_trace(destroy_spu_context__enter, ctx); |
| 74 | mutex_lock(&ctx->state_mutex); |
| 75 | spu_deactivate(ctx); |
| 76 | mutex_unlock(lock: &ctx->state_mutex); |
| 77 | spu_fini_csa(csa: &ctx->csa); |
| 78 | if (ctx->gang) |
| 79 | spu_gang_remove_ctx(gang: ctx->gang, ctx); |
| 80 | if (ctx->prof_priv_kref) |
| 81 | kref_put(kref: ctx->prof_priv_kref, release: ctx->prof_priv_release); |
| 82 | BUG_ON(!list_empty(&ctx->rq)); |
| 83 | atomic_dec(v: &nr_spu_contexts); |
| 84 | kfree(objp: ctx->switch_log); |
| 85 | kfree(objp: ctx); |
| 86 | } |
| 87 | |
| 88 | struct spu_context * get_spu_context(struct spu_context *ctx) |
| 89 | { |
| 90 | kref_get(kref: &ctx->kref); |
| 91 | return ctx; |
| 92 | } |
| 93 | |
| 94 | int put_spu_context(struct spu_context *ctx) |
| 95 | { |
| 96 | return kref_put(kref: &ctx->kref, release: &destroy_spu_context); |
| 97 | } |
| 98 | |
| 99 | /* give up the mm reference when the context is about to be destroyed */ |
| 100 | void spu_forget(struct spu_context *ctx) |
| 101 | { |
| 102 | struct mm_struct *mm; |
| 103 | |
| 104 | /* |
| 105 | * This is basically an open-coded spu_acquire_saved, except that |
| 106 | * we don't acquire the state mutex interruptible, and we don't |
| 107 | * want this context to be rescheduled on release. |
| 108 | */ |
| 109 | mutex_lock(&ctx->state_mutex); |
| 110 | if (ctx->state != SPU_STATE_SAVED) |
| 111 | spu_deactivate(ctx); |
| 112 | |
| 113 | mm = ctx->owner; |
| 114 | ctx->owner = NULL; |
| 115 | mmput(mm); |
| 116 | spu_release(ctx); |
| 117 | } |
| 118 | |
| 119 | void spu_unmap_mappings(struct spu_context *ctx) |
| 120 | { |
| 121 | mutex_lock(&ctx->mapping_lock); |
| 122 | if (ctx->local_store) |
| 123 | unmap_mapping_range(mapping: ctx->local_store, holebegin: 0, holelen: LS_SIZE, even_cows: 1); |
| 124 | if (ctx->mfc) |
| 125 | unmap_mapping_range(mapping: ctx->mfc, holebegin: 0, SPUFS_MFC_MAP_SIZE, even_cows: 1); |
| 126 | if (ctx->cntl) |
| 127 | unmap_mapping_range(mapping: ctx->cntl, holebegin: 0, SPUFS_CNTL_MAP_SIZE, even_cows: 1); |
| 128 | if (ctx->signal1) |
| 129 | unmap_mapping_range(mapping: ctx->signal1, holebegin: 0, SPUFS_SIGNAL_MAP_SIZE, even_cows: 1); |
| 130 | if (ctx->signal2) |
| 131 | unmap_mapping_range(mapping: ctx->signal2, holebegin: 0, SPUFS_SIGNAL_MAP_SIZE, even_cows: 1); |
| 132 | if (ctx->mss) |
| 133 | unmap_mapping_range(mapping: ctx->mss, holebegin: 0, SPUFS_MSS_MAP_SIZE, even_cows: 1); |
| 134 | if (ctx->psmap) |
| 135 | unmap_mapping_range(mapping: ctx->psmap, holebegin: 0, SPUFS_PS_MAP_SIZE, even_cows: 1); |
| 136 | mutex_unlock(lock: &ctx->mapping_lock); |
| 137 | } |
| 138 | |
| 139 | /** |
| 140 | * spu_acquire_saved - lock spu contex and make sure it is in saved state |
| 141 | * @ctx: spu contex to lock |
| 142 | */ |
| 143 | int spu_acquire_saved(struct spu_context *ctx) |
| 144 | { |
| 145 | int ret; |
| 146 | |
| 147 | spu_context_nospu_trace(spu_acquire_saved__enter, ctx); |
| 148 | |
| 149 | ret = spu_acquire(ctx); |
| 150 | if (ret) |
| 151 | return ret; |
| 152 | |
| 153 | if (ctx->state != SPU_STATE_SAVED) { |
| 154 | set_bit(nr: SPU_SCHED_WAS_ACTIVE, addr: &ctx->sched_flags); |
| 155 | spu_deactivate(ctx); |
| 156 | } |
| 157 | |
| 158 | return 0; |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * spu_release_saved - unlock spu context and return it to the runqueue |
| 163 | * @ctx: context to unlock |
| 164 | */ |
| 165 | void spu_release_saved(struct spu_context *ctx) |
| 166 | { |
| 167 | BUG_ON(ctx->state != SPU_STATE_SAVED); |
| 168 | |
| 169 | if (test_and_clear_bit(nr: SPU_SCHED_WAS_ACTIVE, addr: &ctx->sched_flags) && |
| 170 | test_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags)) |
| 171 | spu_activate(ctx, flags: 0); |
| 172 | |
| 173 | spu_release(ctx); |
| 174 | } |
| 175 | |
| 176 | |