diff --git a/kernel-open/nvidia-drm/nvidia-drm-drv.c b/kernel-open/nvidia-drm/nvidia-drm-drv.c index 30e363cf16..0ec4fff4a8 100644 --- a/kernel-open/nvidia-drm/nvidia-drm-drv.c +++ b/kernel-open/nvidia-drm/nvidia-drm-drv.c @@ -95,6 +95,7 @@ #include #include #include +#include static int nv_drm_revoke_modeset_permission(struct drm_device *dev, struct drm_file *filep, @@ -2015,6 +2016,22 @@ void nv_drm_update_drm_driver_features(void) +/* + * nv_drm_device is referenced by userspace-visible DRM/GEM/fence objects + * which may outlive drm_dev_unplug(). Keep it alive until the DRM device's + * final reference is released. + */ +static void nv_drm_dev_private_release(struct drm_device *dev, void *data) +{ + struct nv_drm_device *nv_dev = data; + + if (dev->dev_private == nv_dev) { + dev->dev_private = NULL; + } + + nv_drm_free(nv_dev); +} + /* * Helper function for allocate/register DRM device for given NVIDIA GPU ID. */ @@ -2056,6 +2073,22 @@ void nv_drm_register_drm_device(const struct NvKmsKapiGpuInfo *gpu_info) dev->dev_private = nv_dev; nv_dev->dev = dev; + /* + * drm_dev_unplug() does not imply that all userspace DRM files are closed. + * GEM/fence teardown from those files still dereferences nv_dev, so make + * nv_dev a DRM-managed resource and free it only on the final drm_dev_put(). + */ + if (drmm_add_action(dev, nv_drm_dev_private_release, nv_dev) != 0) { + NV_DRM_DEV_LOG_ERR( + nv_dev, + "Failed to register DRM-managed nv_drm_device cleanup"); + dev->dev_private = NULL; + nv_dev->dev = NULL; + drm_dev_put(dev); + nv_drm_free(nv_dev); + return; + } + bus_is_pci = #if defined(NV_LINUX) device->bus == &pci_bus_type; @@ -2155,10 +2188,16 @@ void nv_drm_register_drm_device(const struct NvKmsKapiGpuInfo *gpu_info) failed_drm_load: + /* + * The DRM-managed release action frees nv_dev when this is the final + * drm_dev_put(). Do not fall through and free it a second time. + */ drm_dev_put(dev); + return; failed_drm_alloc: + /* drm_dev_alloc() failed, so no managed release action exists. */ nv_drm_free(nv_dev); } @@ -2230,8 +2269,13 @@ static void nv_drm_dev_destroy(struct nv_drm_device *nv_dev) struct drm_device *dev = nv_dev->dev; nv_drm_dev_unload(dev); + + /* + * Drop the driver's DRM reference. Open userspace files may still hold + * references; nv_drm_device is freed by nv_drm_dev_private_release() + * only after the final one disappears. + */ drm_dev_put(dev); - nv_drm_free(nv_dev); } /*