linux/drivers/gpu/drm/i915/i915_mm.c

165 lines
4.6 KiB
C
Raw Permalink Normal View History

/*
* Copyright © 2014 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include <linux/mm.h>
#include <linux/io-mapping.h>
#include "i915_drv.h"
#include "i915_mm.h"
struct remap_pfn {
struct mm_struct *mm;
unsigned long pfn;
pgprot_t prot;
struct sgt_iter sgt;
resource_size_t iobase;
};
#define use_dma(io) ((io) != -1)
static inline unsigned long sgt_pfn(const struct remap_pfn *r)
{
if (use_dma(r->iobase))
return (r->sgt.dma + r->sgt.curr + r->iobase) >> PAGE_SHIFT;
else
return r->sgt.pfn + (r->sgt.curr >> PAGE_SHIFT);
}
static int remap_sg(pte_t *pte, unsigned long addr, void *data)
{
struct remap_pfn *r = data;
if (GEM_WARN_ON(!r->sgt.sgp))
return -EINVAL;
/* Special PTE are not associated with any struct page */
set_pte_at(r->mm, addr, pte,
pte_mkspecial(pfn_pte(sgt_pfn(r), r->prot)));
r->pfn++; /* track insertions in case we need to unwind later */
r->sgt.curr += PAGE_SIZE;
if (r->sgt.curr >= r->sgt.max)
r->sgt = __sgt_iter(__sg_next(r->sgt.sgp), use_dma(r->iobase));
return 0;
}
#define EXPECTED_FLAGS (VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP)
#if IS_ENABLED(CONFIG_X86)
static int remap_pfn(pte_t *pte, unsigned long addr, void *data)
{
struct remap_pfn *r = data;
/* Special PTE are not associated with any struct page */
set_pte_at(r->mm, addr, pte, pte_mkspecial(pfn_pte(r->pfn, r->prot)));
r->pfn++;
return 0;
}
Revert "i915: use io_mapping_map_user" This reverts commit b739f125e4ebd73d10ed30a856574e13649119ed. We are unfortunately seeing more issues like we did in 293837b9ac8d ("Revert "i915: fix remap_io_sg to verify the pgprot""), except this is now for the vm_fault_gtt path, where we are now hitting the same BUG_ON(!pte_none(*pte)): [10887.466150] kernel BUG at mm/memory.c:2183! [10887.466162] invalid opcode: 0000 [#1] PREEMPT SMP PTI [10887.466168] CPU: 0 PID: 7775 Comm: ffmpeg Tainted: G U 5.13.0-rc3-CI-Nightly #1 [10887.466174] Hardware name: To Be Filled By O.E.M. To Be Filled By O.E.M./J4205-ITX, BIOS P1.40 07/14/2017 [10887.466177] RIP: 0010:remap_pfn_range_notrack+0x30f/0x440 [10887.466188] Code: e8 96 d7 e0 ff 84 c0 0f 84 27 01 00 00 48 ba 00 f0 ff ff ff ff 0f 00 4c 89 e0 48 c1 e0 0c 4d 85 ed 75 96 48 21 d0 31 f6 eb a9 <0f> 0b 48 39 37 0f 85 0e 01 00 00 48 8b 0c 24 48 39 4f 08 0f 85 00 [10887.466193] RSP: 0018:ffffc90006e33c50 EFLAGS: 00010286 [10887.466198] RAX: 800000000000002f RBX: 00007f5e01800000 RCX: 0000000000000028 [10887.466201] RDX: 0000000000000001 RSI: ffffea0000000000 RDI: 0000000000000000 [10887.466204] RBP: ffffea000033fea8 R08: 800000000000002f R09: ffff8881072256e0 [10887.466207] R10: ffffc9000b84fff8 R11: 0000000017dab000 R12: 0000000000089f9f [10887.466210] R13: 800000000000002f R14: 00007f5e017e4000 R15: ffff88800cffaf20 [10887.466213] FS: 00007f5e04849640(0000) GS:ffff888278000000(0000) knlGS:0000000000000000 [10887.466216] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [10887.466220] CR2: 00007fd9b191a2ac CR3: 00000001829ac000 CR4: 00000000003506f0 [10887.466223] Call Trace: [10887.466233] vm_fault_gtt+0x1ca/0x5d0 [i915] [10887.466381] ? ktime_get+0x38/0x90 [10887.466389] __do_fault+0x37/0x90 [10887.466395] __handle_mm_fault+0xc46/0x1200 [10887.466402] handle_mm_fault+0xce/0x2a0 [10887.466407] do_user_addr_fault+0x1c5/0x660 Reverting this commit is reported to fix the issue. Reported-by: Eero Tamminen <eero.t.tamminen@intel.com> References: https://gitlab.freedesktop.org/drm/intel/-/issues/3519 Fixes: b739f125e4eb ("i915: use io_mapping_map_user") Cc: Christoph Hellwig <hch@lst.de> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Matthew Auld <matthew.auld@intel.com> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: https://patchwork.freedesktop.org/patch/msgid/20210527185145.458021-1-matthew.auld@intel.com
2021-05-27 19:51:45 +01:00
/**
* remap_io_mapping - remap an IO mapping to userspace
* @vma: user vma to map to
* @addr: target user address to start at
* @pfn: physical address of kernel memory
* @size: size of map area
* @iomap: the source io_mapping
*
* Note: this is only safe if the mm semaphore is held when called.
*/
int remap_io_mapping(struct vm_area_struct *vma,
unsigned long addr, unsigned long pfn, unsigned long size,
struct io_mapping *iomap)
{
struct remap_pfn r;
int err;
GEM_BUG_ON((vma->vm_flags & EXPECTED_FLAGS) != EXPECTED_FLAGS);
/* We rely on prevalidation of the io-mapping to skip pfnmap tracking. */
Revert "i915: use io_mapping_map_user" This reverts commit b739f125e4ebd73d10ed30a856574e13649119ed. We are unfortunately seeing more issues like we did in 293837b9ac8d ("Revert "i915: fix remap_io_sg to verify the pgprot""), except this is now for the vm_fault_gtt path, where we are now hitting the same BUG_ON(!pte_none(*pte)): [10887.466150] kernel BUG at mm/memory.c:2183! [10887.466162] invalid opcode: 0000 [#1] PREEMPT SMP PTI [10887.466168] CPU: 0 PID: 7775 Comm: ffmpeg Tainted: G U 5.13.0-rc3-CI-Nightly #1 [10887.466174] Hardware name: To Be Filled By O.E.M. To Be Filled By O.E.M./J4205-ITX, BIOS P1.40 07/14/2017 [10887.466177] RIP: 0010:remap_pfn_range_notrack+0x30f/0x440 [10887.466188] Code: e8 96 d7 e0 ff 84 c0 0f 84 27 01 00 00 48 ba 00 f0 ff ff ff ff 0f 00 4c 89 e0 48 c1 e0 0c 4d 85 ed 75 96 48 21 d0 31 f6 eb a9 <0f> 0b 48 39 37 0f 85 0e 01 00 00 48 8b 0c 24 48 39 4f 08 0f 85 00 [10887.466193] RSP: 0018:ffffc90006e33c50 EFLAGS: 00010286 [10887.466198] RAX: 800000000000002f RBX: 00007f5e01800000 RCX: 0000000000000028 [10887.466201] RDX: 0000000000000001 RSI: ffffea0000000000 RDI: 0000000000000000 [10887.466204] RBP: ffffea000033fea8 R08: 800000000000002f R09: ffff8881072256e0 [10887.466207] R10: ffffc9000b84fff8 R11: 0000000017dab000 R12: 0000000000089f9f [10887.466210] R13: 800000000000002f R14: 00007f5e017e4000 R15: ffff88800cffaf20 [10887.466213] FS: 00007f5e04849640(0000) GS:ffff888278000000(0000) knlGS:0000000000000000 [10887.466216] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [10887.466220] CR2: 00007fd9b191a2ac CR3: 00000001829ac000 CR4: 00000000003506f0 [10887.466223] Call Trace: [10887.466233] vm_fault_gtt+0x1ca/0x5d0 [i915] [10887.466381] ? ktime_get+0x38/0x90 [10887.466389] __do_fault+0x37/0x90 [10887.466395] __handle_mm_fault+0xc46/0x1200 [10887.466402] handle_mm_fault+0xce/0x2a0 [10887.466407] do_user_addr_fault+0x1c5/0x660 Reverting this commit is reported to fix the issue. Reported-by: Eero Tamminen <eero.t.tamminen@intel.com> References: https://gitlab.freedesktop.org/drm/intel/-/issues/3519 Fixes: b739f125e4eb ("i915: use io_mapping_map_user") Cc: Christoph Hellwig <hch@lst.de> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Matthew Auld <matthew.auld@intel.com> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: https://patchwork.freedesktop.org/patch/msgid/20210527185145.458021-1-matthew.auld@intel.com
2021-05-27 19:51:45 +01:00
r.mm = vma->vm_mm;
r.pfn = pfn;
r.prot = __pgprot((pgprot_val(iomap->prot) & _PAGE_CACHE_MASK) |
(pgprot_val(vma->vm_page_prot) & ~_PAGE_CACHE_MASK));
err = apply_to_page_range(r.mm, addr, size, remap_pfn, &r);
if (unlikely(err)) {
zap_vma_ptes(vma, addr, (r.pfn - pfn) << PAGE_SHIFT);
return err;
}
return 0;
}
#endif
/**
* remap_io_sg - remap an IO mapping to userspace
* @vma: user vma to map to
* @addr: target user address to start at
* @size: size of map area
* @sgl: Start sg entry
* @offset: offset from the start of the page
* @iobase: Use stored dma address offset by this address or pfn if -1
*
* Note: this is only safe if the mm semaphore is held when called.
*/
int remap_io_sg(struct vm_area_struct *vma,
unsigned long addr, unsigned long size,
struct scatterlist *sgl, unsigned long offset,
resource_size_t iobase)
{
struct remap_pfn r = {
.mm = vma->vm_mm,
.prot = vma->vm_page_prot,
.sgt = __sgt_iter(sgl, use_dma(iobase)),
.iobase = iobase,
};
int err;
/* We rely on prevalidation of the io-mapping to skip pfnmap tracking. */
GEM_BUG_ON((vma->vm_flags & EXPECTED_FLAGS) != EXPECTED_FLAGS);
drm/i915: ensure segment offset never exceeds allowed max Commit 255fc1703e42 ("drm/i915/gem: Calculate object page offset for partial memory mapping") introduced a new offset, which accounts for userspace mapping not starting from the beginning of object's scatterlist. This works fine for cases where first object pte is larger than the new offset - "r->sgt.curr" counter is set to the offset to match the difference in the number of total pages. However, if object's first pte's size is equal to or smaller than the offset, then information about the offset in userspace is covered up by moving "r->sgt" pointer in remap_sg(): r->sgt.curr += PAGE_SIZE; if (r->sgt.curr >= r->sgt.max) r->sgt = __sgt_iter(__sg_next(r->sgt.sgp), use_dma(r->iobase)); This means that two or more pages from virtual memory are counted for only one page in object's memory, because after moving "r->sgt" pointer "r->sgt.curr" will be 0. We should account for this mismatch by moving "r->sgt" pointer to the next pte. For that we may use "r.sgt.max", which already holds the max allowed size. This change also eliminates possible confusion, when looking at i915_scatterlist.h and remap_io_sg() code: former has scatterlist pointer definition, which differentiates "s.max" value based on "dma" flag (sg_dma_len() is used only when the flag is enabled), while latter uses sg_dma_len() indiscriminately. This patch aims to resolve issue: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12031 v3: - instead of checking if r.sgt.curr would exceed allowed max, changed the value in the while loop to be aligned with `dma` value v4: - remove unnecessary parent relation v5: - update commit message with explanation about page counting mismatch and link to the issue Signed-off-by: Krzysztof Karas <krzysztof.karas@intel.com> Reviewed-by: Andi Shyti <andi.shyti@linux.intel.com> Signed-off-by: Andi Shyti <andi.shyti@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/upbjdavlbcxku63ns4vstp5kgbn2anxwewpmnppszgb67fn66t@tfclfgkqijue
2024-11-18 12:19:22 +00:00
while (offset >= r.sgt.max >> PAGE_SHIFT) {
offset -= r.sgt.max >> PAGE_SHIFT;
r.sgt = __sgt_iter(__sg_next(r.sgt.sgp), use_dma(iobase));
if (!r.sgt.sgp)
return -EINVAL;
}
r.sgt.curr = offset << PAGE_SHIFT;
if (!use_dma(iobase))
flush_cache_range(vma, addr, size);
err = apply_to_page_range(r.mm, addr, size, remap_sg, &r);
if (unlikely(err)) {
zap_vma_ptes(vma, addr, r.pfn << PAGE_SHIFT);
return err;
}
return 0;
}