diff options
Diffstat (limited to 'kernel/gsp_sema_service.c')
| -rw-r--r-- | kernel/gsp_sema_service.c | 509 |
1 files changed, 347 insertions, 162 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 9edb66d..72480a2 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -1088,7 +1088,7 @@ static void svc_add_map(u64 gpu_va, u64 pa); * finishes. VAs whose full scan found nothing are remembered and not rescanned * for SVC_TR_FAIL_HOLD. */ -#define SVC_TR_SLICE (128ULL << 20) /* guest RAM covered per call */ +#define SVC_TR_SLICE (512ULL << 20) /* guest RAM covered per call */ #define SVC_TR_CHUNK (64 * 1024) /* one guest read */ #define SVC_TR_FAIL_MAX 16 #define SVC_TR_FAIL_HOLD (60 * HZ) @@ -1096,6 +1096,7 @@ static void svc_add_map(u64 gpu_va, u64 pa); static u64 tr_va; /* page VA of the active scan, 0 = idle */ static u64 tr_cursor; /* next GPA to read */ static unsigned long tr_start; /* active scan start (staleness bound) */ +static unsigned long tr_last; /* last lookup of the active scan's VA */ static u64 tr_scans; static struct { u64 va; @@ -1134,13 +1135,27 @@ static int svc_targeted_resolve(struct nv_vgpu_device *vgpu, u64 gpu_va, if (svc_tr_recently_failed(page_va)) return -ENOENT; - if (tr_va && tr_va != page_va) - return -EAGAIN; /* another VA owns the active scan */ + if (tr_va && tr_va != page_va) { + /* + * The scan slot is owned by another page. If that page is + * still being looked up (the pump retries its GPFIFO entry + * every pass) let it finish; if it has not been seen for a + * second, its entry was consumed or skipped and the slot + * would otherwise stay wedged for the full 30 s scan window, + * starving every live page behind it (observed: a skipped GR + * entry left CE memops pages unresolvable until the scan + * timed out). + */ + if (time_before(jiffies, tr_last + HZ)) + return -EAGAIN; /* another VA owns the active scan */ + tr_va = 0; + } if (!tr_va) { tr_va = page_va; tr_cursor = 0; tr_start = jiffies; } + tr_last = jiffies; if (time_after(jiffies, tr_start + 30 * HZ)) { /* The guest stopped giving us its memory: abandon the scan. */ svc_tr_note_failure(tr_va); @@ -1213,6 +1228,22 @@ static int svc_targeted_resolve(struct nv_vgpu_device *vgpu, u64 gpu_va, } /* + * True while a full-RAM targeted scan for this VA's page owns the scan slot + * (and has not aged out). The pump uses it to hold an entry whose translation + * may land within the scan instead of consuming it as stale. + */ +static bool svc_resolve_in_progress(u64 va) +{ + u64 page = va & ~(u64)(SEMA_PAGE_SIZE - 1); + + if (!tr_va || tr_va != page) + return false; + if (time_after(jiffies, tr_start + 30 * HZ)) + return false; + return true; +} + +/* * Translate a GPU VA using trusted sources only: a pinned translation, the * learned cache, the discovered map, the directories the driver published and * the self-discovered directory. The final vidmem window convention is @@ -1722,6 +1753,7 @@ static void svc_rebuild(struct nv_vgpu_device *vgpu) #define DMA_SEM_REDUCTION(v) (((v) >> 14) & 0xf) #define DMA_SEM_REDUCTION_EN(v) (((v) >> 19) & 0x1) #define SEM_RELEASE_ONE_WORD 1 +#define SEM_RELEASE_FOUR_WORD 2 #define SEM_REDUCTION_INC 6 /* Aperture targets (SET_*_PHYS_MODE). */ @@ -1947,25 +1979,6 @@ static bool svc_method_stream_plausible(const u8 *buf, u32 len) } /* - * Relaxed push-prefix check for rescue candidate validation only. Real pushes - * observed in this workload start with methods at non-zero addresses (e.g. - * 0x2001001e = count 1, addr 0x78), which the strict check above rejects - * because it requires addr 0. Rescue only needs the candidate page to begin - * a plausible method stream; work execution still uses the strict check. - */ -static bool svc_stream_prefix_ok(const u8 *buf, u32 len) -{ - u32 hdr = 0, op, count; - - if (len < 8) - return false; - memcpy(&hdr, buf, 4); - op = (hdr >> 29) & 0x7; - count = (hdr >> 16) & 0xfff; - return op == 1 && count && count <= 64 && 4 + count * 4 <= len; -} - -/* * Decode one page as a method stream and execute the pushes it contains. * * Push buffers are written back to back and pushes can span page @@ -2320,8 +2333,18 @@ static void svc_decode_stream(struct nv_vgpu_device *vgpu, const u8 *buf, } } + /* + * Both release encodings write the payload word first + * (NvGpuSemaphore.data[0] for the four-word format, the + * single word for the one-word format), and the client's + * trackers poll exactly that u32: apply the payload for + * either. The four-word format's timestamp/status words + * that follow are not observed by the waiters. + */ if (DMA_SEMAPHORE_TYPE(dma) == - SEM_RELEASE_ONE_WORD) { + SEM_RELEASE_ONE_WORD || + DMA_SEMAPHORE_TYPE(dma) == + SEM_RELEASE_FOUR_WORD) { u64 sem_va; u32 cur = 0; int pending = 0; @@ -3325,6 +3348,129 @@ static void svc_pb_pair(struct nv_vgpu_device *vgpu, struct svc_channel *ch) } /* + * Match one buffer for this channel's pool-semaphore release group and append + * candidate pin bases. The group is: + * +0x00 SET_SEMAPHORE_A header (0x20038090, count 3, subch 4) + * +0x04 hi == 1 + * +0x08 lo & 0xfff == this channel's pool slot + * +0x0c payload (any small value; the tracked expectation can lag) + * +0x10 LAUNCH_DMA semaphore-release follower (transfer none) + * Candidates are the page the group sits in (correct when the push starts in + * that page, e.g. a stale release push) and the pages implied by treating the + * group as the end of this entry's push, walking back a page at a time so a + * trailing method run after the release does not defeat the guess. Returns the + * updated candidate count. + */ +static u32 svc_rescue_match_buf(const u8 *buf, u32 len, u64 base_pa, u32 slot, + u64 entry_len, u64 *cand, u32 ncand) +{ + u32 o; + + for (o = 0; o + 24 <= len && ncand < SVC_MBUF_MAX; o += 4) { + u32 hdr, hi, lo, payload, w5, v5; + u64 rel_pa, d; + + memcpy(&hdr, buf + o, 4); + if (hdr != 0x20038090u) + continue; + memcpy(&hi, buf + o + 4, 4); + if (hi != 1) + continue; + memcpy(&lo, buf + o + 8, 4); + if ((lo & 0xfff) != slot) + continue; + memcpy(&payload, buf + o + 12, 4); + if (!payload || payload >= (1u << 24)) + continue; + memcpy(&w5, buf + o + 16, 4); + memcpy(&v5, buf + o + 20, 4); + if ((w5 >> 29) != 1 || ((w5 >> 13) & 0x7) != 4 || + ((w5 & 0x1fff) << 2) != 0x300 || + (v5 & 0x3) != 0) + continue; + + rel_pa = base_pa + o; + cand[ncand++] = rel_pa & ~(u64)(SEMA_PAGE_SIZE - 1); + for (d = entry_len >= 24 ? entry_len - 24 : 0; + d > 0 && ncand < SVC_MBUF_MAX; d -= SEMA_PAGE_SIZE) { + u64 s = (rel_pa - d) & ~(u64)(SEMA_PAGE_SIZE - 1); + + if (s != cand[ncand - 1]) + cand[ncand++] = s; + } + } + return ncand; +} + +/* + * Full method-stream parse: every word run must be a valid INC_METHOD header + * covering the entire buffer, nothing left over. Real pushes satisfy this by + * construction; arbitrary data (firmware pages, page tables, allocator free + * lists) practically never does - a false candidate must pass this test for + * the whole push, not just for its first header. + */ +static bool svc_stream_full_ok(const u8 *buf, u32 len) +{ + u32 off = 0, methods = 0; + + while (off + 4 <= len) { + u32 hdr, op, count; + + memcpy(&hdr, buf + off, 4); + if (hdr == 0) + break; /* trailing zero padding */ + op = (hdr >> 29) & 0x7; + count = (hdr >> 16) & 0xfff; + if (op != 1 || !count || count > 64 || + off + 4 + count * 4 > len) + return false; + off += 4 + count * 4; + if (++methods > 1024) + return false; + } + /* Trailing zero padding is common in recycled push slots. */ + while (off < len && buf[off] == 0) + off++; + return methods > 0 && off == len; +} + +/* + * Pin a candidate page as the translation of the entry VA's page and verify + * by reading the whole push back and parsing it as a method stream. Requires + * the push to end exactly on a method boundary - the stream replaces a weak + * prefix check that accepted page-table pages (whose first word can decode as + * a header). Returns 0 and leaves the pin installed on success. + */ +static int svc_rescue_try(struct nv_vgpu_device *vgpu, struct svc_channel *ch, + u64 entry_va, u32 entry_len, u32 pin_len, u64 cand_pa, + u32 cand_i, u32 ncand) +{ + u8 *full; + u32 j; + int rc = -ENOENT; + + for (j = 0; j < pin_len; j += SEMA_PAGE_SIZE) { + svc_pin_store(entry_va + j, cand_pa + j, SVC_APERTURE_SYS); + svc_lat_store(entry_va + j, cand_pa + j, SVC_APERTURE_SYS); + } + + full = kmalloc(entry_len, GFP_KERNEL); + if (!full) + return -ENOMEM; + if (!svc_read_va_trusted(vgpu, entry_va, full, entry_len) && + svc_stream_full_ok(full, entry_len)) { + for (j = 0; j < pin_len; j += SEMA_PAGE_SIZE) + svc_mark_hot(cand_pa + j); + pr_info("sema-svc: content rescue ch=0x%x rank=%u slot=%u entry_va=0x%llx len=%u -> pa=0x%llx (cand %u/%u)\n", + ch->h_object, ch->ce_index, 4 * ch->ce_index, entry_va, + entry_len, cand_pa, cand_i + 1, ncand); + rc = 0; + } + kfree(full); + return rc; +} + +/* * Content rescue for unmapped push-buffer pool VAs. * When the pump encounters a GPFIFO entry whose push-buffer VA is not in the * VA->PA map, we scan guest RAM for a page containing a push whose semaphore @@ -3347,103 +3493,95 @@ static int svc_rescue_push_page(struct nv_vgpu_device *vgpu, return -ENOENT; if (entry_va < SEMA_VA_LO || entry_va >= SEMA_VA_HI) return -EINVAL; - if (time_before(jiffies, ch->rescue_last + 5 * HZ)) - return -EAGAIN; - ch->rescue_last = jiffies; /* * Slot-aligned entries pin the whole 1 MB slot at once (all of its * pushes share the mapping); an entry in the middle of a slot only - * needs the pages its own push occupies - candidate 2 below is the - * PA of the entry VA's page in both cases. + * needs the pages its own push occupies - the page-walking candidates + * in svc_rescue_match_buf cover the push starting on a page before the + * release. */ pin_len = (entry_va & 0xfffff) ? ALIGN(entry_len + SEMA_PAGE_SIZE, SEMA_PAGE_SIZE) : 0x100000; - buf = kmalloc(SVC_SCAN_CHUNK, GFP_KERNEL); + buf = kmalloc(SEMA_PAGE_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; /* - * Scan guest RAM for the exact release group at any 4-byte offset: - * +0x00 SET_SEMAPHORE_A header (0x20038090, count 3, subch 4) - * +0x04 hi == 1 - * +0x08 lo & 0xfff == this channel's pool slot - * +0x0c payload (any small value; the tracked expectation can lag) - * +0x10 LAUNCH_DMA semaphore-release follower (type 0) - * The header is checked first so the inner loop is one compare for - * almost every offset; the remaining exact fields make false - * positives (the firmware-page matches of the old matcher) - * negligible. - * - * Each match yields two candidate pin bases: the page the group sits - * in (correct when the matched group is in the slot's first page, - * e.g. a stale release push) and the page implied by treating the - * group as the end of THIS entry's push (correct when the push starts - * at the slot base and is longer than one page). Each candidate is - * validated by pinning and reading the entry VA back; the first that - * parses as a push wins. + * Hot pages first: pushbuffer pages are recycled and this workload + * touches only a small working set, so the page holding a just-written + * push is almost always already marked hot. Scanning the hot set is + * milliseconds - the client's waits expire long before the multi- + * second full-RAM scan below can run, and that scan also competes + * with the targeted resolver and the sweeps for the service thread. */ - for (gpa = 0; gpa < SEMA_PA_MAX && ncand < SVC_MBUF_MAX; gpa += SVC_SCAN_CHUNK) { - u32 o; + { + unsigned long h; - if (svc_read(vgpu, gpa, buf, SVC_SCAN_CHUNK)) - continue; - for (o = 0; o + 24 <= SVC_SCAN_CHUNK && ncand < SVC_MBUF_MAX; o += 4) { - u32 hdr, hi, lo, payload, w5, v5; - u64 rel_pa, s; + for (h = find_next_bit(svc_hot, SEMA_HOT_PAGES, 0); + h < SEMA_HOT_PAGES; + h = find_next_bit(svc_hot, SEMA_HOT_PAGES, h + 1)) { + u32 n; - memcpy(&hdr, buf + o, 4); - if (hdr != 0x20038090u) - continue; - memcpy(&hi, buf + o + 4, 4); - if (hi != 1) - continue; - memcpy(&lo, buf + o + 8, 4); - if ((lo & 0xfff) != slot) - continue; - memcpy(&payload, buf + o + 12, 4); - if (!payload || payload >= (1u << 24)) + gpa = (u64)h << SEMA_PAGE_SHIFT; + if (svc_read(vgpu, gpa, buf, SEMA_PAGE_SIZE)) continue; - memcpy(&w5, buf + o + 16, 4); - memcpy(&v5, buf + o + 20, 4); - if ((w5 >> 29) != 1 || ((w5 >> 13) & 0x7) != 4 || - ((w5 & 0x1fff) << 2) != 0x300 || - (v5 & 0x3) != 0) - continue; - - rel_pa = gpa + o; - cand[ncand++] = rel_pa & ~(u64)(SEMA_PAGE_SIZE - 1); - s = (rel_pa - (entry_len >= 24 ? entry_len - 24 : 0)) & - ~(u64)(SEMA_PAGE_SIZE - 1); - if (ncand < SVC_MBUF_MAX && - (ncand == 0 || cand[ncand - 1] != s)) - cand[ncand++] = s; + ncand = svc_rescue_match_buf(buf, SEMA_PAGE_SIZE, gpa, + slot, entry_len, cand, 0); + for (n = 0; n < ncand; n++) { + if (!svc_rescue_try(vgpu, ch, entry_va, + entry_len, pin_len, + cand[n], n, ncand)) { + kfree(buf); + return 0; + } + } } } + + /* + * A full-RAM targeted scan for this exact page is already running (it + * is triggered by the failed read above): let it finish instead of + * duplicating the RAM scan, and let the pump retry the entry next pass + * while the scan's result is still pending. + */ + if (svc_resolve_in_progress(entry_va)) { + kfree(buf); + return -EAGAIN; + } + if (time_before(jiffies, ch->rescue_last + 2 * HZ)) { + kfree(buf); + return -EAGAIN; + } + ch->rescue_last = jiffies; + kfree(buf); + buf = kmalloc(SVC_SCAN_CHUNK, GFP_KERNEL); + if (!buf) + return -ENOMEM; - for (i = 0; i < ncand; i++) { - u8 probe[64]; - u32 j; - - for (j = 0; j < pin_len; j += SEMA_PAGE_SIZE) { - svc_pin_store(entry_va + j, cand[i] + j, - SVC_APERTURE_SYS); - svc_lat_store(entry_va + j, cand[i] + j, - SVC_APERTURE_SYS); - } - if (!svc_read_va_trusted(vgpu, entry_va, probe, - sizeof(probe)) && - svc_stream_prefix_ok(probe, sizeof(probe))) { - for (j = 0; j < pin_len; j += SEMA_PAGE_SIZE) - svc_mark_hot(cand[i] + j); - pr_info("sema-svc: content rescue ch=0x%x rank=%u slot=%u entry_va=0x%llx len=%u -> pa=0x%llx (cand %u/%u)\n", - ch->h_object, ch->ce_index, slot, entry_va, - entry_len, cand[i], i + 1, ncand); - return 0; + /* + * Full-RAM fallback: the header is checked first so the inner loop is + * one compare for almost every offset; the remaining exact fields make + * false positives (the firmware-page matches of the old matcher) + * negligible. Each candidate is validated by pinning and reading the + * entry VA back; the first that parses as a push wins. + */ + for (gpa = 0; gpa < SEMA_PA_MAX; gpa += SVC_SCAN_CHUNK) { + if (svc_read(vgpu, gpa, buf, SVC_SCAN_CHUNK)) + continue; + ncand = svc_rescue_match_buf(buf, SVC_SCAN_CHUNK, gpa, slot, + entry_len, cand, 0); + for (i = 0; i < ncand; i++) { + if (!svc_rescue_try(vgpu, ch, entry_va, entry_len, + pin_len, cand[i], i, ncand)) { + kfree(buf); + return 0; + } } } + kfree(buf); if (ncand) pr_info("sema-svc: content rescue ch=0x%x entry_va=0x%llx len=%u: %u candidate(s) did not validate\n", @@ -3711,7 +3849,8 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) svc.gpfifo_nomap++; kfree(pb); break; - } else if (++ch->rescue_fail >= 3) { + } else if (++ch->rescue_fail >= + (ch->engine_type ? 2 : 1)) { /* * The slot no longer holds this * push (overwritten buffer): the @@ -3719,6 +3858,21 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) * candidate. Consume the entry so * the pump moves on instead of * rescanning RAM every 5 s. + * + * GR (compute) channels have no + * rescue at all - their pushes + * are compute-class method + * streams the CE-shaped matcher + * cannot validate - and their + * completion signal is the + * per-channel counter we publish + * from `put`. Consume on the + * first failed attempt: a slow + * skip delays GPGet and the + * counter long enough for the + * client's submission wait to + * expire (observed at ~12 s with + * the 3-attempt path). */ pr_info("sema-chan: skipping stale entry h=0x%x va=0x%llx len=%u after %u failed rescues\n", ch->h_object, va, len, @@ -3772,8 +3926,8 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) * Persist the per-entry rescue state and the * expected payload: the pump works on a snapshot * copy, so without the write-back the stale-entry - * skip (rescue_fail >= 3) and the 5 s rescue rate - * limit (rescue_last) can never fire and the + * skip (rescue_fail >= threshold) and the rescue + * rate limit (rescue_last) can never fire and the * payload expectation is reinitialized every * pass. */ @@ -4037,90 +4191,121 @@ int gsp_sema_service_dump_channels(void) * discoverable from the structures the service scans; this lets a session * feed a pair that was verified from the guest and watch push execution use * it. + * + * The injection is fully asynchronous: the sysfs write only enqueues the + * pair and returns, so a batch of mappings lands in microseconds even while + * the service thread is deep in a scan. Timed-out single-slot injections + * used to be cancelled (the pair was silently lost), and single-slot params + * could not take a batch at all. */ -static struct completion map_add_done; -static u64 map_add_va; -static u64 map_add_pa; -static u64 map_add_len; -static u32 map_add_ap; -static bool map_add_pending; #define SVC_PIN_REWIND 8 +#define SVC_MAP_ADD_MAX 64 + +struct svc_map_add_req { + u64 va; + u64 pa; + u64 len; + u32 ap; +}; + +static struct svc_map_add_req svc_map_add_q[SVC_MAP_ADD_MAX]; +static u32 svc_map_add_head; /* producer */ +static u32 svc_map_add_tail; /* consumer (service thread) */ +static DEFINE_SPINLOCK(svc_map_add_lock); static void svc_handle_map_add(void) { - u32 ap = map_add_ap ? map_add_ap : SVC_APERTURE_SYS; unsigned long flags; - u64 off; - int i; - if (!map_add_pending) - return; + for (;;) { + struct svc_map_add_req req; + u32 ap, i; + u64 off; - for (off = 0; off < (map_add_len ? map_add_len : 1); - off += SEMA_PAGE_SIZE) { - svc_pin_store(map_add_va + off, map_add_pa + off, ap); - svc_lat_store(map_add_va + off, map_add_pa + off, ap); - } - /* - * A translation can be replaced after the pump already consumed - * pushes that release through it (a new client maps new physical - * pages at the same VA, or the first translation was wrong): the - * releases resolved against the old page, compared as stale against - * its values, and were dropped. Rewind every channel a few ring - * entries so those pushes are re-read under the new translation; - * the release/fill application paths are forward-progress guarded, - * so re-executing them is idempotent. - */ - spin_lock_irqsave(&svc.chan_lock, flags); - for (i = 0; i < SVC_MAX_CHANNELS; i++) { - struct svc_channel *ch = &svc.chan[i]; - u32 get; + spin_lock_irqsave(&svc_map_add_lock, flags); + if (svc_map_add_tail == svc_map_add_head) { + spin_unlock_irqrestore(&svc_map_add_lock, flags); + break; + } + req = svc_map_add_q[svc_map_add_tail % + SVC_MAP_ADD_MAX]; + svc_map_add_tail++; + spin_unlock_irqrestore(&svc_map_add_lock, flags); + + ap = req.ap ? req.ap : SVC_APERTURE_SYS; + for (off = 0; off < (req.len ? req.len : 1); + off += SEMA_PAGE_SIZE) { + svc_pin_store(req.va + off, req.pa + off, ap); + svc_lat_store(req.va + off, req.pa + off, ap); + } + /* + * A translation can be replaced after the pump already + * consumed pushes that release through it (a new client maps + * new physical pages at the same VA, or the first translation + * was wrong): the releases resolved against the old page, + * compared as stale against its values, and were dropped. + * Rewind every channel a few ring entries so those pushes are + * re-read under the new translation; the release/fill + * application paths are forward-progress guarded, so + * re-executing them is idempotent. + */ + spin_lock_irqsave(&svc.chan_lock, flags); + for (i = 0; i < SVC_MAX_CHANNELS; i++) { + struct svc_channel *ch = &svc.chan[i]; + u32 get; + + if (!ch->valid || !ch->gpfifo_entries) + continue; + /* + * Clamp the rewind at 0: wrapping below zero (get=0 -> + * entries - N) makes the pump read ring slots past the + * producer that hold stale entries, so replay only + * recently consumed slots or from the start of the + * ring, never around the wrap. + */ + get = ch->last_get; + ch->last_get = (get >= SVC_PIN_REWIND) ? + get - SVC_PIN_REWIND : 0; + } + spin_unlock_irqrestore(&svc.chan_lock, flags); - if (!ch->valid || !ch->gpfifo_entries) - continue; /* - * Clamp the rewind at 0: wrapping below zero (get=0 -> entries - * - N) makes the pump read ring slots past the producer that - * hold stale entries, so replay only recently consumed slots - * or from the start of the ring, never around the wrap. + * Releases that missed translation before this injection may + * be applicable now: keep the fast full-RAM sweep armed for a + * few seconds so they land without waiting for an unrelated + * stall. */ - get = ch->last_get; - ch->last_get = (get >= SVC_PIN_REWIND) ? get - SVC_PIN_REWIND : 0; + svc.fast_force_until = jiffies + 10 * HZ; + pr_info("sema-svc: injected mapping va=0x%llx -> pa=0x%llx len=0x%llx (%s), rewound channels by %u\n", + req.va, req.pa, req.len, + ap == SVC_APERTURE_FB ? "vidmem" : "sysmem", + SVC_PIN_REWIND); } - spin_unlock_irqrestore(&svc.chan_lock, flags); - - /* - * Releases that missed translation before this injection may be - * applicable now: keep the fast full-RAM sweep armed for a few - * seconds so they land without waiting for an unrelated stall. - */ - svc.fast_force_until = jiffies + 10 * HZ; - pr_info("sema-svc: injected mapping va=0x%llx -> pa=0x%llx len=0x%llx (%s), rewound channels by %u\n", - map_add_va, map_add_pa, map_add_len, - ap == SVC_APERTURE_FB ? "vidmem" : "sysmem", SVC_PIN_REWIND); - map_add_pending = false; - complete(&map_add_done); } int gsp_sema_service_add_map(u64 gpu_va, u64 pa, u64 len, u32 ap) { + unsigned long flags; + u32 next; + if (!svc.task) return -ENODEV; if (len > (64ULL << 20) || ap > SVC_APERTURE_FB) return -EINVAL; - init_completion(&map_add_done); - map_add_va = gpu_va; - map_add_pa = pa; - map_add_len = len; - map_add_ap = ap; - map_add_pending = true; - - if (!wait_for_completion_timeout(&map_add_done, 3 * HZ)) { - map_add_pending = false; - return -ETIMEDOUT; + spin_lock_irqsave(&svc_map_add_lock, flags); + next = svc_map_add_head + 1; + if (next - svc_map_add_tail > SVC_MAP_ADD_MAX) { + spin_unlock_irqrestore(&svc_map_add_lock, flags); + return -ENOSPC; } + svc_map_add_q[svc_map_add_head % SVC_MAP_ADD_MAX].va = gpu_va; + svc_map_add_q[svc_map_add_head % SVC_MAP_ADD_MAX].pa = pa; + svc_map_add_q[svc_map_add_head % SVC_MAP_ADD_MAX].len = len; + svc_map_add_q[svc_map_add_head % SVC_MAP_ADD_MAX].ap = ap; + svc_map_add_head = next; + spin_unlock_irqrestore(&svc_map_add_lock, flags); return 0; } |
