From nobody Wed Jul 22 15:12:45 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4h4yRl0JwPz6mFFn for ; Wed, 22 Jul 2026 15:12:51 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4h4yRk4qjBz3kly for ; Wed, 22 Jul 2026 15:12:50 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1784733170; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=gTD7kcdMOF67ZEPNxR8oi8+JTDmcaAHwwwqFFhPR/FQ=; b=ifbXBnlvLpA/mVKS80dCRY7qbcNKBfJ5fQdDye0LtPHEhHPvaM1/QIimYnYcnNaEdprGyu JMU1uXOOL44uFTbqGkLAifmAsoJYaGsJj2zrMjB7lFSWgZbu/ZZIX6ZE1/zKhJujl2Or0A txGvPE7z1AkwPZdZzZKAryKXAEhWE+eGcLEFQlG8fKMBDicmtqGiP2X5aWBeNx3GPjxB8Y H3yoGIJVjwevRINu9C/BO8+B1otmfxwIOcbGUrxejd+LDMTr5lkXVGmXH2JglsGXYlGPIv P7CbfSTPeJ90B1+WJM1qUOObwPTVgehdhgda5iPRfJrKH8o2jSOPSC0cOgD19A== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1784733170; a=rsa-sha256; cv=none; b=sPwSi8Pzhn1bJYtC5yetyabQlm0A92SQykqtXBW/PFSua9BsTrdb/kyrTIrQw4kKcmJqS0 lAynHgIKTWfFnjAOZGNoiZ4qkhY9Cu25j2LZLrnS4Xavx9Bg/dj09VznrPs6QkGwi+zS0i LtpbvfUSWIT6bluxHl7xibzrhFRisIyZt/fxDboR+ptmhUEAJ9X3IK7eM1F5rYrzwDu9Vs VXrL659c1rQoaIi/VxSGSQePNWE2zdZv4NwGLqCria2I5aduwjpWsg5psvMArU44CxyU0/ ctRwdn4IX/visN0v28Cdofu/UzW7TZ0dYHeEEGDTnVBXV7ufBzm5qo4QsmaQZg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1784733170; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=gTD7kcdMOF67ZEPNxR8oi8+JTDmcaAHwwwqFFhPR/FQ=; b=fXGaYn6fwFK3cIK+3/0VZFN+QBhulz+ALkakTAPkD/gaYqJXYXuep9XxWyBohUo0iaHyIM anRu5l/FV66nklQLhKhD+RiODk9C7JDk0Zs2FzZv8gXyAmGaZBC0GcWX7opCYaaWm3hXH1 5C30APKpVpk9D3Ur86mUWCf9B6x6CBq5kLHcKV8VDkjycnKYNVe8DcoJCV9wF1FXHG28xC 6q3Dl5yWdemCjb1xjK032Ol7SZcThY8G71De4/ysC6lsDD+AStmrYTaQopYEbiLGpGC8Zb e/EPuVZ77DhO71bldokym4ZrYaQDV4Y+GPea6r2SD0v4DilljHrvRZOFEJy85w== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4h4yRk3VKbz7sm for ; Wed, 22 Jul 2026 15:12:50 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 33eb7 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 22 Jul 2026 15:12:45 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Baptiste Daroussin Subject: git: 4c94869c37e1 - main - uvideo: replace contigmalloc with OBJT_PHYS vm_object for mmap buffer List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: bapt X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 4c94869c37e14dc334e2400d1a26d5ded89576fc Auto-Submitted: auto-generated Date: Wed, 22 Jul 2026 15:12:45 +0000 Message-Id: <6a60dded.33eb7.43e089@gitrepo.freebsd.org> The branch main has been updated by bapt: URL: https://cgit.FreeBSD.org/src/commit/?id=4c94869c37e14dc334e2400d1a26d5ded89576fc commit 4c94869c37e14dc334e2400d1a26d5ded89576fc Author: Baptiste Daroussin AuthorDate: 2026-07-22 11:25:31 +0000 Commit: Baptiste Daroussin CommitDate: 2026-07-22 15:10:57 +0000 uvideo: replace contigmalloc with OBJT_PHYS vm_object for mmap buffer Allocate the mmap buffer via phys_pager_allocate() and map it into kernel space with vm_map_find()/vm_map_wire(), instead of a custom cdev_pager backed by contigmalloc. phys_pager_allocate() is required over a bare vm_object_allocate(OBJT_PHYS) to initialise un_pager.phys.ops, otherwise phys_pager_getpages() NULL-derefs during vm_map_wire(). Reviewed by: markj Reported by: markj Differential Revision: https://reviews.freebsd.org/D58394 --- sys/dev/usb/video/uvideo.c | 192 ++++++++++++--------------------------------- 1 file changed, 51 insertions(+), 141 deletions(-) diff --git a/sys/dev/usb/video/uvideo.c b/sys/dev/usb/video/uvideo.c index 07f74f55b121..bb4d42778998 100644 --- a/sys/dev/usb/video/uvideo.c +++ b/sys/dev/usb/video/uvideo.c @@ -46,10 +46,14 @@ #include #include -#include -#include +#include +#include #include +#include #include +#include +#include +#include #include #include @@ -168,25 +172,6 @@ static d_ioctl_t uvideo_cdev_ioctl; static d_poll_t uvideo_cdev_poll; static d_kqfilter_t uvideo_cdev_kqfilter; static d_mmap_single_t uvideo_cdev_mmap_single; -static int uvideo_pg_ctor(void *, vm_ooffset_t, vm_prot_t, - vm_ooffset_t, struct ucred *, u_short *); -static void uvideo_pg_dtor(void *); -static int uvideo_pg_fault(vm_object_t, vm_ooffset_t, int, - vm_page_t *); - -/* - * Per-buffer state for mmap lifetime management. Allocated together - * with the contig buffer at REQBUFS time and attached to a single - * shared vm_object whose reference count tracks the active mappings. - * The buffer is freed by cdev_pg_dtor() when the last reference (the - * softc's own or a user mapping) is dropped. Lives independently of - * the softc so the pager dtor can safely free the buffer even after - * device detach. - */ -struct uvideo_mmap_state { - uint8_t *ms_buffer; - size_t ms_buffer_size; -}; static int uvideo_querycap(struct uvideo_softc *, struct v4l2_capability *); static int uvideo_enum_fmt(struct uvideo_softc *, struct v4l2_fmtdesc *); @@ -258,6 +243,7 @@ struct uvideo_softc { struct uvideo_mmap *sc_mmap_cur; uint8_t *sc_mmap_buffer; size_t sc_mmap_buffer_size; + vm_offset_t sc_mmap_kva; int sc_mmap_buffer_idx; q_mmap sc_mmap_q; int sc_mmap_count; @@ -745,12 +731,6 @@ static const struct usb_config uvideo_bulk_config[1] = { }, }; -static const struct cdev_pager_ops uvideo_pager_ops = { - .cdev_pg_ctor = uvideo_pg_ctor, - .cdev_pg_dtor = uvideo_pg_dtor, - .cdev_pg_fault = uvideo_pg_fault, -}; - /* * Character device switch */ @@ -940,6 +920,7 @@ uvideo_attach(device_t dev) /* Init mmap queue */ STAILQ_INIT(&sc->sc_mmap_q); sc->sc_mmap_count = 0; + sc->sc_mmap_kva = 0; sc->sc_mmap_object = NULL; /* Allocate unit number and create character device */ @@ -2296,18 +2277,14 @@ uvideo_vs_free_frame(struct uvideo_softc *sc) fb->buf = NULL; } - if (sc->sc_mmap_object != NULL) { - /* - * Drop the softc's reference to the shared mmap object. - * If user-space mappings still exist, the object and its - * backing contig buffer stay alive until the last mapping - * is dropped, at which point uvideo_pg_dtor() frees them. - */ - vm_object_deallocate(sc->sc_mmap_object); - sc->sc_mmap_object = NULL; + if (sc->sc_mmap_kva != 0) { + vm_map_remove(kernel_map, sc->sc_mmap_kva, + sc->sc_mmap_kva + sc->sc_mmap_buffer_size); sc->sc_mmap_buffer = NULL; + sc->sc_mmap_kva = 0; sc->sc_mmap_buffer_size = 0; } + sc->sc_mmap_object = NULL; while (!STAILQ_EMPTY(&sc->sc_mmap_q)) STAILQ_REMOVE_HEAD(&sc->sc_mmap_q, q_frames); @@ -2896,11 +2873,6 @@ uvideo_cdev_close(struct cdev *dev, int flags, int fmt, struct thread *td) uvideo_vs_close(sc); } - /* - * Release mmap buffer. If there are still active mmap - * mappings, the actual contigfree() is deferred to the - * last uvideo_pg_dtor() invocation. - */ uvideo_vs_free_frame(sc); if (sc->sc_fbuffer != NULL) { @@ -3157,79 +3129,6 @@ uvideo_cdev_mmap_single(struct cdev *dev, vm_ooffset_t *offset, return (0); } -static int -uvideo_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, - vm_ooffset_t foff, struct ucred *cred, u_short *color) -{ - struct uvideo_mmap_state *ms = handle; - - if (ms->ms_buffer == NULL || foff + size > ms->ms_buffer_size) - return (EINVAL); - - *color = 0; - return (0); -} - -static void -uvideo_pg_dtor(void *handle) -{ - struct uvideo_mmap_state *ms = handle; - - /* - * The last reference to the shared mmap object is gone (either the - * softc released it, or the final user mapping was dropped). Free - * the backing contig buffer and the state. - */ - if (ms->ms_buffer != NULL) - contigfree(ms->ms_buffer, ms->ms_buffer_size, M_USBDEV); - free(ms, M_USBDEV); -} - -static int -uvideo_pg_fault(vm_object_t object, vm_ooffset_t offset, int prot, - vm_page_t *mres) -{ - struct uvideo_mmap_state *ms = object->un_pager.devp.handle; - vm_paddr_t paddr; - vm_page_t m; - - if (ms->ms_buffer == NULL) - return (VM_PAGER_FAIL); - - if (offset >= ms->ms_buffer_size) - return (VM_PAGER_FAIL); - - paddr = vtophys(ms->ms_buffer + offset); - if (paddr == 0) - return (VM_PAGER_FAIL); - - if (((*mres)->flags & PG_FICTITIOUS) != 0) { - /* - * The passed-in page is already a fake page: just update - * its physical address in place. - */ - m = *mres; - vm_page_updatefake(m, paddr, object->memattr); - } else { - /* - * Replace the placeholder page allocated by the generic - * fault path with our own fake page. vm_page_getfake() can - * allocate and must not be called with the object lock held; - * vm_page_replace() then swaps the new page into the object - * and frees the placeholder. Without this the busy - * placeholder stays in the object and dev_pager_dealloc() - * dead-locks trying to acquire it. - */ - VM_OBJECT_WUNLOCK(object); - m = vm_page_getfake(paddr, object->memattr); - VM_OBJECT_WLOCK(object); - vm_page_replace(m, object, (*mres)->pindex, *mres); - *mres = m; - } - m->valid = VM_PAGE_BITS_ALL; - return (VM_PAGER_OK); -} - /* ---------------------------------------------------------------- */ /* V4L2 Ioctl Handlers */ /* ---------------------------------------------------------------- */ @@ -3633,7 +3532,9 @@ static int uvideo_reqbufs(struct uvideo_softc *sc, struct v4l2_requestbuffers *rb) { int i, buf_size, buf_size_total; - struct uvideo_mmap_state *ms; + vm_object_t obj; + vm_offset_t kva; + int error; DPRINTFN(1, "reqbufs: count=%d\n", rb->count); @@ -3654,39 +3555,48 @@ uvideo_reqbufs(struct uvideo_softc *sc, struct v4l2_requestbuffers *rb) buf_size_total = sc->sc_mmap_count * buf_size; buf_size_total = round_page(buf_size_total); - sc->sc_mmap_buffer = contigmalloc(buf_size_total, M_USBDEV, - M_WAITOK | M_ZERO, 0, ~0UL, PAGE_SIZE, 0); - if (sc->sc_mmap_buffer == NULL) { - device_printf(sc->sc_dev, "can't allocate mmap buffer!\n"); + /* + * Allocate a physical vm_object of the requested size. Use + * phys_pager_allocate() so that un_pager.phys.ops is properly + * initialised: a bare vm_object_allocate(OBJT_PHYS, ...) + * leaves ops NULL and causes a page fault when the VM system + * calls phys_pager_getpages() during vm_map_wire() or a + * userspace fault on the mmap mapping. + */ + obj = phys_pager_allocate(NULL, &default_phys_pg_ops, NULL, + buf_size_total, VM_PROT_ALL, 0, curthread->td_ucred); + if (obj == NULL) { + device_printf(sc->sc_dev, "can't allocate mmap vm_object!\n"); sc->sc_mmap_count = 0; return (ENOMEM); } - sc->sc_mmap_buffer_size = buf_size_total; - /* - * Create a single shared vm_object backing the whole mmap buffer. - * Its reference count (bumped by each mmap() and held by the softc) - * tracks the lifetime of the mappings; the contig buffer is freed - * by uvideo_pg_dtor() when the last reference goes away, which may - * be after device detach. - */ - ms = malloc(sizeof(*ms), M_USBDEV, M_WAITOK | M_ZERO); - ms->ms_buffer = sc->sc_mmap_buffer; - ms->ms_buffer_size = sc->sc_mmap_buffer_size; - sc->sc_mmap_object = cdev_pager_allocate(ms, OBJT_DEVICE, - &uvideo_pager_ops, sc->sc_mmap_buffer_size, VM_PROT_ALL, - 0, curthread->td_ucred); - if (sc->sc_mmap_object == NULL) { - free(ms, M_USBDEV); - contigfree(sc->sc_mmap_buffer, sc->sc_mmap_buffer_size, - M_USBDEV); - sc->sc_mmap_buffer = NULL; - sc->sc_mmap_buffer_size = 0; + kva = vm_map_min(kernel_map); + error = vm_map_find(kernel_map, obj, 0, &kva, buf_size_total, 0, + VMFS_OPTIMAL_SPACE, VM_PROT_READ | VM_PROT_WRITE, + VM_PROT_READ | VM_PROT_WRITE, 0); + if (error != KERN_SUCCESS) { + vm_object_deallocate(obj); + device_printf(sc->sc_dev, "vm_map_find failed: %d\n", error); sc->sc_mmap_count = 0; - return (ENOMEM); + return (vm_mmap_to_errno(error)); } + error = vm_map_wire(kernel_map, kva, kva + buf_size_total, + VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES); + if (error != KERN_SUCCESS) { + vm_map_remove(kernel_map, kva, kva + buf_size_total); + device_printf(sc->sc_dev, "vm_map_wire failed: %d\n", error); + sc->sc_mmap_count = 0; + return (vm_mmap_to_errno(error)); + } + + sc->sc_mmap_object = obj; + sc->sc_mmap_buffer = (uint8_t *)kva; + sc->sc_mmap_kva = kva; + sc->sc_mmap_buffer_size = buf_size_total; - DPRINTFN(1, "allocated %d bytes mmap buffer\n", buf_size_total); + DPRINTFN(1, "allocated %d bytes mmap buffer at kva %#jx\n", + buf_size_total, (uintmax_t)kva); for (i = 0; i < sc->sc_mmap_count; i++) { sc->sc_mmap[i].buf = sc->sc_mmap_buffer + (i * buf_size);