Commit f2af99d8 authored by Michał Winiarski's avatar Michał Winiarski Committed by Wen Zhiwei
Browse files

drm: Use XArray instead of IDR for minors

stable inclusion
from stable-v6.6.53
commit d2e3d344e20e605378556610811549249298d0aa
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IAZ0GM

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=d2e3d344e20e605378556610811549249298d0aa



--------------------------------

[ Upstream commit 5fbca8b48b3050ae7fb611a8b09af60012ed6de1 ]

IDR is deprecated, and since XArray manages its own state with internal
locking, it simplifies the locking on DRM side.
Additionally, don't use the IRQ-safe variant, since operating on drm
minor is not done in IRQ context.

Suggested-by: default avatarMatthew Wilcox <willy@infradead.org>
Signed-off-by: default avatarMichał Winiarski <michal.winiarski@intel.com>
Acked-by: default avatarJames Zhu <James.Zhu@amd.com>
Acked-by: default avatarChristian König <christian.koenig@amd.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240823163048.2676257-2-michal.winiarski@intel.com


Signed-off-by: default avatarChristian König <christian.koenig@amd.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarWen Zhiwei <wenzhiwei@kylinos.cn>
parent 2fc9aa60
Loading
Loading
Loading
Loading
+25 −38
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include <linux/pseudo_fs.h>
#include <linux/slab.h>
#include <linux/srcu.h>
#include <linux/xarray.h>

#include <drm/drm_accel.h>
#include <drm/drm_cache.h>
@@ -54,8 +55,7 @@ MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl");
MODULE_DESCRIPTION("DRM shared core routines");
MODULE_LICENSE("GPL and additional rights");

static DEFINE_SPINLOCK(drm_minor_lock);
static struct idr drm_minors_idr;
static DEFINE_XARRAY_ALLOC(drm_minors_xa);

/*
 * If the drm core fails to init for whatever reason,
@@ -101,26 +101,23 @@ static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
static void drm_minor_alloc_release(struct drm_device *dev, void *data)
{
	struct drm_minor *minor = data;
	unsigned long flags;

	WARN_ON(dev != minor->dev);

	put_device(minor->kdev);

	if (minor->type == DRM_MINOR_ACCEL) {
	if (minor->type == DRM_MINOR_ACCEL)
		accel_minor_remove(minor->index);
	} else {
		spin_lock_irqsave(&drm_minor_lock, flags);
		idr_remove(&drm_minors_idr, minor->index);
		spin_unlock_irqrestore(&drm_minor_lock, flags);
	}
	else
		xa_erase(&drm_minors_xa, minor->index);
}

#define DRM_MINOR_LIMIT(t) ({ typeof(t) _t = (t); XA_LIMIT(64 * _t, 64 * _t + 63); })

static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
{
	struct drm_minor *minor;
	unsigned long flags;
	int r;
	int index, r;

	minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
	if (!minor)
@@ -129,24 +126,17 @@ static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
	minor->type = type;
	minor->dev = dev;

	idr_preload(GFP_KERNEL);
	if (type == DRM_MINOR_ACCEL) {
		r = accel_minor_alloc();
		index = r;
	} else {
		spin_lock_irqsave(&drm_minor_lock, flags);
		r = idr_alloc(&drm_minors_idr,
			NULL,
			64 * type,
			64 * (type + 1),
			GFP_NOWAIT);
		spin_unlock_irqrestore(&drm_minor_lock, flags);
		r = xa_alloc(&drm_minors_xa, &index, NULL, DRM_MINOR_LIMIT(type), GFP_KERNEL);
	}
	idr_preload_end();

	if (r < 0)
		return r;

	minor->index = r;
	minor->index = index;

	r = drmm_add_action_or_reset(dev, drm_minor_alloc_release, minor);
	if (r)
@@ -163,7 +153,7 @@ static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type)
{
	struct drm_minor *minor;
	unsigned long flags;
	void *entry;
	int ret;

	DRM_DEBUG("\n");
@@ -190,9 +180,12 @@ static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type)
	if (minor->type == DRM_MINOR_ACCEL) {
		accel_minor_replace(minor, minor->index);
	} else {
		spin_lock_irqsave(&drm_minor_lock, flags);
		idr_replace(&drm_minors_idr, minor, minor->index);
		spin_unlock_irqrestore(&drm_minor_lock, flags);
		entry = xa_store(&drm_minors_xa, minor->index, minor, GFP_KERNEL);
		if (xa_is_err(entry)) {
			ret = xa_err(entry);
			goto err_debugfs;
		}
		WARN_ON(entry);
	}

	DRM_DEBUG("new minor registered %d\n", minor->index);
@@ -206,20 +199,16 @@ static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type)
static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type type)
{
	struct drm_minor *minor;
	unsigned long flags;

	minor = *drm_minor_get_slot(dev, type);
	if (!minor || !device_is_registered(minor->kdev))
		return;

	/* replace @minor with NULL so lookups will fail from now on */
	if (minor->type == DRM_MINOR_ACCEL) {
	if (minor->type == DRM_MINOR_ACCEL)
		accel_minor_replace(NULL, minor->index);
	} else {
		spin_lock_irqsave(&drm_minor_lock, flags);
		idr_replace(&drm_minors_idr, NULL, minor->index);
		spin_unlock_irqrestore(&drm_minor_lock, flags);
	}
	else
		xa_store(&drm_minors_xa, minor->index, NULL, GFP_KERNEL);

	device_del(minor->kdev);
	dev_set_drvdata(minor->kdev, NULL); /* safety belt */
@@ -238,13 +227,12 @@ static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type typ
struct drm_minor *drm_minor_acquire(unsigned int minor_id)
{
	struct drm_minor *minor;
	unsigned long flags;

	spin_lock_irqsave(&drm_minor_lock, flags);
	minor = idr_find(&drm_minors_idr, minor_id);
	xa_lock(&drm_minors_xa);
	minor = xa_load(&drm_minors_xa, minor_id);
	if (minor)
		drm_dev_get(minor->dev);
	spin_unlock_irqrestore(&drm_minor_lock, flags);
	xa_unlock(&drm_minors_xa);

	if (!minor) {
		return ERR_PTR(-ENODEV);
@@ -1073,7 +1061,7 @@ static void drm_core_exit(void)
	unregister_chrdev(DRM_MAJOR, "drm");
	debugfs_remove(drm_debugfs_root);
	drm_sysfs_destroy();
	idr_destroy(&drm_minors_idr);
	WARN_ON(!xa_empty(&drm_minors_xa));
	drm_connector_ida_destroy();
}

@@ -1082,7 +1070,6 @@ static int __init drm_core_init(void)
	int ret;

	drm_connector_ida_init();
	idr_init(&drm_minors_idr);
	drm_memcpy_init_early();

	ret = drm_sysfs_init();