Skip to content

Commit 2c204f3

Browse files
committed
accel: add dedicated minor for accelerator devices
The accelerator devices are exposed to user-space using a dedicated major. In addition, they are represented in /dev with new, dedicated device char names: /dev/accel/accel*. This is done to make sure any user-space software that tries to open a graphic card won't open the accelerator device by mistake. The above implies that the minor numbering should be separated from the rest of the DRM devices. However, to avoid code duplication, we want the drm_minor structure to be able to represent the accelerator device. To achieve this, we add a new drm_minor* to drm_device that represents the accelerator device. This pointer is initialized for drivers that declare they handle compute accelerator, using a new driver feature flag called DRIVER_COMPUTE_ACCEL. It is important to note that this driver feature is mutually exclusive with DRIVER_RENDER. Devices that want to expose both graphics and compute device char files should be handled by two drivers that are connected using the auxiliary bus framework. In addition, we define a different IDR to handle the accelerators minors. This is done to make the minor's index be identical to the device index in /dev/. Any access to the IDR is done solely by functions in accel_drv.c, as the IDR is define as static. The DRM core functions call those functions in case they detect the minor's type is DRM_MINOR_ACCEL. We define a separate accel_open function (from drm_open) that the accel drivers should set as their open callback function. Both these functions eventually call the same drm_open_helper(), which had to be changed to be non-static so it can be called from accel_drv.c. accel_open() only partially duplicates drm_open as I removed some code from it that handles legacy devices. To help new drivers, I defined DEFINE_DRM_ACCEL_FOPS macro to easily set the required function operations pointers structure. Signed-off-by: Oded Gabbay <[email protected]> Reviewed-by: Greg Kroah-Hartman <[email protected]> Reviewed-by: Jeffrey Hugo <[email protected]> Reviewed-by: Dave Airlie <[email protected]> Acked-by: Thomas Zimmermann <[email protected]> Acked-by: Jacek Lawrynowicz <[email protected]> Tested-by: Jacek Lawrynowicz <[email protected]> Reviewed-by: Melissa Wen <[email protected]>
1 parent 8bf4889 commit 2c204f3

File tree

6 files changed

+338
-3
lines changed

6 files changed

+338
-3
lines changed

drivers/accel/drm_accel.c

Lines changed: 241 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,25 @@
88

99
#include <linux/debugfs.h>
1010
#include <linux/device.h>
11+
#include <linux/idr.h>
1112

1213
#include <drm/drm_accel.h>
14+
#include <drm/drm_debugfs.h>
15+
#include <drm/drm_drv.h>
16+
#include <drm/drm_file.h>
1317
#include <drm/drm_ioctl.h>
1418
#include <drm/drm_print.h>
1519

20+
static DEFINE_SPINLOCK(accel_minor_lock);
21+
static struct idr accel_minors_idr;
22+
1623
static struct dentry *accel_debugfs_root;
1724
static struct class *accel_class;
1825

26+
static struct device_type accel_sysfs_device_minor = {
27+
.name = "accel_minor"
28+
};
29+
1930
static char *accel_devnode(struct device *dev, umode_t *mode)
2031
{
2132
return kasprintf(GFP_KERNEL, "accel/%s", dev_name(dev));
@@ -40,9 +51,235 @@ static void accel_sysfs_destroy(void)
4051
accel_class = NULL;
4152
}
4253

