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