Module Name: src Committed By: riastradh Date: Sun Dec 19 12:28:13 UTC 2021
Modified Files: src/sys/external/bsd/drm2/amdgpu: amdgpu_pci.c src/sys/external/bsd/drm2/i915drm: i915_pci_autoconf.c src/sys/external/bsd/drm2/nouveau: nouveau_pci.c src/sys/external/bsd/drm2/radeon: radeon_pci.c Log Message: drm: Rework attach/detach and deferred task logic. - Reduce the number of states the softc can be in. - Fix races between attach and other threads. To generate a diff of this commit: cvs rdiff -u -r1.9 -r1.10 src/sys/external/bsd/drm2/amdgpu/amdgpu_pci.c cvs rdiff -u -r1.9 -r1.10 \ src/sys/external/bsd/drm2/i915drm/i915_pci_autoconf.c cvs rdiff -u -r1.33 -r1.34 src/sys/external/bsd/drm2/nouveau/nouveau_pci.c cvs rdiff -u -r1.19 -r1.20 src/sys/external/bsd/drm2/radeon/radeon_pci.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/external/bsd/drm2/amdgpu/amdgpu_pci.c diff -u src/sys/external/bsd/drm2/amdgpu/amdgpu_pci.c:1.9 src/sys/external/bsd/drm2/amdgpu/amdgpu_pci.c:1.10 --- src/sys/external/bsd/drm2/amdgpu/amdgpu_pci.c:1.9 Sun Dec 19 12:21:29 2021 +++ src/sys/external/bsd/drm2/amdgpu/amdgpu_pci.c Sun Dec 19 12:28:12 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: amdgpu_pci.c,v 1.9 2021/12/19 12:21:29 riastradh Exp $ */ +/* $NetBSD: amdgpu_pci.c,v 1.10 2021/12/19 12:28:12 riastradh Exp $ */ /*- * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -30,9 +30,10 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: amdgpu_pci.c,v 1.9 2021/12/19 12:21:29 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: amdgpu_pci.c,v 1.10 2021/12/19 12:28:12 riastradh Exp $"); #include <sys/types.h> +#include <sys/atomic.h> #include <sys/queue.h> #include <sys/systm.h> #include <sys/workqueue.h> @@ -57,14 +58,9 @@ SIMPLEQ_HEAD(amdgpu_task_head, amdgpu_ta struct amdgpu_softc { device_t sc_dev; struct pci_attach_args sc_pa; - enum { - AMDGPU_TASK_ATTACH, - AMDGPU_TASK_WORKQUEUE, - } sc_task_state; - union { - struct workqueue *workqueue; - struct amdgpu_task_head attach; - } sc_task_u; + struct lwp *sc_task_thread; + struct amdgpu_task_head sc_tasks; + struct workqueue *sc_task_wq; struct drm_device *sc_drm_dev; struct pci_dev sc_pci_dev; bool sc_pci_attached; @@ -135,20 +131,30 @@ amdgpu_attach(device_t parent, device_t { struct amdgpu_softc *const sc = device_private(self); const struct pci_attach_args *const pa = aux; + int error; pci_aprint_devinfo(pa, NULL); - if (!pmf_device_register(self, &amdgpu_do_suspend, &amdgpu_do_resume)) - aprint_error_dev(self, "unable to establish power handler\n"); + /* Initialize the Linux PCI device descriptor. */ + linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0); - /* - * Trivial initialization first; the rest will come after we - * have mounted the root file system and can load firmware - * images. - */ - sc->sc_dev = NULL; + sc->sc_dev = self; sc->sc_pa = *pa; + sc->sc_task_thread = NULL; + SIMPLEQ_INIT(&sc->sc_tasks); + error = workqueue_create(&sc->sc_task_wq, "amdgpufb", + &amdgpu_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); + if (error) { + aprint_error_dev(self, "unable to create workqueue: %d\n", + error); + sc->sc_task_wq = NULL; + return; + } + /* + * Defer the remainder of initialization until we have mounted + * the root file system and can load firmware images. + */ config_mountroot(self, &amdgpu_attach_real); } @@ -164,11 +170,11 @@ amdgpu_attach_real(device_t self) ok = amdgpu_pci_lookup(pa, &flags); KASSERT(ok); - sc->sc_task_state = AMDGPU_TASK_ATTACH; - SIMPLEQ_INIT(&sc->sc_task_u.attach); - - /* Initialize the Linux PCI device descriptor. */ - linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0); + /* + * Cause any tasks issued synchronously during attach to be + * processed at the end of this function. + */ + sc->sc_task_thread = curlwp; sc->sc_drm_dev = drm_dev_alloc(amdgpu_drm_driver, self); if (IS_ERR(sc->sc_drm_dev)) { @@ -190,29 +196,29 @@ amdgpu_attach_real(device_t self) error = -drm_dev_register(sc->sc_drm_dev, flags); if (error) { aprint_error_dev(self, "unable to register drm: %d\n", error); - return; + goto out; } sc->sc_dev_registered = true; - while (!SIMPLEQ_EMPTY(&sc->sc_task_u.attach)) { - struct amdgpu_task *const task = - SIMPLEQ_FIRST(&sc->sc_task_u.attach); + if (!pmf_device_register(self, &amdgpu_do_suspend, &amdgpu_do_resume)) + aprint_error_dev(self, "unable to establish power handler\n"); - SIMPLEQ_REMOVE_HEAD(&sc->sc_task_u.attach, rt_u.queue); - (*task->rt_fn)(task); - } + /* + * Process asynchronous tasks queued synchronously during + * attach. This will be for display detection to attach a + * framebuffer, so we have the opportunity for a console device + * to attach before autoconf has completed, in time for init(8) + * to find that console without panicking. + */ + while (!SIMPLEQ_EMPTY(&sc->sc_tasks)) { + struct amdgpu_task *const task = SIMPLEQ_FIRST(&sc->sc_tasks); - sc->sc_task_state = AMDGPU_TASK_WORKQUEUE; - error = workqueue_create(&sc->sc_task_u.workqueue, "amdgpufb", - &amdgpu_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); - if (error) { - aprint_error_dev(self, "unable to create workqueue: %d\n", - error); - sc->sc_task_u.workqueue = NULL; - goto out; + SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, rt_u.queue); + (*task->rt_fn)(task); } -out: sc->sc_dev = self; +out: /* Cause any subesquent tasks to be processed by the workqueue. */ + atomic_store_relaxed(&sc->sc_task_thread, NULL); } static int @@ -221,35 +227,29 @@ amdgpu_detach(device_t self, int flags) struct amdgpu_softc *const sc = device_private(self); int error; - if (sc->sc_dev == NULL) - /* Not done attaching. */ - return EBUSY; - /* XXX Check for in-use before tearing it all down... */ error = config_detach_children(self, flags); if (error) return error; - if (sc->sc_task_state == AMDGPU_TASK_ATTACH) - goto out0; - if (sc->sc_task_u.workqueue != NULL) { - workqueue_destroy(sc->sc_task_u.workqueue); - sc->sc_task_u.workqueue = NULL; + KASSERT(sc->sc_task_thread == NULL); + KASSERT(SIMPLEQ_EMPTY(&sc->sc_tasks)); + + pmf_device_deregister(self); + if (sc->sc_dev_registered) + drm_dev_unregister(sc->sc_drm_dev); + if (sc->sc_pci_attached) + drm_pci_detach(sc->sc_drm_dev); + if (sc->sc_drm_dev) { + drm_dev_put(sc->sc_drm_dev); + sc->sc_drm_dev = NULL; } + if (sc->sc_task_wq) { + workqueue_destroy(sc->sc_task_wq); + sc->sc_task_wq = NULL; + } + linux_pci_dev_destroy(&sc->sc_pci_dev); - if (sc->sc_drm_dev == NULL) - goto out1; - if (!sc->sc_pci_attached) - goto out2; - if (!sc->sc_dev_registered) - goto out3; - - drm_dev_unregister(sc->sc_drm_dev); -out3: drm_pci_detach(sc->sc_drm_dev); -out2: drm_dev_put(sc->sc_drm_dev); - sc->sc_drm_dev = NULL; -out1: linux_pci_dev_destroy(&sc->sc_pci_dev); -out0: pmf_device_deregister(self); return 0; } @@ -260,9 +260,6 @@ amdgpu_do_suspend(device_t self, const p struct drm_device *const dev = sc->sc_drm_dev; int ret; - if (dev == NULL) - return true; - ret = amdgpu_device_suspend(dev, /*fbcon*/true); if (ret) return false; @@ -277,9 +274,6 @@ amdgpu_do_resume(device_t self, const pm struct drm_device *const dev = sc->sc_drm_dev; int ret; - if (dev == NULL) - return true; - ret = amdgpu_device_resume(dev, /*fbcon*/true); if (ret) return false; @@ -301,20 +295,10 @@ amdgpu_task_schedule(device_t self, stru { struct amdgpu_softc *const sc = device_private(self); - switch (sc->sc_task_state) { - case AMDGPU_TASK_ATTACH: - SIMPLEQ_INSERT_TAIL(&sc->sc_task_u.attach, task, rt_u.queue); - return 0; - case AMDGPU_TASK_WORKQUEUE: - if (sc->sc_task_u.workqueue == NULL) { - aprint_error_dev(self, "unable to schedule task\n"); - return EIO; - } - workqueue_enqueue(sc->sc_task_u.workqueue, &task->rt_u.work, - NULL); - return 0; - default: - panic("amdgpu in invalid task state: %d\n", - (int)sc->sc_task_state); - } + if (atomic_load_relaxed(&sc->sc_task_thread) == curlwp) + SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, rt_u.queue); + else + workqueue_enqueue(sc->sc_task_wq, &task->rt_u.work, NULL); + + return 0; } Index: src/sys/external/bsd/drm2/i915drm/i915_pci_autoconf.c diff -u src/sys/external/bsd/drm2/i915drm/i915_pci_autoconf.c:1.9 src/sys/external/bsd/drm2/i915drm/i915_pci_autoconf.c:1.10 --- src/sys/external/bsd/drm2/i915drm/i915_pci_autoconf.c:1.9 Sun Dec 19 11:54:10 2021 +++ src/sys/external/bsd/drm2/i915drm/i915_pci_autoconf.c Sun Dec 19 12:28:12 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: i915_pci_autoconf.c,v 1.9 2021/12/19 11:54:10 riastradh Exp $ */ +/* $NetBSD: i915_pci_autoconf.c,v 1.10 2021/12/19 12:28:12 riastradh Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -30,9 +30,10 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: i915_pci_autoconf.c,v 1.9 2021/12/19 11:54:10 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: i915_pci_autoconf.c,v 1.10 2021/12/19 12:28:12 riastradh Exp $"); #include <sys/types.h> +#include <sys/atomic.h> #include <sys/queue.h> #include <sys/systm.h> #include <sys/queue.h> @@ -50,18 +51,11 @@ SIMPLEQ_HEAD(i915drmkms_task_head, i915d struct i915drmkms_softc { device_t sc_dev; struct pci_attach_args sc_pa; - enum { - I915DRMKMS_TASK_ATTACH, - I915DRMKMS_TASK_WORKQUEUE, - } sc_task_state; - union { - struct workqueue *workqueue; - struct i915drmkms_task_head attach; - } sc_task_u; + struct lwp *sc_task_thread; + struct i915drmkms_task_head sc_tasks; + struct workqueue *sc_task_wq; struct drm_device *sc_drm_dev; struct pci_dev sc_pci_dev; - bool sc_pci_attached; - bool sc_dev_registered; }; static const struct pci_device_id * @@ -144,21 +138,30 @@ i915drmkms_attach(device_t parent, devic { struct i915drmkms_softc *const sc = device_private(self); const struct pci_attach_args *const pa = aux; + int error; pci_aprint_devinfo(pa, NULL); - if (!pmf_device_register(self, &i915drmkms_suspend, - &i915drmkms_resume)) - aprint_error_dev(self, "unable to establish power handler\n"); + /* Initialize the Linux PCI device descriptor. */ + linux_pci_dev_init(&sc->sc_pci_dev, self, parent, pa, 0); - /* - * Trivial initialization first; the rest will come after we - * have mounted the root file system and can load firmware - * images. - */ sc->sc_dev = self; sc->sc_pa = *pa; + sc->sc_task_thread = NULL; + SIMPLEQ_INIT(&sc->sc_tasks); + error = workqueue_create(&sc->sc_task_wq, "intelfb", + &i915drmkms_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); + if (error) { + aprint_error_dev(self, "unable to create workqueue: %d\n", + error); + sc->sc_task_wq = NULL; + return; + } + /* + * Defer the remainder of initialization until we have mounted + * the root file system and can load firmware images. + */ config_mountroot(self, &i915drmkms_attach_real); } @@ -173,38 +176,46 @@ i915drmkms_attach_real(device_t self) KASSERT(info != NULL); - sc->sc_task_state = I915DRMKMS_TASK_ATTACH; - SIMPLEQ_INIT(&sc->sc_task_u.attach); - - /* Initialize the Linux PCI device descriptor. */ - linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0); + /* + * Cause any tasks issued synchronously during attach to be + * processed at the end of this function. + */ + sc->sc_task_thread = curlwp; + /* Attach the drm driver. */ /* XXX errno Linux->NetBSD */ error = -i915_driver_probe(&sc->sc_pci_dev, ent); if (error) { aprint_error_dev(self, "unable to register drm: %d\n", error); return; } - sc->sc_dev_registered = true; sc->sc_drm_dev = pci_get_drvdata(&sc->sc_pci_dev); - while (!SIMPLEQ_EMPTY(&sc->sc_task_u.attach)) { + /* + * Now that the drm driver is attached, we can safely suspend + * and resume. + */ + if (!pmf_device_register(self, &i915drmkms_suspend, + &i915drmkms_resume)) + aprint_error_dev(self, "unable to establish power handler\n"); + + /* + * Process asynchronous tasks queued synchronously during + * attach. This will be for display detection to attach a + * framebuffer, so we have the opportunity for a console device + * to attach before autoconf has completed, in time for init(8) + * to find that console without panicking. + */ + while (!SIMPLEQ_EMPTY(&sc->sc_tasks)) { struct i915drmkms_task *const task = - SIMPLEQ_FIRST(&sc->sc_task_u.attach); + SIMPLEQ_FIRST(&sc->sc_tasks); - SIMPLEQ_REMOVE_HEAD(&sc->sc_task_u.attach, ift_u.queue); + SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, ift_u.queue); (*task->ift_fn)(task); } - sc->sc_task_state = I915DRMKMS_TASK_WORKQUEUE; - error = workqueue_create(&sc->sc_task_u.workqueue, "intelfb", - &i915drmkms_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); - if (error) { - aprint_error_dev(self, "unable to create workqueue: %d\n", - error); - sc->sc_task_u.workqueue = NULL; - return; - } + /* Cause any subesquent tasks to be processed by the workqueue. */ + atomic_store_relaxed(&sc->sc_task_thread, NULL); } static int @@ -218,20 +229,20 @@ i915drmkms_detach(device_t self, int fla if (error) return error; - if (sc->sc_task_state == I915DRMKMS_TASK_ATTACH) - goto out0; - if (sc->sc_task_u.workqueue != NULL) { - workqueue_destroy(sc->sc_task_u.workqueue); - sc->sc_task_u.workqueue = NULL; - } - - if (sc->sc_drm_dev == NULL) - goto out0; + KASSERT(sc->sc_task_thread == NULL); + KASSERT(SIMPLEQ_EMPTY(&sc->sc_tasks)); - i915_driver_remove(sc->sc_drm_dev->dev_private); - sc->sc_drm_dev = NULL; -out0: linux_pci_dev_destroy(&sc->sc_pci_dev); pmf_device_deregister(self); + if (sc->sc_drm_dev) { + i915_driver_remove(sc->sc_drm_dev->dev_private); + sc->sc_drm_dev = NULL; + } + if (sc->sc_task_wq) { + workqueue_destroy(sc->sc_task_wq); + sc->sc_task_wq = NULL; + } + linux_pci_dev_destroy(&sc->sc_pci_dev); + return 0; } @@ -242,9 +253,6 @@ i915drmkms_suspend(device_t self, const struct drm_device *const dev = sc->sc_drm_dev; int ret; - if (dev == NULL) - return true; - ret = i915_drm_suspend(dev); if (ret) return false; @@ -262,9 +270,6 @@ i915drmkms_resume(device_t self, const p struct drm_device *const dev = sc->sc_drm_dev; int ret; - if (dev == NULL) - return true; - ret = i915_drm_resume_early(dev); if (ret) return false; @@ -289,20 +294,10 @@ i915drmkms_task_schedule(device_t self, { struct i915drmkms_softc *const sc = device_private(self); - switch (sc->sc_task_state) { - case I915DRMKMS_TASK_ATTACH: - SIMPLEQ_INSERT_TAIL(&sc->sc_task_u.attach, task, ift_u.queue); - return 0; - case I915DRMKMS_TASK_WORKQUEUE: - if (sc->sc_task_u.workqueue == NULL) { - aprint_error_dev(self, "unable to schedule task\n"); - return EIO; - } - workqueue_enqueue(sc->sc_task_u.workqueue, &task->ift_u.work, - NULL); - return 0; - default: - panic("i915drmkms in invalid task state: %d\n", - (int)sc->sc_task_state); - } + if (atomic_load_relaxed(&sc->sc_task_thread) == curlwp) + SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, ift_u.queue); + else + workqueue_enqueue(sc->sc_task_wq, &task->ift_u.work, NULL); + + return 0; } Index: src/sys/external/bsd/drm2/nouveau/nouveau_pci.c diff -u src/sys/external/bsd/drm2/nouveau/nouveau_pci.c:1.33 src/sys/external/bsd/drm2/nouveau/nouveau_pci.c:1.34 --- src/sys/external/bsd/drm2/nouveau/nouveau_pci.c:1.33 Sun Dec 19 11:53:51 2021 +++ src/sys/external/bsd/drm2/nouveau/nouveau_pci.c Sun Dec 19 12:28:12 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: nouveau_pci.c,v 1.33 2021/12/19 11:53:51 riastradh Exp $ */ +/* $NetBSD: nouveau_pci.c,v 1.34 2021/12/19 12:28:12 riastradh Exp $ */ /*- * Copyright (c) 2015 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: nouveau_pci.c,v 1.33 2021/12/19 11:53:51 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: nouveau_pci.c,v 1.34 2021/12/19 12:28:12 riastradh Exp $"); #ifdef _KERNEL_OPT #if defined(__arm__) || defined(__aarch64__) @@ -39,6 +39,7 @@ __KERNEL_RCSID(0, "$NetBSD: nouveau_pci. #endif #include <sys/types.h> +#include <sys/atomic.h> #include <sys/device.h> #include <sys/queue.h> #include <sys/workqueue.h> @@ -65,18 +66,14 @@ SIMPLEQ_HEAD(nouveau_pci_task_head, nouv struct nouveau_pci_softc { device_t sc_dev; struct pci_attach_args sc_pa; - enum { - NOUVEAU_TASK_ATTACH, - NOUVEAU_TASK_WORKQUEUE, - } sc_task_state; - union { - struct workqueue *workqueue; - struct nouveau_pci_task_head attach; - } sc_task_u; + struct lwp *sc_task_thread; + struct nouveau_pci_task_head sc_tasks; + struct workqueue *sc_task_wq; struct drm_device *sc_drm_dev; struct pci_dev sc_pci_dev; struct nvkm_device *sc_nv_dev; bool sc_pci_attached; + bool sc_nvdev_inited; bool sc_dev_registered; }; @@ -154,19 +151,25 @@ nouveau_pci_attach(device_t parent, devi { struct nouveau_pci_softc *const sc = device_private(self); const struct pci_attach_args *const pa = aux; + int error; pci_aprint_devinfo(pa, NULL); - if (!pmf_device_register(self, &nouveau_pci_suspend, &nouveau_pci_resume)) - aprint_error_dev(self, "unable to establish power handler\n"); + /* Initialize the Linux PCI device descriptor. */ + linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0); - /* - * Trivial initialization first; the rest will come after we - * have mounted the root file system and can load firmware - * images. - */ - sc->sc_dev = NULL; + sc->sc_dev = self; sc->sc_pa = *pa; + sc->sc_task_thread = NULL; + SIMPLEQ_INIT(&sc->sc_tasks); + error = workqueue_create(&sc->sc_task_wq, "nouveau_pci", + &nouveau_pci_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); + if (error) { + aprint_error_dev(self, "unable to create workqueue: %d\n", + error); + sc->sc_task_wq = NULL; + return; + } #ifdef FDT /* @@ -177,6 +180,10 @@ nouveau_pci_attach(device_t parent, devi fdt_remove_bycompat(fb_compatible); #endif + /* + * Defer the remainder of initialization until we have mounted + * the root file system and can load firmware images. + */ config_mountroot(self, &nouveau_pci_attach_real); } @@ -184,15 +191,13 @@ static void nouveau_pci_attach_real(device_t self) { struct nouveau_pci_softc *const sc = device_private(self); - const struct pci_attach_args *const pa = &sc->sc_pa; int error; - sc->sc_dev = self; - sc->sc_task_state = NOUVEAU_TASK_ATTACH; - SIMPLEQ_INIT(&sc->sc_task_u.attach); - - /* Initialize the Linux PCI device descriptor. */ - linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0); + /* + * Cause any tasks issued synchronously during attach to be + * processed at the end of this function. + */ + sc->sc_task_thread = curlwp; /* XXX errno Linux->NetBSD */ error = -nvkm_device_pci_new(&sc->sc_pci_dev, @@ -203,7 +208,7 @@ nouveau_pci_attach_real(device_t self) aprint_error_dev(self, "unable to create nouveau device: %d\n", error); sc->sc_nv_dev = NULL; - return; + goto out; } sc->sc_drm_dev = drm_dev_alloc(nouveau_drm_driver_pci, self); @@ -211,14 +216,14 @@ nouveau_pci_attach_real(device_t self) aprint_error_dev(self, "unable to create drm device: %ld\n", PTR_ERR(sc->sc_drm_dev)); sc->sc_drm_dev = NULL; - return; + goto out; } /* XXX errno Linux->NetBSD */ error = -drm_pci_attach(sc->sc_drm_dev, &sc->sc_pci_dev); if (error) { aprint_error_dev(self, "unable to attach drm: %d\n", error); - return; + goto out; } sc->sc_pci_attached = true; @@ -226,34 +231,32 @@ nouveau_pci_attach_real(device_t self) error = -nouveau_drm_device_init(sc->sc_drm_dev); if (error) { aprint_error_dev(self, "unable to init nouveau: %d\n", error); - return; + goto out; } + sc->sc_nvdev_inited = true; /* XXX errno Linux->NetBSD */ error = -drm_dev_register(sc->sc_drm_dev, 0); if (error) { aprint_error_dev(self, "unable to register drm: %d\n", error); - return; + goto out; } sc->sc_dev_registered = true; - while (!SIMPLEQ_EMPTY(&sc->sc_task_u.attach)) { + if (!pmf_device_register(self, &nouveau_pci_suspend, + &nouveau_pci_resume)) + aprint_error_dev(self, "unable to establish power handler\n"); + + while (!SIMPLEQ_EMPTY(&sc->sc_tasks)) { struct nouveau_pci_task *const task = - SIMPLEQ_FIRST(&sc->sc_task_u.attach); + SIMPLEQ_FIRST(&sc->sc_tasks); - SIMPLEQ_REMOVE_HEAD(&sc->sc_task_u.attach, nt_u.queue); + SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, nt_u.queue); (*task->nt_fn)(task); } - sc->sc_task_state = NOUVEAU_TASK_WORKQUEUE; - error = workqueue_create(&sc->sc_task_u.workqueue, "nouveau_pci", - &nouveau_pci_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); - if (error) { - aprint_error_dev(self, "unable to create workqueue: %d\n", - error); - sc->sc_task_u.workqueue = NULL; - return; - } +out: /* Cause any subesquent tasks to be processed by the workqueue. */ + atomic_store_relaxed(&sc->sc_task_thread, NULL); } static int @@ -262,38 +265,30 @@ nouveau_pci_detach(device_t self, int fl struct nouveau_pci_softc *const sc = device_private(self); int error; - if (sc->sc_dev == NULL) - /* Not done attaching. */ - return EBUSY; - /* XXX Check for in-use before tearing it all down... */ error = config_detach_children(self, flags); if (error) return error; - if (sc->sc_task_state == NOUVEAU_TASK_ATTACH) - goto out0; - if (sc->sc_task_u.workqueue != NULL) { - workqueue_destroy(sc->sc_task_u.workqueue); - sc->sc_task_u.workqueue = NULL; + pmf_device_deregister(self); + if (sc->sc_dev_registered) + drm_dev_unregister(sc->sc_drm_dev); + if (sc->sc_nvdev_inited) + nouveau_drm_device_fini(sc->sc_drm_dev); + if (sc->sc_pci_attached) + drm_pci_detach(sc->sc_drm_dev); + if (sc->sc_drm_dev) { + drm_dev_put(sc->sc_drm_dev); + sc->sc_drm_dev = NULL; } + if (sc->sc_nv_dev) + nvkm_device_del(&sc->sc_nv_dev); + if (sc->sc_task_wq) { + workqueue_destroy(sc->sc_task_wq); + sc->sc_task_wq = NULL; + } + linux_pci_dev_destroy(&sc->sc_pci_dev); - if (sc->sc_nv_dev == NULL) - goto out0; - if (sc->sc_drm_dev == NULL) - goto out1; - if (!sc->sc_pci_attached) - goto out2; - if (!sc->sc_dev_registered) - goto out3; - - drm_dev_unregister(sc->sc_drm_dev); -out3: drm_pci_detach(sc->sc_drm_dev); -out2: drm_dev_put(sc->sc_drm_dev); - sc->sc_drm_dev = NULL; -out1: nvkm_device_del(&sc->sc_nv_dev); -out0: linux_pci_dev_destroy(&sc->sc_pci_dev); - pmf_device_deregister(self); return 0; } @@ -330,22 +325,12 @@ nouveau_pci_task_schedule(device_t self, { struct nouveau_pci_softc *const sc = device_private(self); - switch (sc->sc_task_state) { - case NOUVEAU_TASK_ATTACH: - SIMPLEQ_INSERT_TAIL(&sc->sc_task_u.attach, task, nt_u.queue); - return 0; - case NOUVEAU_TASK_WORKQUEUE: - if (sc->sc_task_u.workqueue == NULL) { - aprint_error_dev(self, "unable to schedule task\n"); - return EIO; - } - workqueue_enqueue(sc->sc_task_u.workqueue, &task->nt_u.work, - NULL); - return 0; - default: - panic("nouveau in invalid task state: %d\n", - (int)sc->sc_task_state); - } + if (atomic_load_relaxed(&sc->sc_task_thread) == curlwp) + SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, nt_u.queue); + else + workqueue_enqueue(sc->sc_task_wq, &task->nt_u.work, NULL); + + return 0; } extern struct drm_driver *const nouveau_drm_driver_stub; /* XXX */ Index: src/sys/external/bsd/drm2/radeon/radeon_pci.c diff -u src/sys/external/bsd/drm2/radeon/radeon_pci.c:1.19 src/sys/external/bsd/drm2/radeon/radeon_pci.c:1.20 --- src/sys/external/bsd/drm2/radeon/radeon_pci.c:1.19 Sun Dec 19 11:53:51 2021 +++ src/sys/external/bsd/drm2/radeon/radeon_pci.c Sun Dec 19 12:28:12 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: radeon_pci.c,v 1.19 2021/12/19 11:53:51 riastradh Exp $ */ +/* $NetBSD: radeon_pci.c,v 1.20 2021/12/19 12:28:12 riastradh Exp $ */ /*- * Copyright (c) 2014 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: radeon_pci.c,v 1.19 2021/12/19 11:53:51 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: radeon_pci.c,v 1.20 2021/12/19 12:28:12 riastradh Exp $"); #ifdef _KERNEL_OPT #include "vga.h" @@ -40,6 +40,7 @@ __KERNEL_RCSID(0, "$NetBSD: radeon_pci.c #endif #include <sys/types.h> +#include <sys/atomic.h> #include <sys/queue.h> #include <sys/systm.h> #include <sys/workqueue.h> @@ -81,14 +82,9 @@ SIMPLEQ_HEAD(radeon_task_head, radeon_ta struct radeon_softc { device_t sc_dev; struct pci_attach_args sc_pa; - enum { - RADEON_TASK_ATTACH, - RADEON_TASK_WORKQUEUE, - } sc_task_state; - union { - struct workqueue *workqueue; - struct radeon_task_head attach; - } sc_task_u; + struct lwp *sc_task_thread; + struct radeon_task_head sc_tasks; + struct workqueue *sc_task_wq; struct drm_device *sc_drm_dev; struct pci_dev sc_pci_dev; bool sc_pci_attached; @@ -182,19 +178,25 @@ radeon_attach(device_t parent, device_t { struct radeon_softc *const sc = device_private(self); const struct pci_attach_args *const pa = aux; + int error; pci_aprint_devinfo(pa, NULL); - if (!pmf_device_register(self, &radeon_do_suspend, &radeon_do_resume)) - aprint_error_dev(self, "unable to establish power handler\n"); + /* Initialize the Linux PCI device descriptor. */ + linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0); - /* - * Trivial initialization first; the rest will come after we - * have mounted the root file system and can load firmware - * images. - */ - sc->sc_dev = NULL; + sc->sc_dev = self; sc->sc_pa = *pa; + sc->sc_task_thread = NULL; + SIMPLEQ_INIT(&sc->sc_tasks); + error = workqueue_create(&sc->sc_task_wq, "radeonfb", + &radeon_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); + if (error) { + aprint_error_dev(self, "unable to create workqueue: %d\n", + error); + sc->sc_task_wq = NULL; + return; + } #ifdef RADEON_PCI_UGLY_MAP_HACK /* @@ -221,6 +223,10 @@ radeon_attach(device_t parent, device_t fdt_remove_bycompat(fb_compatible); #endif + /* + * Defer the remainder of initialization until we have mounted + * the root file system and can load firmware images. + */ config_mountroot(self, &radeon_attach_real); } @@ -245,11 +251,11 @@ radeon_attach_real(device_t self) bus_space_unmap(pa->pa_memt, sc->sc_temp_memh, 0x10000); #endif - sc->sc_task_state = RADEON_TASK_ATTACH; - SIMPLEQ_INIT(&sc->sc_task_u.attach); - - /* Initialize the Linux PCI device descriptor. */ - linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0); + /* + * Cause any tasks issued synchronously during attach to be + * processed at the end of this function. + */ + sc->sc_task_thread = curlwp; sc->sc_drm_dev = drm_dev_alloc(radeon_drm_driver, self); if (IS_ERR(sc->sc_drm_dev)) { @@ -271,29 +277,29 @@ radeon_attach_real(device_t self) error = -drm_dev_register(sc->sc_drm_dev, flags); if (error) { aprint_error_dev(self, "unable to register drm: %d\n", error); - return; + goto out; } sc->sc_dev_registered = true; - while (!SIMPLEQ_EMPTY(&sc->sc_task_u.attach)) { - struct radeon_task *const task = - SIMPLEQ_FIRST(&sc->sc_task_u.attach); + if (!pmf_device_register(self, &radeon_do_suspend, &radeon_do_resume)) + aprint_error_dev(self, "unable to establish power handler\n"); - SIMPLEQ_REMOVE_HEAD(&sc->sc_task_u.attach, rt_u.queue); - (*task->rt_fn)(task); - } + /* + * Process asynchronous tasks queued synchronously during + * attach. This will be for display detection to attach a + * framebuffer, so we have the opportunity for a console device + * to attach before autoconf has completed, in time for init(8) + * to find that console without panicking. + */ + while (!SIMPLEQ_EMPTY(&sc->sc_tasks)) { + struct radeon_task *const task = SIMPLEQ_FIRST(&sc->sc_tasks); - sc->sc_task_state = RADEON_TASK_WORKQUEUE; - error = workqueue_create(&sc->sc_task_u.workqueue, "radeonfb", - &radeon_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); - if (error) { - aprint_error_dev(self, "unable to create workqueue: %d\n", - error); - sc->sc_task_u.workqueue = NULL; - goto out; + SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, rt_u.queue); + (*task->rt_fn)(task); } -out: sc->sc_dev = self; +out: /* Cause any subesquent tasks to be processed by the workqueue. */ + atomic_store_relaxed(&sc->sc_task_thread, NULL); } static int @@ -302,35 +308,29 @@ radeon_detach(device_t self, int flags) struct radeon_softc *const sc = device_private(self); int error; - if (sc->sc_dev == NULL) - /* Not done attaching. */ - return EBUSY; - /* XXX Check for in-use before tearing it all down... */ error = config_detach_children(self, flags); if (error) return error; - if (sc->sc_task_state == RADEON_TASK_ATTACH) - goto out0; - if (sc->sc_task_u.workqueue != NULL) { - workqueue_destroy(sc->sc_task_u.workqueue); - sc->sc_task_u.workqueue = NULL; - } + KASSERT(sc->sc_task_thread == NULL); + KASSERT(SIMPLEQ_EMPTY(&sc->sc_tasks)); - if (sc->sc_drm_dev == NULL) - goto out0; - if (!sc->sc_pci_attached) - goto out1; - if (!sc->sc_dev_registered) - goto out2; - - drm_dev_unregister(sc->sc_drm_dev); -out2: drm_pci_detach(sc->sc_drm_dev); -out1: drm_dev_put(sc->sc_drm_dev); - sc->sc_drm_dev = NULL; -out0: linux_pci_dev_destroy(&sc->sc_pci_dev); pmf_device_deregister(self); + if (sc->sc_dev_registered) + drm_dev_unregister(sc->sc_drm_dev); + if (sc->sc_pci_attached) + drm_pci_detach(sc->sc_drm_dev); + if (sc->sc_drm_dev) { + drm_dev_put(sc->sc_drm_dev); + sc->sc_drm_dev = NULL; + } + if (sc->sc_task_wq) { + workqueue_destroy(sc->sc_task_wq); + sc->sc_task_wq = NULL; + } + linux_pci_dev_destroy(&sc->sc_pci_dev); + return 0; } @@ -342,9 +342,6 @@ radeon_do_suspend(device_t self, const p int ret; bool is_console = true; /* XXX */ - if (dev == NULL) - return true; - ret = radeon_suspend_kms(dev, true, is_console, false); if (ret) return false; @@ -360,9 +357,6 @@ radeon_do_resume(device_t self, const pm int ret; bool is_console = true; /* XXX */ - if (dev == NULL) - return true; - ret = radeon_resume_kms(dev, true, is_console); if (ret) return false; @@ -384,20 +378,10 @@ radeon_task_schedule(device_t self, stru { struct radeon_softc *const sc = device_private(self); - switch (sc->sc_task_state) { - case RADEON_TASK_ATTACH: - SIMPLEQ_INSERT_TAIL(&sc->sc_task_u.attach, task, rt_u.queue); - return 0; - case RADEON_TASK_WORKQUEUE: - if (sc->sc_task_u.workqueue == NULL) { - aprint_error_dev(self, "unable to schedule task\n"); - return EIO; - } - workqueue_enqueue(sc->sc_task_u.workqueue, &task->rt_u.work, - NULL); - return 0; - default: - panic("radeon in invalid task state: %d\n", - (int)sc->sc_task_state); - } + if (atomic_load_relaxed(&sc->sc_task_thread) == curlwp) + SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, rt_u.queue); + else + workqueue_enqueue(sc->sc_task_wq, &task->rt_u.work, NULL); + + return 0; }