54+
static int accel_name_info(struct seq_file *m, void *data)
55+
{
56+
struct drm_info_node *node = (struct drm_info_node *) m->private;
57+
struct drm_minor *minor = node->minor;
58+
struct drm_device *dev = minor->dev;
59+
struct drm_master *master;
60+
61+
mutex_lock(&dev->master_mutex);
62+
master = dev->master;
63+
seq_printf(m, "%s", dev->driver->name);
64+
if (dev->dev)
65+
seq_printf(m, " dev=%s", dev_name(dev->dev));
66+
if (master && master->unique)
67+
seq_printf(m, " master=%s", master->unique);
68+
if (dev->unique)
69+
seq_printf(m, " unique=%s", dev->unique);
70+
seq_puts(m, "\n");
71+
mutex_unlock(&dev->master_mutex);
72+
73+
return 0;
74+
}
75+
76+
static const struct drm_info_list accel_debugfs_list[] = {
77+
{"name", accel_name_info, 0}
78+
};
79+
#define ACCEL_DEBUGFS_ENTRIES ARRAY_SIZE(accel_debugfs_list)
80+
81+
/**
82+
* accel_debugfs_init() - Initialize debugfs for accel minor
83+
* @minor: Pointer to the drm_minor instance.
84+
* @minor_id: The minor's id
85+
*
86+
* This function initializes the drm minor's debugfs members and creates
87+
* a root directory for the minor in debugfs. It also creates common files
88+
* for accelerators and calls the driver's debugfs init callback.
89+
*/
90+
void accel_debugfs_init(struct drm_minor *minor, int minor_id)
91+
{
92+
struct drm_device *dev = minor->dev;
93+
char name[64];
94+
95+
INIT_LIST_HEAD(&minor->debugfs_list);
96+
mutex_init(&minor->debugfs_lock);
97+
sprintf(name, "%d", minor_id);
98+
minor->debugfs_root = debugfs_create_dir(name, accel_debugfs_root);
99+
100+
drm_debugfs_create_files(accel_debugfs_list, ACCEL_DEBUGFS_ENTRIES,
101+
minor->debugfs_root, minor);
102+
103+
if (dev->driver->debugfs_init)
104+
dev->driver->debugfs_init(minor);
105+
}
106+
107+
/**
108+
* accel_set_device_instance_params() - Set some device parameters for accel device
109+
* @kdev: Pointer to the device instance.
110+
* @index: The minor's index
111+
*
112+
* This function creates the dev_t of the device using the accel major and
113+
* the device's minor number. In addition, it sets the class and type of the
114+
* device instance to the accel sysfs class and device type, respectively.
115+
*/
116+
void accel_set_device_instance_params(struct device *kdev, int index)
117+
{
118+
kdev->devt = MKDEV(ACCEL_MAJOR, index);
119+
kdev->class = accel_class;
120+
kdev->type = &accel_sysfs_device_minor;
121+
}
122+
123+
/**
124+
* accel_minor_alloc() - Allocates a new accel minor
125+
*
126+
* This function access the accel minors idr and allocates from it
127+
* a new id to represent a new accel minor
128+
*
129+
* Return: A new id on success or error code in case idr_alloc failed
130+
*/
131+
int accel_minor_alloc(void)
132+
{
133+
unsigned long flags;
134+
int r;
135+
136+
spin_lock_irqsave(&accel_minor_lock, flags);
137+
r = idr_alloc(&accel_minors_idr, NULL, 0, ACCEL_MAX_MINORS, GFP_NOWAIT);
138+
spin_unlock_irqrestore(&accel_minor_lock, flags);
139+
140+
return r;
141+
}
142+
143+
/**
144+
* accel_minor_remove() - Remove an accel minor
145+
* @index: The minor id to remove.
146+
*
147+
* This function access the accel minors idr and removes from
148+
* it the member with the id that is passed to this function.
149+
*/
150+
void accel_minor_remove(int index)
151+
{
152+
unsigned long flags;
153+
154+
spin_lock_irqsave(&accel_minor_lock, flags);
155+
idr_remove(&accel_minors_idr, index);
156+
spin_unlock_irqrestore(&accel_minor_lock, flags);
157+
}
158+
159+
/**
160+
* accel_minor_replace() - Replace minor pointer in accel minors idr.
161+
* @minor: Pointer to the new minor.
162+
* @index: The minor id to replace.
163+
*
164+
* This function access the accel minors idr structure and replaces the pointer
165+
* that is associated with an existing id. Because the minor pointer can be
166+
* NULL, we need to explicitly pass the index.
167+
*
168+
* Return: 0 for success, negative value for error
169+
*/
170+
void accel_minor_replace(struct drm_minor *minor, int index)
171+
{
172+
unsigned long flags;
173+
174+
spin_lock_irqsave(&accel_minor_lock, flags);
175+
idr_replace(&accel_minors_idr, minor, index);
176+
spin_unlock_irqrestore(&accel_minor_lock, flags);
177+
}
178+
179+
/*
180+
* Looks up the given minor-ID and returns the respective DRM-minor object. The
181+
* refence-count of the underlying device is increased so you must release this
182+
* object with accel_minor_release().
183+
*
184+
* The object can be only a drm_minor that represents an accel device.
185+
*
186+
* As long as you hold this minor, it is guaranteed that the object and the
187+
* minor->dev pointer will stay valid! However, the device may get unplugged and
188+
* unregistered while you hold the minor.
189+
*/
190+
static struct drm_minor *accel_minor_acquire(unsigned int minor_id)
191+
{
192+
struct drm_minor *minor;
193+
unsigned long flags;
194+
195+
spin_lock_irqsave(&accel_minor_lock, flags);
196+
minor = idr_find(&accel_minors_idr, minor_id);
197+
if (minor)
198+
drm_dev_get(minor->dev);
199+
spin_unlock_irqrestore(&accel_minor_lock, flags);
200+
201+
if (!minor) {
202+
return ERR_PTR(-ENODEV);
203+
} else if (drm_dev_is_unplugged(minor->dev)) {
204+
drm_dev_put(minor->dev);
205+
return ERR_PTR(-ENODEV);
206+
}
207+
208+
return minor;
209+
}
210+
211+
static void accel_minor_release(struct drm_minor *minor)
212+
{
213+
drm_dev_put(minor->dev);
214+
}
215+
216+
/**
217+
* accel_open - open method for ACCEL file
218+
* @inode: device inode
219+
* @filp: file pointer.
220+
*
221+
* This function must be used by drivers as their &file_operations.open method.
222+
* It looks up the correct ACCEL device and instantiates all the per-file
223+
* resources for it. It also calls the &drm_driver.open driver callback.
224+
*
225+
* Return: 0 on success or negative errno value on failure.
226+
*/
227+
int accel_open(struct inode *inode, struct file *filp)
228+
{
229+
struct drm_device *dev;
230+
struct drm_minor *minor;
231+
int retcode;
232+
233+
minor = accel_minor_acquire(iminor(inode));
234+
if (IS_ERR(minor))
235+
return PTR_ERR(minor);
236+
237+
dev = minor->dev;
238+
239+
atomic_fetch_inc(&dev->open_count);
240+
241+
/* share address_space across all char-devs of a single device */
242+
filp->f_mapping = dev->anon_inode->i_mapping;
243+
244+
retcode = drm_open_helper(filp, minor);
245+
if (retcode)
246+
goto err_undo;
247+
248+
return 0;
249+
250+
err_undo:
251+
atomic_dec(&dev->open_count);
252+
accel_minor_release(minor);
253+
return retcode;
254+
}
255+
EXPORT_SYMBOL_GPL(accel_open);
256+
43257
static int accel_stub_open(struct inode *inode, struct file *filp)
44258
{
45-
return -EOPNOTSUPP;
259+
const struct file_operations *new_fops;
260+
struct drm_minor *minor;
261+
int err;
262+
263+
minor = accel_minor_acquire(iminor(inode));
264+
if (IS_ERR(minor))
265+
return PTR_ERR(minor);
266+
267+
new_fops = fops_get(minor->dev->driver->fops);
268+
if (!new_fops) {
269+
err = -ENODEV;
270+
goto out;
271+
}
272+
273+
replace_fops(filp, new_fops);
274+
if (filp->f_op->open)
275+
err = filp->f_op->open(inode, filp);
276+
else
277+
err = 0;
278+
279+
out:
280+
accel_minor_release(minor);
281+
282+
return err;
46283
}
47284

