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