summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2016-12-07 07:06:40 +0000
committerDave Airlie <airlied@redhat.com>2016-12-07 07:06:40 +0000
commitf8eeba2dc5a026c77d23d069cbcaad92619b2234 (patch)
tree22ee138dd4ca2dfe428fe8d90ff7ebe58285d344
parent197aa6ed522cc44710687d3b02dd4e4573991416 (diff)
drm: add exclusive resource handling interfaces.drm-exclusive-objects
The idea here is for VR compositors. There is an opt in interface for modesetting masters (X, compositors) that they can state they will respect the allowed resources list, and will update it on hotplug events. This list contains crtcs, planes and connectors the modesetting master will interact with. Any modesetting operations on these objects will be ignored by the kernel. The list will change on a hotplug event, and needs to be re-read in full. Then there is another root-only ioctl. The is to be used by the VR process compositor (possbly via a suid-root helper or logind). This ioctl takes a list of objects for exclusive mode and tags them as exclusive for that fd, it also disconnects the fd from the current master and makes it into a disconnected master of it's own. TODO: DRM_MASTER still blocks this from working, as we want a non-current master to use modesetting ioctls.
-rw-r--r--drivers/gpu/drm/drm_auth.c26
-rw-r--r--drivers/gpu/drm/drm_crtc.c14
-rw-r--r--drivers/gpu/drm/drm_crtc_internal.h8
-rw-r--r--drivers/gpu/drm/drm_fops.c1
-rw-r--r--drivers/gpu/drm/drm_internal.h2
-rw-r--r--drivers/gpu/drm/drm_ioctl.c11
-rw-r--r--drivers/gpu/drm/drm_mode_config.c180
-rw-r--r--drivers/gpu/drm/drm_mode_object.c33
-rw-r--r--include/drm/drmP.h7
-rw-r--r--include/drm/drm_auth.h1
-rw-r--r--include/drm/drm_mode_object.h7
-rw-r--r--include/uapi/drm/drm.h9
-rw-r--r--include/uapi/drm/drm_mode.h15
13 files changed, 302 insertions, 12 deletions
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index 6b143514a566..5bdc5d315d5b 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -93,7 +93,7 @@ int drm_authmagic(struct drm_device *dev, void *data,
return file ? 0 : -EINVAL;
}
-static struct drm_master *drm_master_create(struct drm_device *dev)
+static struct drm_master *drm_master_create(struct drm_device *dev, bool allowed_resources)
{
struct drm_master *master;
@@ -106,7 +106,7 @@ static struct drm_master *drm_master_create(struct drm_device *dev)
init_waitqueue_head(&master->lock.lock_queue);
idr_init(&master->magic_map);
master->dev = dev;
-
+ master->allowed_resources = allowed_resources;
return master;
}
@@ -126,7 +126,8 @@ static int drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
return ret;
}
-static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
+int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv,
+ bool set_device)
{
struct drm_master *old_master;
int ret;
@@ -134,7 +135,7 @@ static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
lockdep_assert_held_once(&dev->master_mutex);
old_master = fpriv->master;
- fpriv->master = drm_master_create(dev);
+ fpriv->master = drm_master_create(dev, fpriv->allowed_resources);
if (!fpriv->master) {
fpriv->master = old_master;
return -ENOMEM;
@@ -148,9 +149,11 @@ static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
fpriv->is_master = 1;
fpriv->authenticated = 1;
- ret = drm_set_master(dev, fpriv, true);
- if (ret)
- goto out_err;
+ if (set_device) {
+ ret = drm_set_master(dev, fpriv, true);
+ if (ret)
+ goto out_err;
+ }
if (old_master)
drm_master_put(&old_master);
@@ -185,7 +188,7 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
}
if (!file_priv->is_master) {
- ret = drm_new_set_master(dev, file_priv);
+ ret = drm_new_set_master(dev, file_priv, true);
goto out_unlock;
}
@@ -231,7 +234,7 @@ int drm_master_open(struct drm_file *file_priv)
* any master object for render clients */
mutex_lock(&dev->master_mutex);
if (!dev->master)
- ret = drm_new_set_master(dev, file_priv);
+ ret = drm_new_set_master(dev, file_priv, true);
else
file_priv->master = drm_master_get(dev->master);
mutex_unlock(&dev->master_mutex);
@@ -292,6 +295,11 @@ bool drm_is_current_master(struct drm_file *fpriv)
}
EXPORT_SYMBOL(drm_is_current_master);
+bool drm_master_respects_allowed_resources(struct drm_master *master)
+{
+ return master->allowed_resources;
+}
+
/**
* drm_master_get - reference a master pointer
* @master: struct &drm_master
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 90931e039731..ad2ab06ef46d 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -511,8 +511,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
ret = -ENOENT;
goto out;
}
- DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
+ if (!drm_mode_object_allowed(dev, &crtc->base, file_priv)) {
+ ret = 0;
+ DRM_DEBUG_KMS("[CRTC:%d:%s] on ignore list\n", crtc->base.id, crtc->name);
+ goto out;
+ }
+ DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
if (crtc_req->mode_valid) {
/* If we have a mode we need a framebuffer. */
/* If we pass -1, set the mode with the currently bound fb */
@@ -618,6 +623,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
ret = -ENOENT;
goto out;
}
+
+ if (!drm_mode_object_allowed(dev, &connector->base, file_priv)) {
+ ret = 0;
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s] on ignore list\n", connector->base.id,
+ connector->name);
+ goto out;
+ }
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id,
connector->name);
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 33b17d0b127e..c43249e385fb 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -55,11 +55,15 @@ int drm_mode_setcrtc(struct drm_device *dev,
/* drm_mode_config.c */
int drm_modeset_register_all(struct drm_device *dev);
void drm_modeset_unregister_all(struct drm_device *dev);
-
+void drm_mode_clear_exclusives(struct drm_device *dev,
+ struct drm_file *file_priv, bool hotplug);
/* IOCTLs */
int drm_mode_getresources(struct drm_device *dev,
void *data, struct drm_file *file_priv);
-
+int drm_mode_getallowedres_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_mode_exclusive_mode_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
/* drm_dumb_buffers.c */
/* IOCTLs */
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 5d96de40b63f..070e7e3df8ae 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -396,6 +396,7 @@ int drm_release(struct inode *inode, struct file *filp)
drm_events_release(file_priv);
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ drm_mode_clear_exclusives(dev, file_priv, true);
drm_fb_release(file_priv);
drm_property_destroy_user_blobs(dev, file_priv);
}
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index db80ec860e33..a604a237afbb 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -74,6 +74,8 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_master_open(struct drm_file *file_priv);
void drm_master_release(struct drm_file *file_priv);
+int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv, bool set_device);
+bool drm_master_respects_allowed_resources(struct drm_master *master);
/* drm_sysfs.c */
extern struct class *drm_class;
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 71c3473476c7..6ca0f13229b7 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -277,6 +277,9 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_
case DRM_CAP_ADDFB2_MODIFIERS:
req->value = dev->mode_config.allow_fb_modifiers;
break;
+ case DRM_CAP_ALLOWED_RESOURCES:
+ req->value = 1;
+ break;
default:
return -EINVAL;
}
@@ -310,6 +313,11 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
file_priv->atomic = req->value;
file_priv->universal_planes = req->value;
break;
+ case DRM_CLIENT_CAP_ALLOWED_RESOURCES:
+ if (req->value > 1)
+ return -EINVAL;
+ file_priv->allowed_resources = req->value;
+ break;
default:
return -EINVAL;
}
@@ -629,6 +637,9 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATEPROPBLOB, drm_mode_createblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROYPROPBLOB, drm_mode_destroyblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETALLOWEDRES, drm_mode_getallowedres_ioctl, DRM_MASTER|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_EXCLUSIVE_MODE, drm_mode_exclusive_mode_ioctl, DRM_ROOT_ONLY),
};
#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c
index 2735a5847ffa..0c90576ecbc7 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -194,6 +194,186 @@ out:
return ret;
}
+int drm_mode_getallowedres_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_allowed_res *allowed_list = data;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
+ struct drm_plane *plane;
+ int connector_count = 0;
+ int crtc_count = 0;
+ int plane_count = 0;
+ int ret = 0;
+ int copied = 0;
+ uint32_t __user *id;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return -EINVAL;
+
+ if (!file_priv->allowed_resources)
+ return -EINVAL;
+
+ /* mode_config.mutex protects the connector list against e.g. DP MST
+ * connector hot-adding. CRTC/Plane lists are invariant. */
+ drm_for_each_plane(plane, dev) {
+ /*
+ * Unless userspace set the 'universal planes'
+ * capability bit, only advertise overlays.
+ */
+ if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
+ !file_priv->universal_planes)
+ continue;
+ if (drm_mode_object_allowed(dev, &plane->base, file_priv))
+ plane_count++;
+ }
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_for_each_crtc(crtc, dev) {
+ if (drm_mode_object_allowed(dev, &crtc->base, file_priv))
+ crtc_count++;
+ }
+
+ drm_for_each_crtc(connector, dev) {
+ if (drm_mode_object_allowed(dev, &connector->base, file_priv))
+ connector_count++;
+ }
+
+ if (allowed_list->count_planes >= plane_count) {
+ copied = 0;
+ id = (uint32_t __user *)(unsigned long)allowed_list->plane_id_ptr;
+ drm_for_each_plane(plane, dev) {
+ /*
+ * Unless userspace set the 'universal planes'
+ * capability bit, only advertise overlays.
+ */
+ if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
+ !file_priv->universal_planes)
+ continue;
+
+ if (put_user(plane->base.id, id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ allowed_list->count_planes = plane_count;
+
+ if (allowed_list->count_crtcs >= crtc_count) {
+ copied = 0;
+ id = (uint32_t __user *)(unsigned long)allowed_list->crtc_id_ptr;
+ drm_for_each_crtc(crtc, dev) {
+ if (drm_mode_object_allowed(dev, &crtc->base, file_priv)) {
+ if (put_user(crtc->base.id, id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ allowed_list->count_crtcs = crtc_count;
+
+ if (allowed_list->count_connectors >= connector_count) {
+ copied = 0;
+ id = (uint32_t __user *)(unsigned long)allowed_list->connector_id_ptr;
+ drm_for_each_connector(connector, dev) {
+ if (drm_mode_object_allowed(dev, &connector->base, file_priv)) {
+ if (put_user(connector->base.id, id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ allowed_list->count_connectors = connector_count;
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_exclusive_mode_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_exclusive_res *list = data;
+ int ret = 0;
+ int i;
+ uint32_t __user *id;
+ bool promote_to_master = true;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return -EINVAL;
+
+ if (!file_priv->allowed_resources)
+ return -EINVAL;
+
+ /* current master must support allowed resources */
+ if (file_priv->master && !drm_master_respects_allowed_resources(file_priv->master))
+ return -EINVAL;
+
+ /* once only operation - nuke all current exclusives - maybe post hotplug */
+ if (file_priv->num_exclusive) {
+ drm_mode_clear_exclusives(dev, file_priv, false);
+ promote_to_master = false;
+ }
+
+ /* return if we have no crtcs or connectors left */
+ if (list->count_ids == 0)
+ return 0;
+
+ if (list->count_ids > DRM_FILE_MAX_EXCLUSIVE)
+ return -EINVAL;
+
+ mutex_lock(&dev->mode_config.mutex);
+ for (i = 0; i < list->count_ids; i++) {
+ id = (uint32_t __user *)(unsigned long)list->id_ptr;
+ if (get_user(file_priv->exclusive_objects[i], id + i)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ }
+ file_priv->num_exclusive = list->count_ids;
+ mutex_unlock(&dev->mode_config.mutex);
+
+ /* should we use this ioctl to elevate this process to master? */
+ if (promote_to_master)
+ ret = drm_new_set_master(dev, file_priv, false);
+
+ /* send hotplug so everyone updates their ignore lists */
+ drm_sysfs_hotplug_event(dev);
+out:
+ return ret;
+}
+
+void drm_mode_clear_exclusives(struct drm_device *dev,
+ struct drm_file *file_priv,
+ bool hotplug)
+{
+ struct drm_mode_object *obj;
+ int i;
+
+ if (!file_priv->num_exclusive)
+ return;
+
+ mutex_lock(&dev->mode_config.mutex);
+ for (i = 0; i < file_priv->num_exclusive; i++) {
+ obj = drm_mode_object_find(dev, file_priv->exclusive_objects[i], DRM_MODE_OBJECT_ANY);
+ if (!obj)
+ continue;
+
+ obj->exclusive_access = false;
+ }
+ file_priv->num_exclusive = 0;
+ mutex_unlock(&dev->mode_config.mutex);
+
+ /* send hotplug so everyone updates their ignore lists */
+ if (hotplug)
+ drm_sysfs_hotplug_event(dev);
+}
+
/**
* drm_mode_config_reset - call ->reset callbacks
* @dev: drm device
diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c
index 9f17085b1fdd..1493403e54da 100644
--- a/drivers/gpu/drm/drm_mode_object.c
+++ b/drivers/gpu/drm/drm_mode_object.c
@@ -182,6 +182,39 @@ void drm_mode_object_reference(struct drm_mode_object *obj)
}
EXPORT_SYMBOL(drm_mode_object_reference);
+/*
+ * return true of object is allowed for this file priv
+ */
+bool drm_mode_object_allowed(struct drm_device *dev,
+ struct drm_mode_object *obj,
+ struct drm_file *file_priv)
+{
+ int i;
+
+ /* if the object is not exclusive and file is
+ * not exclusive, allow it.
+ */
+ if (!obj->exclusive_access && !file_priv->num_exclusive)
+ return true;
+
+ /* if we are exclusive and the object is on our list - allow it */
+ for (i = 0; i < file_priv->num_exclusive; i++) {
+ if (file_priv->exclusive_objects[i] == obj->id)
+ return true;
+ }
+
+ /* if we are exclusive and the object wasn't found - don't allow it */
+ if (file_priv->num_exclusive)
+ return false;
+
+ /* if we aren't exclusive and the object has exclusive access enabled
+ ignore it */
+ if (obj->exclusive_access)
+ return false;
+
+ return true;
+}
+
/**
* drm_object_attach_property - attach a property to a modeset object
* @obj: drm modeset object
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index a9cfd33c7b1a..8ab8ce531f7c 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -375,6 +375,7 @@ struct drm_prime_file_private {
struct rb_root handles;
};
+#define DRM_FILE_MAX_EXCLUSIVE 4
/** File private data */
struct drm_file {
unsigned authenticated :1;
@@ -387,6 +388,8 @@ struct drm_file {
unsigned universal_planes:1;
/* true if client understands atomic properties */
unsigned atomic:1;
+ /* true if this file will respect the allowed resources data */
+ unsigned allowed_resources:1;
/*
* This client is the creator of @master.
* Protected by struct drm_device::master_mutex.
@@ -430,6 +433,10 @@ struct drm_file {
struct mutex event_read_lock;
struct drm_prime_file_private prime;
+
+ /* objects exclusive to this file descriptor - crtcs and connectors - fbs(??) */
+ int num_exclusive;
+ uint32_t exclusive_objects[DRM_FILE_MAX_EXCLUSIVE];
};
/**
diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h
index 610223b0481b..817fa1c15601 100644
--- a/include/drm/drm_auth.h
+++ b/include/drm/drm_auth.h
@@ -50,6 +50,7 @@ struct drm_master {
struct idr magic_map;
struct drm_lock_data lock;
void *driver_priv;
+ bool allowed_resources;
};
struct drm_master *drm_master_get(struct drm_master *master);
diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h
index 43460b21d112..14da237a2187 100644
--- a/include/drm/drm_mode_object.h
+++ b/include/drm/drm_mode_object.h
@@ -53,6 +53,7 @@ struct drm_device;
struct drm_mode_object {
uint32_t id;
uint32_t type;
+ bool exclusive_access;
struct drm_object_properties *properties;
struct kref refcount;
void (*free_cb)(struct kref *kref);
@@ -122,4 +123,10 @@ int drm_object_property_get_value(struct drm_mode_object *obj,
void drm_object_attach_property(struct drm_mode_object *obj,
struct drm_property *property,
uint64_t init_val);
+
+struct drm_file;
+bool drm_mode_object_allowed(struct drm_device *dev,
+ struct drm_mode_object *obj,
+ struct drm_file *file_priv);
+
#endif
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index b2c52843bc70..6b20c23e4222 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -647,6 +647,7 @@ struct drm_gem_open {
#define DRM_CAP_CURSOR_HEIGHT 0x9
#define DRM_CAP_ADDFB2_MODIFIERS 0x10
#define DRM_CAP_PAGE_FLIP_TARGET 0x11
+#define DRM_CAP_ALLOWED_RESOURCES 0x12
/** DRM_IOCTL_GET_CAP ioctl argument type */
struct drm_get_cap {
@@ -678,6 +679,11 @@ struct drm_get_cap {
*/
#define DRM_CLIENT_CAP_ATOMIC 3
+/**
+ * DRM_CLIENT_CAP_ALLOWED_RESOURCES
+ */
+#define DRM_CLIENT_CAP_ALLOWED_RESOURCES 4
+
/** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
struct drm_set_client_cap {
__u64 capability;
@@ -814,6 +820,9 @@ extern "C" {
#define DRM_IOCTL_MODE_CREATEPROPBLOB DRM_IOWR(0xBD, struct drm_mode_create_blob)
#define DRM_IOCTL_MODE_DESTROYPROPBLOB DRM_IOWR(0xBE, struct drm_mode_destroy_blob)
+#define DRM_IOCTL_MODE_GETALLOWEDRES DRM_IOWR(0xBF, struct drm_mode_allowed_res)
+#define DRM_IOCTL_MODE_EXCLUSIVE_MODE DRM_IOWR(0xC0, struct drm_mode_exclusive_res)
+
/**
* Device specific ioctls should only be in their respective headers
* The device specific ioctl range is from 0x40 to 0x9f.
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 728790b92354..72b277626a04 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -158,6 +158,21 @@ struct drm_mode_card_res {
__u32 max_height;
};
+/* also used for exclusive mode */
+struct drm_mode_allowed_res {
+ __u64 crtc_id_ptr;
+ __u64 connector_id_ptr;
+ __u64 plane_id_ptr;
+ __u32 count_crtcs;
+ __u32 count_connectors;
+ __u32 count_planes;
+};
+
+struct drm_mode_exclusive_res {
+ __u64 id_ptr;
+ __u32 count_ids;
+};
+
struct drm_mode_crtc {
__u64 set_connectors_ptr;
__u32 count_connectors;