48285
static const struct file_operations accel_stub_fops = {
@@ -56,12 +293,15 @@ void accel_core_exit(void)
56293
unregister_chrdev(ACCEL_MAJOR, "accel");
57294
debugfs_remove(accel_debugfs_root);
58295
accel_sysfs_destroy();
296+
idr_destroy(&accel_minors_idr);
59297
}
60298

61299
int __init accel_core_init(void)
62300
{
63301
int ret;
64302

303+
idr_init(&accel_minors_idr);
304+
65305
ret = accel_sysfs_init();
66306
if (ret < 0) {
67307
DRM_ERROR("Cannot create ACCEL class: %d\n", ret);

drivers/gpu/drm/drm_file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ static int drm_cpu_valid(void)
326326
* Creates and initializes a drm_file structure for the file private data in \p
327327
* filp and add it into the double linked list in \p dev.
328328
*/
329-
static int drm_open_helper(struct file *filp, struct drm_minor *minor)
329+
int drm_open_helper(struct file *filp, struct drm_minor *minor)
330330
{
331331
struct drm_device *dev = minor->dev;
332332
struct drm_file *priv;

include/drm/drm_accel.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,56 @@
88
#ifndef DRM_ACCEL_H_
99
#define DRM_ACCEL_H_
1010

11+
#include <drm/drm_file.h>
12+
1113
#define ACCEL_MAJOR 261
14+
#define ACCEL_MAX_MINORS 256
15+
16+
/**
17+
* DRM_ACCEL_FOPS - Default drm accelerators file operations
18+
*
19+
* This macro provides a shorthand for setting the accelerator file ops in the
20+
* &file_operations structure. If all you need are the default ops, use
21+
* DEFINE_DRM_ACCEL_FOPS instead.
22+
*/
23+
#define DRM_ACCEL_FOPS \
24+
.open = accel_open,\
25+
.release = drm_release,\
26+
.unlocked_ioctl = drm_ioctl,\
27+
.compat_ioctl = drm_compat_ioctl,\
28+
.poll = drm_poll,\
29+
.read = drm_read,\
30+
.llseek = noop_llseek
31+
32+
/**
33+
* DEFINE_DRM_ACCEL_FOPS() - macro to generate file operations for accelerators drivers
34+
* @name: name for the generated structure
35+
*
36+
* This macro autogenerates a suitable &struct file_operations for accelerators based
37+
* drivers, which can be assigned to &drm_driver.fops. Note that this structure
38+
* cannot be shared between drivers, because it contains a reference to the
39+
* current module using THIS_MODULE.
40+
*
41+
* Note that the declaration is already marked as static - if you need a
42+
* non-static version of this you're probably doing it wrong and will break the
43+
* THIS_MODULE reference by accident.
44+
*/
45+
#define DEFINE_DRM_ACCEL_FOPS(name) \
46+
static const struct file_operations name = {\
47+
.owner = THIS_MODULE,\
48+
DRM_ACCEL_FOPS,\
49+
}
1250

1351
#if IS_ENABLED(CONFIG_DRM_ACCEL)
1452

1553
void accel_core_exit(void);
1654
int accel_core_init(void);
55+
void accel_minor_remove(int index);
56+
int accel_minor_alloc(void);
57+
void accel_minor_replace(struct drm_minor *minor, int index);
58+
void accel_set_device_instance_params(struct device *kdev, int index);
59+
int accel_open(struct inode *inode, struct file *filp);
60+
void accel_debugfs_init(struct drm_minor *minor, int minor_id);
1761

1862
#else
1963

@@ -27,6 +71,27 @@ static inline int __init accel_core_init(void)
2771
return 0;
2872
}
2973

74+
static inline void accel_minor_remove(int index)
75+
{
76+
}
77+
78+
static inline int accel_minor_alloc(void)
79+
{
80+
return -EOPNOTSUPP;
81+
}
82+
83+
static inline void accel_minor_replace(struct drm_minor *minor, int index)
84+
{
85+
}
86+
87+
static inline void accel_set_device_instance_params(struct device *kdev, int index)
88+
{
89+
}
90+
91+
static inline void accel_debugfs_init(struct drm_minor *minor, int minor_id)
92+
{
93+
}
94+
3095
#endif /* IS_ENABLED(CONFIG_DRM_ACCEL) */
3196

3297
#endif /* DRM_ACCEL_H_ */

include/drm/drm_device.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ struct drm_device {
9393
/** @render: Render node */
9494
struct drm_minor *render;
9595

96+
/** @accel: Compute Acceleration node */
97+
struct drm_minor *accel;
98+
9699
/**
97100
* @registered:
98101
*

include/drm/drm_drv.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ enum drm_driver_feature {
9696
* synchronization of command submission.
9797
*/
9898
DRIVER_SYNCOBJ_TIMELINE = BIT(6),
99+
/**
100+
* @DRIVER_COMPUTE_ACCEL:
101+
*
102+
* Driver supports compute acceleration devices. This flag is mutually exclusive with
103+
* @DRIVER_RENDER and @DRIVER_MODESET. Devices that support both graphics and compute
104+
* acceleration should be handled by two drivers that are connected using auxiliary bus.
105+
*/
106+
DRIVER_COMPUTE_ACCEL = BIT(7),
99107

100108
/* IMPORTANT: Below are all the legacy flags, add new ones above. */
101109

0 commit comments

Comments
 (0)