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;
}