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 */ | |
ce858828 | 295 | drm_sysfs_lease_event(dev); |
2ed077e4 KP |
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, | |
62884cd3 | 356 | int object_count, |
96802905 DV |
357 | struct drm_mode_object **objects, |
358 | bool universal_planes) | |
62884cd3 KP |
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 | ||
96802905 | 375 | if (universal_planes) { |
62884cd3 KP |
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; | |
96802905 | 382 | if (universal_planes && has_plane == -1) |
62884cd3 KP |
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; | |
96802905 DV |
396 | bool universal_planes = READ_ONCE(lessor_priv->universal_planes); |
397 | ||
62884cd3 KP |
398 | objects = kcalloc(object_count, sizeof(struct drm_mode_object *), |
399 | GFP_KERNEL); | |
400 | if (!objects) | |
401 | return -ENOMEM; | |
402 | ||
403 | /* step one - get references to all the mode objects | |
404 | and check for validity. */ | |
405 | for (o = 0; o < object_count; o++) { | |
406 | if ((int) object_ids[o] < 0) { | |
407 | ret = -EINVAL; | |
408 | goto out_free_objects; | |
409 | } | |
410 | ||
411 | objects[o] = drm_mode_object_find(dev, lessor_priv, | |
412 | object_ids[o], | |
413 | DRM_MODE_OBJECT_ANY); | |
414 | if (!objects[o]) { | |
415 | ret = -ENOENT; | |
416 | goto out_free_objects; | |
417 | } | |
418 | ||
419 | if (!drm_mode_object_lease_required(objects[o]->type)) { | |
a0c1af46 | 420 | DRM_DEBUG_KMS("invalid object for lease\n"); |
62884cd3 KP |
421 | ret = -EINVAL; |
422 | goto out_free_objects; | |
423 | } | |
424 | } | |
425 | ||
96802905 | 426 | ret = validate_lease(dev, object_count, objects, universal_planes); |
a0c1af46 DV |
427 | if (ret) { |
428 | DRM_DEBUG_LEASE("lease validation failed\n"); | |
62884cd3 | 429 | goto out_free_objects; |
a0c1af46 | 430 | } |
62884cd3 KP |
431 | |
432 | /* add their IDs to the lease request - taking into account | |
433 | universal planes */ | |
434 | for (o = 0; o < object_count; o++) { | |
435 | struct drm_mode_object *obj = objects[o]; | |
436 | u32 object_id = objects[o]->id; | |
437 | DRM_DEBUG_LEASE("Adding object %d to lease\n", object_id); | |
438 | ||
439 | /* | |
440 | * We're using an IDR to hold the set of leased | |
441 | * objects, but we don't need to point at the object's | |
442 | * data structure from the lease as the main crtc_idr | |
443 | * will be used to actually find that. Instead, all we | |
444 | * really want is a 'leased/not-leased' result, for | |
445 | * which any non-NULL pointer will work fine. | |
446 | */ | |
447 | ret = idr_alloc(leases, &drm_lease_idr_object , object_id, object_id + 1, GFP_KERNEL); | |
448 | if (ret < 0) { | |
449 | DRM_DEBUG_LEASE("Object %d cannot be inserted into leases (%d)\n", | |
450 | object_id, ret); | |
451 | goto out_free_objects; | |
452 | } | |
96802905 | 453 | if (obj->type == DRM_MODE_OBJECT_CRTC && !universal_planes) { |
62884cd3 KP |
454 | struct drm_crtc *crtc = obj_to_crtc(obj); |
455 | ret = idr_alloc(leases, &drm_lease_idr_object, crtc->primary->base.id, crtc->primary->base.id + 1, GFP_KERNEL); | |
456 | if (ret < 0) { | |
457 | DRM_DEBUG_LEASE("Object primary plane %d cannot be inserted into leases (%d)\n", | |
458 | object_id, ret); | |
459 | goto out_free_objects; | |
460 | } | |
461 | if (crtc->cursor) { | |
462 | ret = idr_alloc(leases, &drm_lease_idr_object, crtc->cursor->base.id, crtc->cursor->base.id + 1, GFP_KERNEL); | |
463 | if (ret < 0) { | |
464 | DRM_DEBUG_LEASE("Object cursor plane %d cannot be inserted into leases (%d)\n", | |
465 | object_id, ret); | |
466 | goto out_free_objects; | |
467 | } | |
468 | } | |
469 | } | |
470 | } | |
471 | ||
472 | ret = 0; | |
473 | out_free_objects: | |
474 | for (o = 0; o < object_count; o++) { | |
475 | if (objects[o]) | |
476 | drm_mode_object_put(objects[o]); | |
477 | } | |
478 | kfree(objects); | |
479 | return ret; | |
480 | } | |
481 | ||
482 | /** | |
483 | * drm_mode_create_lease_ioctl - create a new lease | |
484 | * @dev: the drm device | |
485 | * @data: pointer to struct drm_mode_create_lease | |
7591844c | 486 | * @lessor_priv: the file being manipulated |
62884cd3 KP |
487 | * |
488 | * The master associated with the specified file will have a lease | |
489 | * created containing the objects specified in the ioctl structure. | |
490 | * A file descriptor will be allocated for that and returned to the | |
491 | * application. | |
492 | */ | |
493 | int drm_mode_create_lease_ioctl(struct drm_device *dev, | |
494 | void *data, struct drm_file *lessor_priv) | |
495 | { | |
496 | struct drm_mode_create_lease *cl = data; | |
497 | size_t object_count; | |
498 | int ret = 0; | |
499 | struct idr leases; | |
500 | struct drm_master *lessor = lessor_priv->master; | |
501 | struct drm_master *lessee = NULL; | |
502 | struct file *lessee_file = NULL; | |
503 | struct file *lessor_file = lessor_priv->filp; | |
504 | struct drm_file *lessee_priv; | |
505 | int fd = -1; | |
506 | uint32_t *object_ids; | |
507 | ||
508 | /* Can't lease without MODESET */ | |
509 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 510 | return -EOPNOTSUPP; |
62884cd3 KP |
511 | |
512 | /* Do not allow sub-leases */ | |
a0c1af46 DV |
513 | if (lessor->lessor) { |
514 | DRM_DEBUG_LEASE("recursive leasing not allowed\n"); | |
62884cd3 | 515 | return -EINVAL; |
a0c1af46 | 516 | } |
62884cd3 KP |
517 | |
518 | /* need some objects */ | |
a0c1af46 DV |
519 | if (cl->object_count == 0) { |
520 | DRM_DEBUG_LEASE("no objects in lease\n"); | |
62884cd3 | 521 | return -EINVAL; |
a0c1af46 | 522 | } |
62884cd3 | 523 | |
a0c1af46 DV |
524 | if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK))) { |
525 | DRM_DEBUG_LEASE("invalid flags\n"); | |
62884cd3 | 526 | return -EINVAL; |
a0c1af46 | 527 | } |
62884cd3 KP |
528 | |
529 | object_count = cl->object_count; | |
530 | ||
531 | object_ids = memdup_user(u64_to_user_ptr(cl->object_ids), object_count * sizeof(__u32)); | |
532 | if (IS_ERR(object_ids)) | |
533 | return PTR_ERR(object_ids); | |
534 | ||
535 | idr_init(&leases); | |
536 | ||
537 | /* fill and validate the object idr */ | |
538 | ret = fill_object_idr(dev, lessor_priv, &leases, | |
539 | object_count, object_ids); | |
540 | kfree(object_ids); | |
541 | if (ret) { | |
a0c1af46 | 542 | DRM_DEBUG_LEASE("lease object lookup failed: %i\n", ret); |
62884cd3 KP |
543 | idr_destroy(&leases); |
544 | return ret; | |
545 | } | |
546 | ||
547 | /* Allocate a file descriptor for the lease */ | |
548 | fd = get_unused_fd_flags(cl->flags & (O_CLOEXEC | O_NONBLOCK)); | |
549 | if (fd < 0) { | |
550 | idr_destroy(&leases); | |
551 | return fd; | |
552 | } | |
553 | ||
554 | DRM_DEBUG_LEASE("Creating lease\n"); | |
555 | lessee = drm_lease_create(lessor, &leases); | |
556 | ||
557 | if (IS_ERR(lessee)) { | |
558 | ret = PTR_ERR(lessee); | |
559 | goto out_leases; | |
560 | } | |
561 | ||
562 | /* Clone the lessor file to create a new file for us */ | |
563 | DRM_DEBUG_LEASE("Allocating lease file\n"); | |
19f391eb | 564 | lessee_file = file_clone_open(lessor_file); |
62884cd3 KP |
565 | if (IS_ERR(lessee_file)) { |
566 | ret = PTR_ERR(lessee_file); | |
567 | goto out_lessee; | |
568 | } | |
569 | ||
62884cd3 | 570 | lessee_priv = lessee_file->private_data; |
62884cd3 KP |
571 | /* Change the file to a master one */ |
572 | drm_master_put(&lessee_priv->master); | |
573 | lessee_priv->master = lessee; | |
574 | lessee_priv->is_master = 1; | |
575 | lessee_priv->authenticated = 1; | |
576 | ||
62884cd3 KP |
577 | /* Pass fd back to userspace */ |
578 | DRM_DEBUG_LEASE("Returning fd %d id %d\n", fd, lessee->lessee_id); | |
579 | cl->fd = fd; | |
580 | cl->lessee_id = lessee->lessee_id; | |
581 | ||
12d43deb JH |
582 | /* Hook up the fd */ |
583 | fd_install(fd, lessee_file); | |
584 | ||
62884cd3 KP |
585 | DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl succeeded\n"); |
586 | return 0; | |
587 | ||
62884cd3 KP |
588 | out_lessee: |
589 | drm_master_put(&lessee); | |
590 | ||
591 | out_leases: | |
592 | put_unused_fd(fd); | |
593 | idr_destroy(&leases); | |
594 | ||
595 | DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl failed: %d\n", ret); | |
596 | return ret; | |
597 | } | |
598 | ||
599 | /** | |
600 | * drm_mode_list_lessees_ioctl - list lessee ids | |
601 | * @dev: the drm device | |
602 | * @data: pointer to struct drm_mode_list_lessees | |
603 | * @lessor_priv: the file being manipulated | |
604 | * | |
605 | * Starting from the master associated with the specified file, | |
606 | * the master with the provided lessee_id is found, and then | |
607 | * an array of lessee ids associated with leases from that master | |
608 | * are returned. | |
609 | */ | |
610 | ||
611 | int drm_mode_list_lessees_ioctl(struct drm_device *dev, | |
612 | void *data, struct drm_file *lessor_priv) | |
613 | { | |
614 | struct drm_mode_list_lessees *arg = data; | |
615 | __u32 __user *lessee_ids = (__u32 __user *) (uintptr_t) (arg->lessees_ptr); | |
616 | __u32 count_lessees = arg->count_lessees; | |
617 | struct drm_master *lessor = lessor_priv->master, *lessee; | |
618 | int count; | |
619 | int ret = 0; | |
620 | ||
621 | if (arg->pad) | |
622 | return -EINVAL; | |
623 | ||
624 | /* Can't lease without MODESET */ | |
625 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 626 | return -EOPNOTSUPP; |
62884cd3 KP |
627 | |
628 | DRM_DEBUG_LEASE("List lessees for %d\n", lessor->lessee_id); | |
629 | ||
630 | mutex_lock(&dev->mode_config.idr_mutex); | |
631 | ||
632 | count = 0; | |
633 | drm_for_each_lessee(lessee, lessor) { | |
634 | /* Only list un-revoked leases */ | |
635 | if (!idr_is_empty(&lessee->leases)) { | |
636 | if (count_lessees > count) { | |
637 | DRM_DEBUG_LEASE("Add lessee %d\n", lessee->lessee_id); | |
638 | ret = put_user(lessee->lessee_id, lessee_ids + count); | |
639 | if (ret) | |
640 | break; | |
641 | } | |
642 | count++; | |
643 | } | |
644 | } | |
645 | ||
646 | DRM_DEBUG_LEASE("Lessor leases to %d\n", count); | |
647 | if (ret == 0) | |
648 | arg->count_lessees = count; | |
649 | ||
650 | mutex_unlock(&dev->mode_config.idr_mutex); | |
651 | ||
652 | return ret; | |
653 | } | |
654 | ||
655 | /** | |
656 | * drm_mode_get_lease_ioctl - list leased objects | |
657 | * @dev: the drm device | |
658 | * @data: pointer to struct drm_mode_get_lease | |
7591844c | 659 | * @lessee_priv: the file being manipulated |
62884cd3 KP |
660 | * |
661 | * Return the list of leased objects for the specified lessee | |
662 | */ | |
663 | ||
664 | int drm_mode_get_lease_ioctl(struct drm_device *dev, | |
665 | void *data, struct drm_file *lessee_priv) | |
666 | { | |
667 | struct drm_mode_get_lease *arg = data; | |
668 | __u32 __user *object_ids = (__u32 __user *) (uintptr_t) (arg->objects_ptr); | |
669 | __u32 count_objects = arg->count_objects; | |
670 | struct drm_master *lessee = lessee_priv->master; | |
671 | struct idr *object_idr; | |
672 | int count; | |
673 | void *entry; | |
674 | int object; | |
675 | int ret = 0; | |
676 | ||
677 | if (arg->pad) | |
678 | return -EINVAL; | |
679 | ||
680 | /* Can't lease without MODESET */ | |
681 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 682 | return -EOPNOTSUPP; |
62884cd3 KP |
683 | |
684 | DRM_DEBUG_LEASE("get lease for %d\n", lessee->lessee_id); | |
685 | ||
686 | mutex_lock(&dev->mode_config.idr_mutex); | |
687 | ||
688 | if (lessee->lessor == NULL) | |
689 | /* owner can use all objects */ | |
690 | object_idr = &lessee->dev->mode_config.crtc_idr; | |
691 | else | |
692 | /* lessee can only use allowed object */ | |
693 | object_idr = &lessee->leases; | |
694 | ||
695 | count = 0; | |
696 | idr_for_each_entry(object_idr, entry, object) { | |
697 | if (count_objects > count) { | |
698 | DRM_DEBUG_LEASE("adding object %d\n", object); | |
699 | ret = put_user(object, object_ids + count); | |
700 | if (ret) | |
701 | break; | |
702 | } | |
703 | count++; | |
704 | } | |
705 | ||
706 | DRM_DEBUG("lease holds %d objects\n", count); | |
707 | if (ret == 0) | |
708 | arg->count_objects = count; | |
709 | ||
710 | mutex_unlock(&dev->mode_config.idr_mutex); | |
711 | ||
712 | return ret; | |
713 | } | |
714 | ||
715 | /** | |
716 | * drm_mode_revoke_lease_ioctl - revoke lease | |
717 | * @dev: the drm device | |
718 | * @data: pointer to struct drm_mode_revoke_lease | |
7591844c | 719 | * @lessor_priv: the file being manipulated |
62884cd3 KP |
720 | * |
721 | * This removes all of the objects from the lease without | |
722 | * actually getting rid of the lease itself; that way all | |
723 | * references to it still work correctly | |
724 | */ | |
725 | int drm_mode_revoke_lease_ioctl(struct drm_device *dev, | |
726 | void *data, struct drm_file *lessor_priv) | |
727 | { | |
728 | struct drm_mode_revoke_lease *arg = data; | |
729 | struct drm_master *lessor = lessor_priv->master; | |
730 | struct drm_master *lessee; | |
731 | int ret = 0; | |
732 | ||
733 | DRM_DEBUG_LEASE("revoke lease for %d\n", arg->lessee_id); | |
734 | ||
735 | /* Can't lease without MODESET */ | |
736 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | |
69fdf420 | 737 | return -EOPNOTSUPP; |
62884cd3 KP |
738 | |
739 | mutex_lock(&dev->mode_config.idr_mutex); | |
740 | ||
741 | lessee = _drm_find_lessee(lessor, arg->lessee_id); | |
742 | ||
743 | /* No such lessee */ | |
744 | if (!lessee) { | |
745 | ret = -ENOENT; | |
746 | goto fail; | |
747 | } | |
748 | ||
749 | /* Lease is not held by lessor */ | |
750 | if (lessee->lessor != lessor) { | |
751 | ret = -EACCES; | |
752 | goto fail; | |
753 | } | |
754 | ||
755 | _drm_lease_revoke(lessee); | |
756 | ||
757 | fail: | |
758 | mutex_unlock(&dev->mode_config.idr_mutex); | |
759 | ||
760 | return ret; | |
761 | } |