1*2ed077e4SKeith Packard /* 2*2ed077e4SKeith Packard * Copyright © 2017 Keith Packard <keithp@keithp.com> 3*2ed077e4SKeith Packard * 4*2ed077e4SKeith Packard * This program is free software; you can redistribute it and/or modify 5*2ed077e4SKeith Packard * it under the terms of the GNU General Public License as published by 6*2ed077e4SKeith Packard * the Free Software Foundation, either version 2 of the License, or 7*2ed077e4SKeith Packard * (at your option) any later version. 8*2ed077e4SKeith Packard * 9*2ed077e4SKeith Packard * This program is distributed in the hope that it will be useful, but 10*2ed077e4SKeith Packard * WITHOUT ANY WARRANTY; without even the implied warranty of 11*2ed077e4SKeith Packard * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12*2ed077e4SKeith Packard * General Public License for more details. 13*2ed077e4SKeith Packard */ 14*2ed077e4SKeith Packard 15*2ed077e4SKeith Packard #include <drm/drmP.h> 16*2ed077e4SKeith Packard #include "drm_internal.h" 17*2ed077e4SKeith Packard #include "drm_legacy.h" 18*2ed077e4SKeith Packard #include "drm_crtc_internal.h" 19*2ed077e4SKeith Packard #include <drm/drm_lease.h> 20*2ed077e4SKeith Packard #include <drm/drm_auth.h> 21*2ed077e4SKeith Packard #include <drm/drm_crtc_helper.h> 22*2ed077e4SKeith Packard 23*2ed077e4SKeith Packard #define drm_for_each_lessee(lessee, lessor) \ 24*2ed077e4SKeith Packard list_for_each_entry((lessee), &(lessor)->lessees, lessee_list) 25*2ed077e4SKeith Packard 26*2ed077e4SKeith Packard /** 27*2ed077e4SKeith Packard * drm_lease_owner - return ancestor owner drm_master 28*2ed077e4SKeith Packard * @master: drm_master somewhere within tree of lessees and lessors 29*2ed077e4SKeith Packard * 30*2ed077e4SKeith Packard * RETURN: 31*2ed077e4SKeith Packard * 32*2ed077e4SKeith Packard * drm_master at the top of the tree (i.e, with lessor NULL 33*2ed077e4SKeith Packard */ 34*2ed077e4SKeith Packard struct drm_master *drm_lease_owner(struct drm_master *master) 35*2ed077e4SKeith Packard { 36*2ed077e4SKeith Packard while (master->lessor != NULL) 37*2ed077e4SKeith Packard master = master->lessor; 38*2ed077e4SKeith Packard return master; 39*2ed077e4SKeith Packard } 40*2ed077e4SKeith Packard EXPORT_SYMBOL(drm_lease_owner); 41*2ed077e4SKeith Packard 42*2ed077e4SKeith Packard /** 43*2ed077e4SKeith Packard * _drm_find_lessee - find lessee by id (idr_mutex held) 44*2ed077e4SKeith Packard * @master: drm_master of lessor 45*2ed077e4SKeith Packard * @id: lessee_id 46*2ed077e4SKeith Packard * 47*2ed077e4SKeith Packard * RETURN: 48*2ed077e4SKeith Packard * 49*2ed077e4SKeith Packard * drm_master of the lessee if valid, NULL otherwise 50*2ed077e4SKeith Packard */ 51*2ed077e4SKeith Packard 52*2ed077e4SKeith Packard static struct drm_master* 53*2ed077e4SKeith Packard _drm_find_lessee(struct drm_master *master, int lessee_id) 54*2ed077e4SKeith Packard { 55*2ed077e4SKeith Packard lockdep_assert_held(&master->dev->mode_config.idr_mutex); 56*2ed077e4SKeith Packard return idr_find(&drm_lease_owner(master)->lessee_idr, lessee_id); 57*2ed077e4SKeith Packard } 58*2ed077e4SKeith Packard 59*2ed077e4SKeith Packard /** 60*2ed077e4SKeith Packard * _drm_lease_held_master - check to see if an object is leased (or owned) by master (idr_mutex held) 61*2ed077e4SKeith Packard * @master: the master to check the lease status of 62*2ed077e4SKeith Packard * @id: the id to check 63*2ed077e4SKeith Packard * 64*2ed077e4SKeith Packard * Checks if the specified master holds a lease on the object. Return 65*2ed077e4SKeith Packard * value: 66*2ed077e4SKeith Packard * 67*2ed077e4SKeith Packard * true 'master' holds a lease on (or owns) the object 68*2ed077e4SKeith Packard * false 'master' does not hold a lease. 69*2ed077e4SKeith Packard */ 70*2ed077e4SKeith Packard static int _drm_lease_held_master(struct drm_master *master, int id) 71*2ed077e4SKeith Packard { 72*2ed077e4SKeith Packard lockdep_assert_held(&master->dev->mode_config.idr_mutex); 73*2ed077e4SKeith Packard if (master->lessor) 74*2ed077e4SKeith Packard return idr_find(&master->leases, id) != NULL; 75*2ed077e4SKeith Packard return true; 76*2ed077e4SKeith Packard } 77*2ed077e4SKeith Packard 78*2ed077e4SKeith Packard /** 79*2ed077e4SKeith Packard * _drm_has_leased - check to see if an object has been leased (idr_mutex held) 80*2ed077e4SKeith Packard * @master: the master to check the lease status of 81*2ed077e4SKeith Packard * @id: the id to check 82*2ed077e4SKeith Packard * 83*2ed077e4SKeith Packard * Checks if any lessee of 'master' holds a lease on 'id'. Return 84*2ed077e4SKeith Packard * value: 85*2ed077e4SKeith Packard * 86*2ed077e4SKeith Packard * true Some lessee holds a lease on the object. 87*2ed077e4SKeith Packard * false No lessee has a lease on the object. 88*2ed077e4SKeith Packard */ 89*2ed077e4SKeith Packard static bool _drm_has_leased(struct drm_master *master, int id) 90*2ed077e4SKeith Packard { 91*2ed077e4SKeith Packard struct drm_master *lessee; 92*2ed077e4SKeith Packard 93*2ed077e4SKeith Packard lockdep_assert_held(&master->dev->mode_config.idr_mutex); 94*2ed077e4SKeith Packard drm_for_each_lessee(lessee, master) 95*2ed077e4SKeith Packard if (_drm_lease_held_master(lessee, id)) 96*2ed077e4SKeith Packard return true; 97*2ed077e4SKeith Packard return false; 98*2ed077e4SKeith Packard } 99*2ed077e4SKeith Packard 100*2ed077e4SKeith Packard /** 101*2ed077e4SKeith Packard * _drm_lease_held - check drm_mode_object lease status (idr_mutex held) 102*2ed077e4SKeith Packard * @master: the drm_master 103*2ed077e4SKeith Packard * @id: the object id 104*2ed077e4SKeith Packard * 105*2ed077e4SKeith Packard * Checks if the specified master holds a lease on the object. Return 106*2ed077e4SKeith Packard * value: 107*2ed077e4SKeith Packard * 108*2ed077e4SKeith Packard * true 'master' holds a lease on (or owns) the object 109*2ed077e4SKeith Packard * false 'master' does not hold a lease. 110*2ed077e4SKeith Packard */ 111*2ed077e4SKeith Packard bool _drm_lease_held(struct drm_file *file_priv, int id) 112*2ed077e4SKeith Packard { 113*2ed077e4SKeith Packard if (file_priv == NULL || file_priv->master == NULL) 114*2ed077e4SKeith Packard return true; 115*2ed077e4SKeith Packard 116*2ed077e4SKeith Packard return _drm_lease_held_master(file_priv->master, id); 117*2ed077e4SKeith Packard } 118*2ed077e4SKeith Packard EXPORT_SYMBOL(_drm_lease_held); 119*2ed077e4SKeith Packard 120*2ed077e4SKeith Packard /** 121*2ed077e4SKeith Packard * drm_lease_held - check drm_mode_object lease status (idr_mutex not held) 122*2ed077e4SKeith Packard * @master: the drm_master 123*2ed077e4SKeith Packard * @id: the object id 124*2ed077e4SKeith Packard * 125*2ed077e4SKeith Packard * Checks if the specified master holds a lease on the object. Return 126*2ed077e4SKeith Packard * value: 127*2ed077e4SKeith Packard * 128*2ed077e4SKeith Packard * true 'master' holds a lease on (or owns) the object 129*2ed077e4SKeith Packard * false 'master' does not hold a lease. 130*2ed077e4SKeith Packard */ 131*2ed077e4SKeith Packard bool drm_lease_held(struct drm_file *file_priv, int id) 132*2ed077e4SKeith Packard { 133*2ed077e4SKeith Packard struct drm_master *master; 134*2ed077e4SKeith Packard bool ret; 135*2ed077e4SKeith Packard 136*2ed077e4SKeith Packard if (file_priv == NULL || file_priv->master == NULL) 137*2ed077e4SKeith Packard return true; 138*2ed077e4SKeith Packard 139*2ed077e4SKeith Packard master = file_priv->master; 140*2ed077e4SKeith Packard mutex_lock(&master->dev->mode_config.idr_mutex); 141*2ed077e4SKeith Packard ret = _drm_lease_held_master(master, id); 142*2ed077e4SKeith Packard mutex_unlock(&master->dev->mode_config.idr_mutex); 143*2ed077e4SKeith Packard return ret; 144*2ed077e4SKeith Packard } 145*2ed077e4SKeith Packard EXPORT_SYMBOL(drm_lease_held); 146*2ed077e4SKeith Packard 147*2ed077e4SKeith Packard /** 148*2ed077e4SKeith Packard * drm_lease_filter_crtcs - restricted crtc set to leased values (idr_mutex not held) 149*2ed077e4SKeith Packard * @file_priv: requestor file 150*2ed077e4SKeith Packard * @crtcs: bitmask of crtcs to check 151*2ed077e4SKeith Packard * 152*2ed077e4SKeith Packard * Reconstructs a crtc mask based on the crtcs which are visible 153*2ed077e4SKeith Packard * through the specified file. 154*2ed077e4SKeith Packard */ 155*2ed077e4SKeith Packard uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs_in) 156*2ed077e4SKeith Packard { 157*2ed077e4SKeith Packard struct drm_master *master; 158*2ed077e4SKeith Packard struct drm_device *dev; 159*2ed077e4SKeith Packard struct drm_crtc *crtc; 160*2ed077e4SKeith Packard int count_in, count_out; 161*2ed077e4SKeith Packard uint32_t crtcs_out = 0; 162*2ed077e4SKeith Packard 163*2ed077e4SKeith Packard if (file_priv == NULL || file_priv->master == NULL) 164*2ed077e4SKeith Packard return crtcs_in; 165*2ed077e4SKeith Packard 166*2ed077e4SKeith Packard master = file_priv->master; 167*2ed077e4SKeith Packard dev = master->dev; 168*2ed077e4SKeith Packard 169*2ed077e4SKeith Packard count_in = count_out = 0; 170*2ed077e4SKeith Packard mutex_lock(&master->dev->mode_config.idr_mutex); 171*2ed077e4SKeith Packard list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 172*2ed077e4SKeith Packard if (_drm_lease_held_master(master, crtc->base.id)) { 173*2ed077e4SKeith Packard uint32_t mask_in = 1ul << count_in; 174*2ed077e4SKeith Packard if ((crtcs_in & mask_in) != 0) { 175*2ed077e4SKeith Packard uint32_t mask_out = 1ul << count_out; 176*2ed077e4SKeith Packard crtcs_out |= mask_out; 177*2ed077e4SKeith Packard } 178*2ed077e4SKeith Packard count_out++; 179*2ed077e4SKeith Packard } 180*2ed077e4SKeith Packard count_in++; 181*2ed077e4SKeith Packard } 182*2ed077e4SKeith Packard mutex_unlock(&master->dev->mode_config.idr_mutex); 183*2ed077e4SKeith Packard return crtcs_out; 184*2ed077e4SKeith Packard } 185*2ed077e4SKeith Packard EXPORT_SYMBOL(drm_lease_filter_crtcs); 186*2ed077e4SKeith Packard 187*2ed077e4SKeith Packard /* 188*2ed077e4SKeith Packard * drm_lease_create - create a new drm_master with leased objects (idr_mutex not held) 189*2ed077e4SKeith Packard * @lessor: lease holder (or owner) of objects 190*2ed077e4SKeith Packard * @leases: objects to lease to the new drm_master 191*2ed077e4SKeith Packard * 192*2ed077e4SKeith Packard * Uses drm_master_create to allocate a new drm_master, then checks to 193*2ed077e4SKeith Packard * make sure all of the desired objects can be leased, atomically 194*2ed077e4SKeith Packard * leasing them to the new drmmaster. 195*2ed077e4SKeith Packard * 196*2ed077e4SKeith Packard * ERR_PTR(-EACCESS) some other master holds the title to any object 197*2ed077e4SKeith Packard * ERR_PTR(-ENOENT) some object is not a valid DRM object for this device 198*2ed077e4SKeith Packard * ERR_PTR(-EBUSY) some other lessee holds title to this object 199*2ed077e4SKeith Packard * ERR_PTR(-EEXIST) same object specified more than once in the provided list 200*2ed077e4SKeith Packard * ERR_PTR(-ENOMEM) allocation failed 201*2ed077e4SKeith Packard */ 202*2ed077e4SKeith Packard static struct drm_master *drm_lease_create(struct drm_master *lessor, struct idr *leases) 203*2ed077e4SKeith Packard { 204*2ed077e4SKeith Packard struct drm_device *dev = lessor->dev; 205*2ed077e4SKeith Packard int error; 206*2ed077e4SKeith Packard struct drm_master *lessee; 207*2ed077e4SKeith Packard int object; 208*2ed077e4SKeith Packard int id; 209*2ed077e4SKeith Packard void *entry; 210*2ed077e4SKeith Packard 211*2ed077e4SKeith Packard DRM_DEBUG_LEASE("lessor %d\n", lessor->lessee_id); 212*2ed077e4SKeith Packard 213*2ed077e4SKeith Packard lessee = drm_master_create(lessor->dev); 214*2ed077e4SKeith Packard if (!lessee) { 215*2ed077e4SKeith Packard DRM_DEBUG_LEASE("drm_master_create failed\n"); 216*2ed077e4SKeith Packard return ERR_PTR(-ENOMEM); 217*2ed077e4SKeith Packard } 218*2ed077e4SKeith Packard 219*2ed077e4SKeith Packard mutex_lock(&dev->mode_config.idr_mutex); 220*2ed077e4SKeith Packard 221*2ed077e4SKeith Packard /* Insert the new lessee into the tree */ 222*2ed077e4SKeith Packard id = idr_alloc(&(drm_lease_owner(lessor)->lessee_idr), lessee, 1, 0, GFP_KERNEL); 223*2ed077e4SKeith Packard if (id < 0) { 224*2ed077e4SKeith Packard error = id; 225*2ed077e4SKeith Packard goto out_lessee; 226*2ed077e4SKeith Packard } 227*2ed077e4SKeith Packard 228*2ed077e4SKeith Packard lessee->lessee_id = id; 229*2ed077e4SKeith Packard lessee->lessor = drm_master_get(lessor); 230*2ed077e4SKeith Packard list_add_tail(&lessee->lessee_list, &lessor->lessees); 231*2ed077e4SKeith Packard 232*2ed077e4SKeith Packard idr_for_each_entry(leases, entry, object) { 233*2ed077e4SKeith Packard error = 0; 234*2ed077e4SKeith Packard if (!idr_find(&dev->mode_config.crtc_idr, object)) 235*2ed077e4SKeith Packard error = -ENOENT; 236*2ed077e4SKeith Packard else if (!_drm_lease_held_master(lessor, object)) 237*2ed077e4SKeith Packard error = -EACCES; 238*2ed077e4SKeith Packard else if (_drm_has_leased(lessor, object)) 239*2ed077e4SKeith Packard error = -EBUSY; 240*2ed077e4SKeith Packard 241*2ed077e4SKeith Packard if (error != 0) { 242*2ed077e4SKeith Packard DRM_DEBUG_LEASE("object %d failed %d\n", object, error); 243*2ed077e4SKeith Packard goto out_lessee; 244*2ed077e4SKeith Packard } 245*2ed077e4SKeith Packard } 246*2ed077e4SKeith Packard 247*2ed077e4SKeith Packard /* Move the leases over */ 248*2ed077e4SKeith Packard lessee->leases = *leases; 249*2ed077e4SKeith Packard DRM_DEBUG_LEASE("new lessee %d %p, lessor %d %p\n", lessee->lessee_id, lessee, lessor->lessee_id, lessor); 250*2ed077e4SKeith Packard 251*2ed077e4SKeith Packard mutex_unlock(&dev->mode_config.idr_mutex); 252*2ed077e4SKeith Packard return lessee; 253*2ed077e4SKeith Packard 254*2ed077e4SKeith Packard out_lessee: 255*2ed077e4SKeith Packard drm_master_put(&lessee); 256*2ed077e4SKeith Packard 257*2ed077e4SKeith Packard mutex_unlock(&dev->mode_config.idr_mutex); 258*2ed077e4SKeith Packard 259*2ed077e4SKeith Packard return ERR_PTR(error); 260*2ed077e4SKeith Packard } 261*2ed077e4SKeith Packard 262*2ed077e4SKeith Packard /** 263*2ed077e4SKeith Packard * drm_lease_destroy - a master is going away (idr_mutex not held) 264*2ed077e4SKeith Packard * @master: the drm_master being destroyed 265*2ed077e4SKeith Packard * 266*2ed077e4SKeith Packard * All lessees will have been destroyed as they 267*2ed077e4SKeith Packard * hold a reference on their lessor. Notify any 268*2ed077e4SKeith Packard * lessor for this master so that it can check 269*2ed077e4SKeith Packard * the list of lessees. 270*2ed077e4SKeith Packard */ 271*2ed077e4SKeith Packard void drm_lease_destroy(struct drm_master *master) 272*2ed077e4SKeith Packard { 273*2ed077e4SKeith Packard struct drm_device *dev = master->dev; 274*2ed077e4SKeith Packard 275*2ed077e4SKeith Packard mutex_lock(&dev->mode_config.idr_mutex); 276*2ed077e4SKeith Packard 277*2ed077e4SKeith Packard DRM_DEBUG_LEASE("drm_lease_destroy %d\n", master->lessee_id); 278*2ed077e4SKeith Packard 279*2ed077e4SKeith Packard /* This master is referenced by all lessees, hence it cannot be destroyed 280*2ed077e4SKeith Packard * until all of them have been 281*2ed077e4SKeith Packard */ 282*2ed077e4SKeith Packard WARN_ON(!list_empty(&master->lessees)); 283*2ed077e4SKeith Packard 284*2ed077e4SKeith Packard /* Remove this master from the lessee idr in the owner */ 285*2ed077e4SKeith Packard if (master->lessee_id != 0) { 286*2ed077e4SKeith Packard DRM_DEBUG_LEASE("remove master %d from device list of lessees\n", master->lessee_id); 287*2ed077e4SKeith Packard idr_remove(&(drm_lease_owner(master)->lessee_idr), master->lessee_id); 288*2ed077e4SKeith Packard } 289*2ed077e4SKeith Packard 290*2ed077e4SKeith Packard /* Remove this master from any lessee list it may be on */ 291*2ed077e4SKeith Packard list_del(&master->lessee_list); 292*2ed077e4SKeith Packard 293*2ed077e4SKeith Packard mutex_unlock(&dev->mode_config.idr_mutex); 294*2ed077e4SKeith Packard 295*2ed077e4SKeith Packard if (master->lessor) { 296*2ed077e4SKeith Packard /* Tell the master to check the lessee list */ 297*2ed077e4SKeith Packard drm_sysfs_hotplug_event(dev); 298*2ed077e4SKeith Packard drm_master_put(&master->lessor); 299*2ed077e4SKeith Packard } 300*2ed077e4SKeith Packard 301*2ed077e4SKeith Packard DRM_DEBUG_LEASE("drm_lease_destroy done %d\n", master->lessee_id); 302*2ed077e4SKeith Packard } 303*2ed077e4SKeith Packard 304*2ed077e4SKeith Packard /** 305*2ed077e4SKeith Packard * _drm_lease_revoke - revoke access to all leased objects (idr_mutex held) 306*2ed077e4SKeith Packard * @master: the master losing its lease 307*2ed077e4SKeith Packard */ 308*2ed077e4SKeith Packard static void _drm_lease_revoke(struct drm_master *top) 309*2ed077e4SKeith Packard { 310*2ed077e4SKeith Packard int object; 311*2ed077e4SKeith Packard void *entry; 312*2ed077e4SKeith Packard struct drm_master *master = top; 313*2ed077e4SKeith Packard 314*2ed077e4SKeith Packard lockdep_assert_held(&top->dev->mode_config.idr_mutex); 315*2ed077e4SKeith Packard 316*2ed077e4SKeith Packard /* 317*2ed077e4SKeith Packard * Walk the tree starting at 'top' emptying all leases. Because 318*2ed077e4SKeith Packard * the tree is fully connected, we can do this without recursing 319*2ed077e4SKeith Packard */ 320*2ed077e4SKeith Packard for (;;) { 321*2ed077e4SKeith Packard DRM_DEBUG_LEASE("revoke leases for %p %d\n", master, master->lessee_id); 322*2ed077e4SKeith Packard 323*2ed077e4SKeith Packard /* Evacuate the lease */ 324*2ed077e4SKeith Packard idr_for_each_entry(&master->leases, entry, object) 325*2ed077e4SKeith Packard idr_remove(&master->leases, object); 326*2ed077e4SKeith Packard 327*2ed077e4SKeith Packard /* Depth-first list walk */ 328*2ed077e4SKeith Packard 329*2ed077e4SKeith Packard /* Down */ 330*2ed077e4SKeith Packard if (!list_empty(&master->lessees)) { 331*2ed077e4SKeith Packard master = list_first_entry(&master->lessees, struct drm_master, lessee_list); 332*2ed077e4SKeith Packard } else { 333*2ed077e4SKeith Packard /* Up */ 334*2ed077e4SKeith Packard while (master != top && master == list_last_entry(&master->lessor->lessees, struct drm_master, lessee_list)) 335*2ed077e4SKeith Packard master = master->lessor; 336*2ed077e4SKeith Packard 337*2ed077e4SKeith Packard if (master == top) 338*2ed077e4SKeith Packard break; 339*2ed077e4SKeith Packard 340*2ed077e4SKeith Packard /* Over */ 341*2ed077e4SKeith Packard master = list_entry(master->lessee_list.next, struct drm_master, lessee_list); 342*2ed077e4SKeith Packard } 343*2ed077e4SKeith Packard } 344*2ed077e4SKeith Packard } 345*2ed077e4SKeith Packard 346*2ed077e4SKeith Packard /** 347*2ed077e4SKeith Packard * drm_lease_revoke - revoke access to all leased objects (idr_mutex not held) 348*2ed077e4SKeith Packard * @top: the master losing its lease 349*2ed077e4SKeith Packard */ 350*2ed077e4SKeith Packard void drm_lease_revoke(struct drm_master *top) 351*2ed077e4SKeith Packard { 352*2ed077e4SKeith Packard mutex_lock(&top->dev->mode_config.idr_mutex); 353*2ed077e4SKeith Packard _drm_lease_revoke(top); 354*2ed077e4SKeith Packard mutex_unlock(&top->dev->mode_config.idr_mutex); 355*2ed077e4SKeith Packard } 356