Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2ed077e4 KP |
2 | /* |
3 | * Copyright © 2017 Keith Packard <keithp@keithp.com> | |
2ed077e4 | 4 | */ |
0500c04e SR |
5 | #include <linux/file.h> |
6 | #include <linux/uaccess.h> | |
2ed077e4 | 7 | |
2ed077e4 KP |
8 | #include <drm/drm_auth.h> |
9 | #include <drm/drm_crtc_helper.h> | |
0500c04e SR |
10 | #include <drm/drm_drv.h> |
11 | #include <drm/drm_file.h> | |
12 | #include <drm/drm_lease.h> | |
13 | #include <drm/drm_print.h> | |
14 | ||
15 | #include "drm_crtc_internal.h" | |
16 | #include "drm_internal.h" | |
17 | #include "drm_legacy.h" | |
2ed077e4 KP |
18 | |
19 | #define drm_for_each_lessee(lessee, lessor) \ | |
20 | list_for_each_entry((lessee), &(lessor)->lessees, lessee_list) | |
21 | ||
62884cd3 KP |
22 | static uint64_t drm_lease_idr_object; |
23 | ||
2ed077e4 KP |
24 | /** |
25 | * drm_lease_owner - return ancestor owner drm_master | |
26 | * @master: drm_master somewhere within tree of lessees and lessors | |
27 | * | |
28 | * RETURN: | |
29 | * | |
30 | * drm_master at the top of the tree (i.e, with lessor NULL | |
31 | */ | |
32 | struct drm_master *drm_lease_owner(struct drm_master *master) | |
33 | { | |
34 | while (master->lessor != NULL) | |
35 | master = master->lessor; | |
36 | return master; | |
37 | } | |
2ed077e4 KP |
38 | |
39 | /** | |
40 | * _drm_find_lessee - find lessee by id (idr_mutex held) | |
41 | * @master: drm_master of lessor | |
7591844c | 42 | * @lessee_id: id |
2ed077e4 KP |
43 | * |
44 | * RETURN: | |
45 | * | |
46 | * drm_master of the lessee if valid, NULL otherwise | |
47 | */ | |
48 | ||
49 | static struct drm_master* | |
50 | _drm_find_lessee(struct drm_master *master, int lessee_id) | |
51 | { | |
52 | lockdep_assert_held(&master->dev->mode_config.idr_mutex); | |
53 | return idr_find(&drm_lease_owner(master)->lessee_idr, lessee_id); | |
54 | } | |
55 | ||
56 | /** | |
57 | * _drm_lease_held_master - check to see if an object is leased (or owned) by master (idr_mutex held) | |
58 | * @master: the master to check the lease status of | |
59 | * @id: the id to check | |
60 | * | |
61 | * Checks if the specified master holds a lease on the object. Return | |
62 | * value: | |
63 | * | |
64 | * true 'master' holds a lease on (or owns) the object | |
65 | * false 'master' does not hold a lease. | |
66 | */ | |
67 | static int _drm_lease_held_master(struct drm_master *master, int id) | |
68 | { | |
69 | lockdep_assert_held(&master->dev->mode_config.idr_mutex); | |
70 | if (master->lessor) | |
71 | return idr_find(&master->leases, id) != NULL; | |
72 | return true; | |
73 | } | |
74 | ||
75 | /** | |
76 | * _drm_has_leased - check to see if an object has been leased (idr_mutex held) | |
77 | * @master: the master to check the lease status of | |
78 | * @id: the id to check | |
79 | * | |
80 | * Checks if any lessee of 'master' holds a lease on 'id'. Return | |
81 | * value: | |
82 | * | |
83 | * true Some lessee holds a lease on the object. | |
84 | * false No lessee has a lease on the object. | |
85 | */ | |
86 | static bool _drm_has_leased(struct drm_master *master, int id) | |
87 | { | |
88 | struct drm_master *lessee; | |
89 | ||
90 | lockdep_assert_held(&master->dev->mode_config.idr_mutex); | |
91 | drm_for_each_lessee(lessee, master) | |
92 | if (_drm_lease_held_master(lessee, id)) | |
93 | return true; | |
94 | return false; | |
95 | } | |
96 | ||
97 | /** | |
98 | * _drm_lease_held - check drm_mode_object lease status (idr_mutex held) | |
7591844c | 99 | * @file_priv: the master drm_file |
2ed077e4 KP |
100 | * @id: the object id |
101 | * | |
102 | * Checks if the specified master holds a lease on the object. Return | |
103 | * value: | |
104 | * | |
105 | * true 'master' holds a lease on (or owns) the object | |
106 | * false 'master' does not hold a lease. | |
107 | */ | |
108 | bool _drm_lease_held(struct drm_file *file_priv, int id) | |
109 | { | |
46b75778 | 110 | if (!file_priv || !file_priv->master) |
2ed077e4 KP |
111 | return true; |
112 | ||
113 | return _drm_lease_held_master(file_priv->master, id); | |
114 | } | |
2ed077e4 KP |
115 | |
116 | /** | |
117 | * drm_lease_held - check drm_mode_object lease status (idr_mutex not held) | |
7591844c | 118 | * @file_priv: the master drm_file |
2ed077e4 KP |
119 | * @id: the object id |
120 | * | |
121 | * Checks if the specified master holds a lease on the object. Return | |
122 | * value: | |
123 | * | |
124 | * true 'master' holds a lease on (or owns) the object | |
125 | * false 'master' does not hold a lease. | |
126 | */ | |
127 | bool drm_lease_held(struct drm_file *file_priv, int id) | |
128 | { | |
129 | struct drm_master *master; | |
130 | bool ret; | |
131 | ||
46b75778 | 132 | if (!file_priv || !file_priv->master || !file_priv->master->lessor) |
2ed077e4 KP |
133 | return true; |
134 | ||
135 | master = file_priv->master; | |
136 | mutex_lock(&master->dev->mode_config.idr_mutex); | |
137 | ret = _drm_lease_held_master(master, id); | |
138 | mutex_unlock(&master->dev->mode_config.idr_mutex); | |
139 | return ret; | |
140 | } | |
2ed077e4 KP |
141 | |
142 | /** | |
143 | * drm_lease_filter_crtcs - restricted crtc set to leased values (idr_mutex not held) | |
144 | * @file_priv: requestor file | |
7591844c | 145 | * @crtcs_in: bitmask of crtcs to check |
2ed077e4 KP |
146 | * |
147 | * Reconstructs a crtc mask based on the crtcs which are visible | |
148 | * through the specified file. | |
149 | */ | |
150 | uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs_in) | |
151 | { | |
152 | struct drm_master *master; | |
153 | struct drm_device *dev; | |
154 | struct drm_crtc *crtc; | |
155 | int count_in, count_out; | |
156 | uint32_t crtcs_out = 0; | |
157 | ||
46b75778 | 158 | if (!file_priv || !file_priv->master || !file_priv->master->lessor) |
2ed077e4 KP |
159 | return crtcs_in; |
160 | ||
161 | master = file_priv->master; | |
162 | dev = master->dev; | |
163 | ||
164 | count_in = count_out = 0; | |
165 | mutex_lock(&master->dev->mode_config.idr_mutex); | |
166 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | |
167 | if (_drm_lease_held_master(master, crtc->base.id)) { | |
168 | uint32_t mask_in = 1ul << count_in; | |
169 | if ((crtcs_in & mask_in) != 0) { | |
170 | uint32_t mask_out = 1ul << count_out; | |
171 | crtcs_out |= mask_out; | |
172 | } | |
173 | count_out++; | |
174 | } | |
175 | count_in++; | |
176 | } | |
177 | mutex_unlock(&master->dev->mode_config.idr_mutex); | |
178 | return crtcs_out; | |
179 | } | |
2ed077e4 KP |
180 | |
181 | /* | |
182 | * drm_lease_create - create a new drm_master with leased objects (idr_mutex not held) | |
183 | * @lessor: lease holder (or owner) of objects | |
184 | * @leases: objects to lease to the new drm_master | |
185 | * | |
186 | * Uses drm_master_create to allocate a new drm_master, then checks to | |
187 | * make sure all of the desired objects can be leased, atomically | |
188 | * leasing them to the new drmmaster. | |
189 | * | |
918d89bb | 190 | * ERR_PTR(-EACCES) some other master holds the title to any object |
2ed077e4 KP |
191 | * ERR_PTR(-ENOENT) some object is not a valid DRM object for this device |
192 | * ERR_PTR(-EBUSY) some other lessee holds title to this object | |
193 | * ERR_PTR(-EEXIST) same object specified more than once in the provided list | |
194 | * ERR_PTR(-ENOMEM) allocation failed | |
195 | */ | |
196 | static struct drm_master *drm_lease_create(struct drm_master *lessor, struct idr *leases) | |
197 | { | |
198 | struct drm_device *dev = lessor->dev; | |
199 | int error; | |
200 | struct drm_master *lessee; | |
201 | int object; | |
202 | int id; | |
203 | void *entry; | |
204 | ||
205 | DRM_DEBUG_LEASE("lessor %d\n", lessor->lessee_id); | |
206 | ||
207 | lessee = drm_master_create(lessor->dev); | |
208 | if (!lessee) { | |
209 | DRM_DEBUG_LEASE("drm_master_create failed\n"); | |
210 | return ERR_PTR(-ENOMEM); | |
211 | } | |
212 | ||
213 | mutex_lock(&dev->mode_config.idr_mutex); | |
214 | ||
2ed077e4 KP |
215 | idr_for_each_entry(leases, entry, object) { |
216 | error = 0; | |
b5f06893 | 217 | if (!idr_find(&dev->mode_config.object_idr, object)) |
2ed077e4 | 218 | error = -ENOENT; |
2ed077e4 KP |
219 | else if (_drm_has_leased(lessor, object)) |
220 | error = -EBUSY; | |
221 | ||
222 | if (error != 0) { | |
223 | DRM_DEBUG_LEASE("object %d failed %d\n", object, error); | |
224 | goto out_lessee; | |
225 | } | |
226 | } | |
227 | ||
d2a48e52 KP |
228 | /* Insert the new lessee into the tree */ |
229 | id = idr_alloc(&(drm_lease_owner(lessor)->lessee_idr), lessee, 1, 0, GFP_KERNEL); | |
230 | if (id < 0) { | |
231 | error = id; | |
232 | goto out_lessee; | |
233 | } | |
234 | ||
235 | lessee->lessee_id = id; | |
236 | lessee->lessor = drm_master_get(lessor); | |
237 | list_add_tail(&lessee->lessee_list, &lessor->lessees); | |
238 | ||
2ed077e4 KP |
239 | /* Move the leases over */ |
240 | lessee->leases = *leases; | |
241 | DRM_DEBUG_LEASE("new lessee %d %p, lessor %d %p\n", lessee->lessee_id, lessee, lessor->lessee_id, lessor); | |
242 | ||
243 | mutex_unlock(&dev->mode_config.idr_mutex); | |
244 | return lessee; | |
245 | ||
246 | out_lessee: | |
2ed077e4 KP |
247 | mutex_unlock(&dev->mode_config.idr_mutex); |
248 | ||
bd36d3ba MV |
249 | drm_master_put(&lessee); |
250 | ||
2ed077e4 KP |
251 | return ERR_PTR(error); |
252 | } | |
253 | ||
254 | /** | |
255 | * drm_lease_destroy - a master is going away (idr_mutex not held) | |
256 | * @master: the drm_master being destroyed | |
257 | * | |
258 | * All lessees will have been destroyed as they | |
259 | * hold a reference on their lessor. Notify any | |
260 | * lessor for this master so that it can check | |
261 | * the list of lessees. | |
262 | */ | |
263 | void drm_lease_destroy(struct drm_master *master) | |
264 | { | |
265 | struct drm_device *dev = master->dev; | |
266 | ||
267 | mutex_lock(&dev->mode_config.idr_mutex); | |
268 | ||
269 | DRM_DEBUG_LEASE("drm_lease_destroy %d\n", master->lessee_id); | |
270 | ||
271 | /* This master is referenced by all lessees, hence it cannot be destroyed | |
272 | * until all of them have been | |
273 | */ | |
274 | WARN_ON(!list_empty(&master->lessees)); | |
275 | ||
276 | /* Remove this master from the lessee idr in the owner */ | |
277 | if (master->lessee_id != 0) { | |
278 | DRM_DEBUG_LEASE("remove master %d from device list of lessees\n", master->lessee_id); | |
279 | idr_remove(&(drm_lease_owner(master)->lessee_idr), master->lessee_id); | |
280 | } | |
281 | ||
282 | /* Remove this master from any lessee list it may be on */ | |
283 | list_del(&master->lessee_list); | |
284 | ||
285 | mutex_unlock(&dev->mode_config.idr_mutex); | |
286 | ||
287 | if (master->lessor) { | |
288 | /* Tell the master to check the lessee list */ | |
ce858828 | 289 | drm_sysfs_lease_event(dev); |
2ed077e4 KP |
290 | drm_master_put(&master->lessor); |
291 | } | |
292 | ||
293 | DRM_DEBUG_LEASE("drm_lease_destroy done %d\n", master->lessee_id); | |
294 | } | |
295 | ||
296 | /** | |
297 | * _drm_lease_revoke - revoke access to all leased objects (idr_mutex held) | |
7591844c | 298 | * @top: the master losing its lease |
2ed077e4 KP |
299 | */ |
300 | static void _drm_lease_revoke(struct drm_master *top) | |
301 | { | |
302 | int object; | |
303 | void *entry; | |
304 | struct drm_master *master = top; | |
305 | ||
306 | lockdep_assert_held(&top->dev->mode_config.idr_mutex); | |
307 | ||
308 | /* | |
309 | * Walk the tree starting at 'top' emptying all leases. Because | |
310 | * the tree is fully connected, we can do this without recursing | |
311 | */ | |
312 | for (;;) { | |
313 | DRM_DEBUG_LEASE("revoke leases for %p %d\n", master, master->lessee_id); | |
314 | ||
315 | /* Evacuate the lease */ | |
316 | idr_for_each_entry(&master->leases, entry, object) | |
317 | idr_remove(&master->leases, object); | |
318 | ||
319 | /* Depth-first list walk */ | |
320 | ||
321 | /* Down */ | |
322 | if (!list_empty(&master->lessees)) { | |
323 | master = list_first_entry(&master->lessees, struct drm_master, lessee_list); | |
324 | } else { | |
325 | /* Up */ | |
326 | while (master != top && master == list_last_entry(&master->lessor->lessees, struct drm_master, lessee_list)) | |
327 | master = master->lessor; | |
328 | ||
329 | if (master == top) | |
330 | break; | |
331 | ||
332 | /* Over */ | |
aec06c76 | 333 | master = list_next_entry(master, lessee_list); |
2ed077e4 KP |
334 | } |
335 | } | |
336 | } | |
337 | ||
338 | /** | |
339 | * drm_lease_revoke - revoke access to all leased objects (idr_mutex not held) | |
340 | * @top: the master losing its lease | |
341 | */ | |
342 | void drm_lease_revoke(struct drm_master *top) | |
343 | { | |
344 | mutex_lock(&top->dev->mode_config.idr_mutex); | |
345 | _drm_lease_revoke(top); | |
346 | mutex_unlock(&top->dev->mode_config.idr_mutex); | |
347 | } | |
62884cd3 KP |
348 | |
349 | static int validate_lease(struct drm_device *dev, | |
62884cd3 | 350 | int object_count, |
96802905 DV |
351 | struct drm_mode_object **objects, |
352 | bool universal_planes) | |
62884cd3 KP |
353 | { |
354 | int o; | |
355 | int has_crtc = -1; | |
356 | int has_connector = -1; | |
357 | int has_plane = -1; | |
358 | ||
359 | /* we want to confirm that there is at least one crtc, plane | |
360 | connector object. */ | |
361 | ||
362 | for (o = 0; o < object_count; o++) { | |
363 | if (objects[o]->type == DRM_MODE_OBJECT_CRTC && has_crtc == -1) { | |
364 | has_crtc = o; | |
365 | } | |
366 | if (objects[o]->type == DRM_MODE_OBJECT_CONNECTOR && has_connector == -1) | |
367 | has_connector = o; | |
368 | ||
96802905 | 369 | if (universal_planes) { |
62884cd3 KP |
370 | if (objects[o]->type == DRM_MODE_OBJECT_PLANE && has_plane == -1) |
371 | has_plane = o; | |
372 | } | |
373 | } | |
374 | if (has_crtc == -1 || has_connector == -1) | |
375 | return -EINVAL; | |
96802905 | 376 | if (universal_planes && has_plane == -1) |
62884cd3 KP |
377 | return -EINVAL; |
378 | return 0; | |
379 | } | |
380 | ||
381 | static int fill_object_idr(struct drm_device *dev, | |
382 | struct drm_file *lessor_priv, | |
383 | struct idr *leases, | |
384 | int object_count, | |
385 | u32 *object_ids) | |
386 | { | |
387 | struct drm_mode_object **objects; | |
388 | u32 o; | |
389 | int ret; | |
96802905 DV |
390 | bool universal_planes = READ_ONCE(lessor_priv->universal_planes); |
391 | ||
62884cd3 KP |
392 | objects = kcalloc(object_count, sizeof(struct drm_mode_object *), |
393 | GFP_KERNEL); | |
394 | if (!objects) | |
395 | return -ENOMEM; | |
396 | ||
397 | /* step one - get references to all the mode objects | |
398 | and check for validity. */ | |
399 | for (o = 0; o < object_count; o++) { | |
62884cd3 KP |
400 | objects[o] = drm_mode_object_find(dev, lessor_priv, |
401 | object_ids[o], | |
402 | DRM_MODE_OBJECT_ANY); | |
403 | if (!objects[o]) { | |
404 | ret = -ENOENT; | |
405 | goto out_free_objects; | |
406 | } | |
407 | ||
408 | if (!drm_mode_object_lease_required(objects[o]->type)) { | |
a0c1af46 | 409 | DRM_DEBUG_KMS("invalid object for lease\n"); |
62884cd3 KP |
410 | ret = -EINVAL; |
411 | goto out_free_objects; | |
412 | } | |
413 | } | |
414 | ||
96802905 | 415 | ret = validate_lease(dev, object_count, objects, universal_planes); |
a0c1af46 DV |
416 | if (ret) { |
417 | DRM_DEBUG_LEASE("lease validation failed\n"); | |
62884cd3 | 418 | goto out_free_objects; |
a0c1af46 | 419 | } |
62884cd3 KP |
420 | |
421 | /* add their IDs to the lease request - taking into account | |
422 | universal planes */ | |
423 | for (o = 0; o < object_count; o++) { | |
424 | struct drm_mode_object *obj = objects[o]; | |
425 | u32 object_id = objects[o]->id; | |
426 | DRM_DEBUG_LEASE("Adding object %d to lease\n", object_id); | |
427 | ||
428 | /* | |
429 | * We're using an IDR to hold the set of leased | |
430 | * objects, but we don't need to point at the object's | |
b5f06893 | 431 | * data structure from the lease as the main object_idr |
62884cd3 KP |
432 | * will be used to actually find that. Instead, all we |
433 | * really want is a 'leased/not-leased' result, for | |
434 | * which any non-NULL pointer will work fine. | |
435 | */ | |
436 | ret = idr_alloc(leases, &drm_lease_idr_object , object_id, object_id + 1, GFP_KERNEL); | |
437 | if (ret < 0) { | |
438 | DRM_DEBUG_LEASE("Object %d cannot be inserted into leases (%d)\n", | |
439 | object_id, ret); | |
440 | goto out_free_objects; | |
441 | } | |
96802905 | 442 | if (obj->type == DRM_MODE_OBJECT_CRTC && !universal_planes) { |
62884cd3 KP |
443 | struct drm_crtc *crtc = obj_to_crtc(obj); |
444 | ret = idr_alloc(leases, &drm_lease_idr_object, crtc->primary->base.id, crtc->primary->base.id + 1, GFP_KERNEL); | |
445 | if (ret < 0) { | |
446 | DRM_DEBUG_LEASE("Object primary plane %d cannot be inserted into leases (%d)\n", | |
447 | object_id, ret); | |
448 | goto out_free_objects; | |
449 | } | |
450 | if (crtc->cursor) { | |
451 | ret = idr_alloc(leases, &drm_lease_idr_object, crtc->cursor->base.id, crtc->cursor->base.id + 1, GFP_KERNEL); | |
452 | if (ret < 0) { | |
453 | DRM_DEBUG_LEASE("Object cursor plane %d cannot be inserted into leases (%d)\n", | |
454 | object_id, ret); | |
455 | goto out_free_objects; | |
456 | } | |
457 | } | |
458 | } | |
459 | } | |
460 | ||
461 | ret = 0; | |
462 | out_free_objects: | |
463 | for (o = 0; o < object_count; o++) { | |
464 | if (objects[o]) | |
465 | drm_mode_object_put(objects[o]); | |
466 | } | |
467 | kfree(objects); | |
468 | return ret; | |
469 | } | |
470 | ||
471 | /** | |
472 | * drm_mode_create_lease_ioctl - create a new lease | |
473 | * @dev: the drm device | |
474 | * @data: pointer to struct drm_mode_create_lease | |
7591844c | 475 | * @lessor_priv: the file being manipulated |
62884cd3 KP |
476 | * |
477 | * The master associated with the specified file will have a lease | |
478 | * created containing the objects specified in the ioctl structure. | |
479 | * A file descriptor will be allocated for that and returned to the | |
480 | * application. | |
481 | */ | |
482 | int drm_mode_create_lease_ioctl(struct drm_device *dev, | |
483 | void *data, struct drm_file *lessor_priv) | |
484 | { | |
485 | struct drm_mode_create_lease *cl = data; | |
486 | size_t object_count; | |
487 | int ret = 0; | |
488 | struct idr leases; | |
489 | struct drm_master *lessor = lessor_priv->master; | |
490 | struct drm_master *lessee = NULL; | |
491 | struct file *lessee_file = NULL; | |
492 | struct file *lessor_file = lessor_priv->filp; | |
493 | struct drm_file *lessee_priv; | |
494 | int fd = -1; | |
495 | uint32_t *object_ids; | |
496 | ||
497 | /* Can't lease without MODESET */ | |
498 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 499 | return -EOPNOTSUPP; |
62884cd3 KP |
500 | |
501 | /* Do not allow sub-leases */ | |
a0c1af46 DV |
502 | if (lessor->lessor) { |
503 | DRM_DEBUG_LEASE("recursive leasing not allowed\n"); | |
62884cd3 | 504 | return -EINVAL; |
a0c1af46 | 505 | } |
62884cd3 KP |
506 | |
507 | /* need some objects */ | |
a0c1af46 DV |
508 | if (cl->object_count == 0) { |
509 | DRM_DEBUG_LEASE("no objects in lease\n"); | |
62884cd3 | 510 | return -EINVAL; |
a0c1af46 | 511 | } |
62884cd3 | 512 | |
a0c1af46 DV |
513 | if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK))) { |
514 | DRM_DEBUG_LEASE("invalid flags\n"); | |
62884cd3 | 515 | return -EINVAL; |
a0c1af46 | 516 | } |
62884cd3 KP |
517 | |
518 | object_count = cl->object_count; | |
519 | ||
69ef943d MW |
520 | object_ids = memdup_user(u64_to_user_ptr(cl->object_ids), |
521 | array_size(object_count, sizeof(__u32))); | |
62884cd3 KP |
522 | if (IS_ERR(object_ids)) |
523 | return PTR_ERR(object_ids); | |
524 | ||
525 | idr_init(&leases); | |
526 | ||
527 | /* fill and validate the object idr */ | |
528 | ret = fill_object_idr(dev, lessor_priv, &leases, | |
529 | object_count, object_ids); | |
530 | kfree(object_ids); | |
531 | if (ret) { | |
a0c1af46 | 532 | DRM_DEBUG_LEASE("lease object lookup failed: %i\n", ret); |
62884cd3 KP |
533 | idr_destroy(&leases); |
534 | return ret; | |
535 | } | |
536 | ||
537 | /* Allocate a file descriptor for the lease */ | |
538 | fd = get_unused_fd_flags(cl->flags & (O_CLOEXEC | O_NONBLOCK)); | |
539 | if (fd < 0) { | |
540 | idr_destroy(&leases); | |
541 | return fd; | |
542 | } | |
543 | ||
544 | DRM_DEBUG_LEASE("Creating lease\n"); | |
545 | lessee = drm_lease_create(lessor, &leases); | |
546 | ||
547 | if (IS_ERR(lessee)) { | |
548 | ret = PTR_ERR(lessee); | |
549 | goto out_leases; | |
550 | } | |
551 | ||
552 | /* Clone the lessor file to create a new file for us */ | |
553 | DRM_DEBUG_LEASE("Allocating lease file\n"); | |
19f391eb | 554 | lessee_file = file_clone_open(lessor_file); |
62884cd3 KP |
555 | if (IS_ERR(lessee_file)) { |
556 | ret = PTR_ERR(lessee_file); | |
557 | goto out_lessee; | |
558 | } | |
559 | ||
62884cd3 | 560 | lessee_priv = lessee_file->private_data; |
62884cd3 KP |
561 | /* Change the file to a master one */ |
562 | drm_master_put(&lessee_priv->master); | |
563 | lessee_priv->master = lessee; | |
564 | lessee_priv->is_master = 1; | |
565 | lessee_priv->authenticated = 1; | |
566 | ||
62884cd3 KP |
567 | /* Pass fd back to userspace */ |
568 | DRM_DEBUG_LEASE("Returning fd %d id %d\n", fd, lessee->lessee_id); | |
569 | cl->fd = fd; | |
570 | cl->lessee_id = lessee->lessee_id; | |
571 | ||
12d43deb JH |
572 | /* Hook up the fd */ |
573 | fd_install(fd, lessee_file); | |
574 | ||
62884cd3 KP |
575 | DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl succeeded\n"); |
576 | return 0; | |
577 | ||
62884cd3 KP |
578 | out_lessee: |
579 | drm_master_put(&lessee); | |
580 | ||
581 | out_leases: | |
582 | put_unused_fd(fd); | |
583 | idr_destroy(&leases); | |
584 | ||
585 | DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl failed: %d\n", ret); | |
586 | return ret; | |
587 | } | |
588 | ||
589 | /** | |
590 | * drm_mode_list_lessees_ioctl - list lessee ids | |
591 | * @dev: the drm device | |
592 | * @data: pointer to struct drm_mode_list_lessees | |
593 | * @lessor_priv: the file being manipulated | |
594 | * | |
595 | * Starting from the master associated with the specified file, | |
596 | * the master with the provided lessee_id is found, and then | |
597 | * an array of lessee ids associated with leases from that master | |
598 | * are returned. | |
599 | */ | |
600 | ||
601 | int drm_mode_list_lessees_ioctl(struct drm_device *dev, | |
602 | void *data, struct drm_file *lessor_priv) | |
603 | { | |
604 | struct drm_mode_list_lessees *arg = data; | |
605 | __u32 __user *lessee_ids = (__u32 __user *) (uintptr_t) (arg->lessees_ptr); | |
606 | __u32 count_lessees = arg->count_lessees; | |
607 | struct drm_master *lessor = lessor_priv->master, *lessee; | |
608 | int count; | |
609 | int ret = 0; | |
610 | ||
611 | if (arg->pad) | |
612 | return -EINVAL; | |
613 | ||
614 | /* Can't lease without MODESET */ | |
615 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 616 | return -EOPNOTSUPP; |
62884cd3 KP |
617 | |
618 | DRM_DEBUG_LEASE("List lessees for %d\n", lessor->lessee_id); | |
619 | ||
620 | mutex_lock(&dev->mode_config.idr_mutex); | |
621 | ||
622 | count = 0; | |
623 | drm_for_each_lessee(lessee, lessor) { | |
624 | /* Only list un-revoked leases */ | |
625 | if (!idr_is_empty(&lessee->leases)) { | |
626 | if (count_lessees > count) { | |
627 | DRM_DEBUG_LEASE("Add lessee %d\n", lessee->lessee_id); | |
628 | ret = put_user(lessee->lessee_id, lessee_ids + count); | |
629 | if (ret) | |
630 | break; | |
631 | } | |
632 | count++; | |
633 | } | |
634 | } | |
635 | ||
636 | DRM_DEBUG_LEASE("Lessor leases to %d\n", count); | |
637 | if (ret == 0) | |
638 | arg->count_lessees = count; | |
639 | ||
640 | mutex_unlock(&dev->mode_config.idr_mutex); | |
641 | ||
642 | return ret; | |
643 | } | |
644 | ||
645 | /** | |
646 | * drm_mode_get_lease_ioctl - list leased objects | |
647 | * @dev: the drm device | |
648 | * @data: pointer to struct drm_mode_get_lease | |
7591844c | 649 | * @lessee_priv: the file being manipulated |
62884cd3 KP |
650 | * |
651 | * Return the list of leased objects for the specified lessee | |
652 | */ | |
653 | ||
654 | int drm_mode_get_lease_ioctl(struct drm_device *dev, | |
655 | void *data, struct drm_file *lessee_priv) | |
656 | { | |
657 | struct drm_mode_get_lease *arg = data; | |
658 | __u32 __user *object_ids = (__u32 __user *) (uintptr_t) (arg->objects_ptr); | |
659 | __u32 count_objects = arg->count_objects; | |
660 | struct drm_master *lessee = lessee_priv->master; | |
661 | struct idr *object_idr; | |
662 | int count; | |
663 | void *entry; | |
664 | int object; | |
665 | int ret = 0; | |
666 | ||
667 | if (arg->pad) | |
668 | return -EINVAL; | |
669 | ||
670 | /* Can't lease without MODESET */ | |
671 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 672 | return -EOPNOTSUPP; |
62884cd3 KP |
673 | |
674 | DRM_DEBUG_LEASE("get lease for %d\n", lessee->lessee_id); | |
675 | ||
676 | mutex_lock(&dev->mode_config.idr_mutex); | |
677 | ||
678 | if (lessee->lessor == NULL) | |
679 | /* owner can use all objects */ | |
b5f06893 | 680 | object_idr = &lessee->dev->mode_config.object_idr; |
62884cd3 KP |
681 | else |
682 | /* lessee can only use allowed object */ | |
683 | object_idr = &lessee->leases; | |
684 | ||
685 | count = 0; | |
686 | idr_for_each_entry(object_idr, entry, object) { | |
687 | if (count_objects > count) { | |
688 | DRM_DEBUG_LEASE("adding object %d\n", object); | |
689 | ret = put_user(object, object_ids + count); | |
690 | if (ret) | |
691 | break; | |
692 | } | |
693 | count++; | |
694 | } | |
695 | ||
696 | DRM_DEBUG("lease holds %d objects\n", count); | |
697 | if (ret == 0) | |
698 | arg->count_objects = count; | |
699 | ||
700 | mutex_unlock(&dev->mode_config.idr_mutex); | |
701 | ||
702 | return ret; | |
703 | } | |
704 | ||
705 | /** | |
706 | * drm_mode_revoke_lease_ioctl - revoke lease | |
707 | * @dev: the drm device | |
708 | * @data: pointer to struct drm_mode_revoke_lease | |
7591844c | 709 | * @lessor_priv: the file being manipulated |
62884cd3 KP |
710 | * |
711 | * This removes all of the objects from the lease without | |
712 | * actually getting rid of the lease itself; that way all | |
713 | * references to it still work correctly | |
714 | */ | |
715 | int drm_mode_revoke_lease_ioctl(struct drm_device *dev, | |
716 | void *data, struct drm_file *lessor_priv) | |
717 | { | |
718 | struct drm_mode_revoke_lease *arg = data; | |
719 | struct drm_master *lessor = lessor_priv->master; | |
720 | struct drm_master *lessee; | |
721 | int ret = 0; | |
722 | ||
723 | DRM_DEBUG_LEASE("revoke lease for %d\n", arg->lessee_id); | |
724 | ||
725 | /* Can't lease without MODESET */ | |
726 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 727 | return -EOPNOTSUPP; |
62884cd3 KP |
728 | |
729 | mutex_lock(&dev->mode_config.idr_mutex); | |
730 | ||
731 | lessee = _drm_find_lessee(lessor, arg->lessee_id); | |
732 | ||
733 | /* No such lessee */ | |
734 | if (!lessee) { | |
735 | ret = -ENOENT; | |
736 | goto fail; | |
737 | } | |
738 | ||
739 | /* Lease is not held by lessor */ | |
740 | if (lessee->lessor != lessor) { | |
741 | ret = -EACCES; | |
742 | goto fail; | |
743 | } | |
744 | ||
745 | _drm_lease_revoke(lessee); | |
746 | ||
747 | fail: | |
748 | mutex_unlock(&dev->mode_config.idr_mutex); | |
749 | ||
750 | return ret; | |
751 | } |