Commit a48d4a33 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'drm-misc-fixes-2020-01-22-1' of...

Merge tag 'drm-misc-fixes-2020-01-22-1' of git://anongit.freedesktop.org/drm/drm-misc

 into drm-fixes

-mst: Fix SST branch device handling (Wayne)
-panfrost: Fix mapping of globally visible BO's (Boris)

Cc: Wayne Lin <Wayne.Lin@amd.com>
CC: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Sean Paul <sean@poorly.run>
Link: https://patchwork.freedesktop.org/patch/msgid/20200122213725.GA22099@art_vandelay
parents def9d278 bdefca2d
Loading
Loading
Loading
Loading
+80 −60
Original line number Diff line number Diff line
@@ -1916,55 +1916,72 @@ static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
	return parent_lct + 1;
}

static int drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt)
static bool drm_dp_mst_is_dp_mst_end_device(u8 pdt, bool mcs)
{
	switch (pdt) {
	case DP_PEER_DEVICE_DP_LEGACY_CONV:
	case DP_PEER_DEVICE_SST_SINK:
		return true;
	case DP_PEER_DEVICE_MST_BRANCHING:
		/* For sst branch device */
		if (!mcs)
			return true;

		return false;
	}
	return true;
}

static int
drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt,
		    bool new_mcs)
{
	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
	struct drm_dp_mst_branch *mstb;
	u8 rad[8], lct;
	int ret = 0;

	if (port->pdt == new_pdt)
	if (port->pdt == new_pdt && port->mcs == new_mcs)
		return 0;

	/* Teardown the old pdt, if there is one */
	switch (port->pdt) {
	case DP_PEER_DEVICE_DP_LEGACY_CONV:
	case DP_PEER_DEVICE_SST_SINK:
	if (port->pdt != DP_PEER_DEVICE_NONE) {
		if (drm_dp_mst_is_dp_mst_end_device(port->pdt, port->mcs)) {
			/*
		 * If the new PDT would also have an i2c bus, don't bother
		 * with reregistering it
			 * If the new PDT would also have an i2c bus,
			 * don't bother with reregistering it
			 */
		if (new_pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
		    new_pdt == DP_PEER_DEVICE_SST_SINK) {
			if (new_pdt != DP_PEER_DEVICE_NONE &&
			    drm_dp_mst_is_dp_mst_end_device(new_pdt, new_mcs)) {
				port->pdt = new_pdt;
				port->mcs = new_mcs;
				return 0;
			}

			/* remove i2c over sideband */
			drm_dp_mst_unregister_i2c_bus(&port->aux);
		break;
	case DP_PEER_DEVICE_MST_BRANCHING:
		} else {
			mutex_lock(&mgr->lock);
			drm_dp_mst_topology_put_mstb(port->mstb);
			port->mstb = NULL;
			mutex_unlock(&mgr->lock);
		break;
		}
	}

	port->pdt = new_pdt;
	switch (port->pdt) {
	case DP_PEER_DEVICE_DP_LEGACY_CONV:
	case DP_PEER_DEVICE_SST_SINK:
	port->mcs = new_mcs;

	if (port->pdt != DP_PEER_DEVICE_NONE) {
		if (drm_dp_mst_is_dp_mst_end_device(port->pdt, port->mcs)) {
			/* add i2c over sideband */
			ret = drm_dp_mst_register_i2c_bus(&port->aux);
		break;

	case DP_PEER_DEVICE_MST_BRANCHING:
		} else {
			lct = drm_dp_calculate_rad(port, rad);
			mstb = drm_dp_add_mst_branch_device(lct, rad);
			if (!mstb) {
				ret = -ENOMEM;
			DRM_ERROR("Failed to create MSTB for port %p", port);
				DRM_ERROR("Failed to create MSTB for port %p",
					  port);
				goto out;
			}

@@ -1982,7 +1999,7 @@ static int drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt)

			/* And make sure we send a link address for this */
			ret = 1;
		break;
		}
	}

out:
@@ -2135,9 +2152,8 @@ drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb,
		goto error;
	}

	if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
	     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
	    port->port_num >= DP_MST_LOGICAL_PORT_0) {
	if (port->pdt != DP_PEER_DEVICE_NONE &&
	    drm_dp_mst_is_dp_mst_end_device(port->pdt, port->mcs)) {
		port->cached_edid = drm_get_edid(port->connector,
						 &port->aux.ddc);
		drm_connector_set_tile_property(port->connector);
@@ -2201,6 +2217,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
	struct drm_dp_mst_port *port;
	int old_ddps = 0, ret;
	u8 new_pdt = DP_PEER_DEVICE_NONE;
	bool new_mcs = 0;
	bool created = false, send_link_addr = false, changed = false;

	port = drm_dp_get_port(mstb, port_msg->port_number);
@@ -2245,7 +2262,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
	port->input = port_msg->input_port;
	if (!port->input)
		new_pdt = port_msg->peer_device_type;
	port->mcs = port_msg->mcs;
	new_mcs = port_msg->mcs;
	port->ddps = port_msg->ddps;
	port->ldps = port_msg->legacy_device_plug_status;
	port->dpcd_rev = port_msg->dpcd_revision;
@@ -2272,7 +2289,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
		}
	}

	ret = drm_dp_port_set_pdt(port, new_pdt);
	ret = drm_dp_port_set_pdt(port, new_pdt, new_mcs);
	if (ret == 1) {
		send_link_addr = true;
	} else if (ret < 0) {
@@ -2286,7 +2303,8 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
	 * we're coming out of suspend. In this case, always resend the link
	 * address if there's an MSTB on this port
	 */
	if (!created && port->pdt == DP_PEER_DEVICE_MST_BRANCHING)
	if (!created && port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
	    port->mcs)
		send_link_addr = true;

	if (port->connector)
@@ -2323,6 +2341,7 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
	struct drm_dp_mst_port *port;
	int old_ddps, old_input, ret, i;
	u8 new_pdt;
	bool new_mcs;
	bool dowork = false, create_connector = false;

	port = drm_dp_get_port(mstb, conn_stat->port_number);
@@ -2354,7 +2373,6 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
	old_ddps = port->ddps;
	old_input = port->input;
	port->input = conn_stat->input_port;
	port->mcs = conn_stat->message_capability_status;
	port->ldps = conn_stat->legacy_device_plug_status;
	port->ddps = conn_stat->displayport_device_plug_status;

@@ -2367,8 +2385,8 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
	}

	new_pdt = port->input ? DP_PEER_DEVICE_NONE : conn_stat->peer_device_type;

	ret = drm_dp_port_set_pdt(port, new_pdt);
	new_mcs = conn_stat->message_capability_status;
	ret = drm_dp_port_set_pdt(port, new_pdt, new_mcs);
	if (ret == 1) {
		dowork = true;
	} else if (ret < 0) {
@@ -3929,6 +3947,8 @@ drm_dp_mst_detect_port(struct drm_connector *connector,
	switch (port->pdt) {
	case DP_PEER_DEVICE_NONE:
	case DP_PEER_DEVICE_MST_BRANCHING:
		if (!port->mcs)
			ret = connector_status_connected;
		break;

	case DP_PEER_DEVICE_SST_SINK:
@@ -4541,7 +4561,7 @@ drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)
	if (port->connector)
		port->mgr->cbs->destroy_connector(port->mgr, port->connector);

	drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE);
	drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE, port->mcs);
	drm_dp_mst_put_port_malloc(port);
}

+82 −9
Original line number Diff line number Diff line
@@ -78,8 +78,10 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct
static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
		struct drm_file *file)
{
	struct panfrost_file_priv *priv = file->driver_priv;
	struct panfrost_gem_object *bo;
	struct drm_panfrost_create_bo *args = data;
	struct panfrost_gem_mapping *mapping;

	if (!args->size || args->pad ||
	    (args->flags & ~(PANFROST_BO_NOEXEC | PANFROST_BO_HEAP)))
@@ -95,7 +97,14 @@ static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
	if (IS_ERR(bo))
		return PTR_ERR(bo);

	args->offset = bo->node.start << PAGE_SHIFT;
	mapping = panfrost_gem_mapping_get(bo, priv);
	if (!mapping) {
		drm_gem_object_put_unlocked(&bo->base.base);
		return -EINVAL;
	}

	args->offset = mapping->mmnode.start << PAGE_SHIFT;
	panfrost_gem_mapping_put(mapping);

	return 0;
}
@@ -119,6 +128,11 @@ panfrost_lookup_bos(struct drm_device *dev,
		  struct drm_panfrost_submit *args,
		  struct panfrost_job *job)
{
	struct panfrost_file_priv *priv = file_priv->driver_priv;
	struct panfrost_gem_object *bo;
	unsigned int i;
	int ret;

	job->bo_count = args->bo_handle_count;

	if (!job->bo_count)
@@ -130,9 +144,32 @@ panfrost_lookup_bos(struct drm_device *dev,
	if (!job->implicit_fences)
		return -ENOMEM;

	return drm_gem_objects_lookup(file_priv,
	ret = drm_gem_objects_lookup(file_priv,
				     (void __user *)(uintptr_t)args->bo_handles,
				     job->bo_count, &job->bos);
	if (ret)
		return ret;

	job->mappings = kvmalloc_array(job->bo_count,
				       sizeof(struct panfrost_gem_mapping *),
				       GFP_KERNEL | __GFP_ZERO);
	if (!job->mappings)
		return -ENOMEM;

	for (i = 0; i < job->bo_count; i++) {
		struct panfrost_gem_mapping *mapping;

		bo = to_panfrost_bo(job->bos[i]);
		mapping = panfrost_gem_mapping_get(bo, priv);
		if (!mapping) {
			ret = -EINVAL;
			break;
		}

		job->mappings[i] = mapping;
	}

	return ret;
}

/**
@@ -320,7 +357,9 @@ out:
static int panfrost_ioctl_get_bo_offset(struct drm_device *dev, void *data,
			    struct drm_file *file_priv)
{
	struct panfrost_file_priv *priv = file_priv->driver_priv;
	struct drm_panfrost_get_bo_offset *args = data;
	struct panfrost_gem_mapping *mapping;
	struct drm_gem_object *gem_obj;
	struct panfrost_gem_object *bo;

@@ -331,18 +370,26 @@ static int panfrost_ioctl_get_bo_offset(struct drm_device *dev, void *data,
	}
	bo = to_panfrost_bo(gem_obj);

	args->offset = bo->node.start << PAGE_SHIFT;

	mapping = panfrost_gem_mapping_get(bo, priv);
	drm_gem_object_put_unlocked(gem_obj);

	if (!mapping)
		return -EINVAL;

	args->offset = mapping->mmnode.start << PAGE_SHIFT;
	panfrost_gem_mapping_put(mapping);
	return 0;
}

static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
				  struct drm_file *file_priv)
{
	struct panfrost_file_priv *priv = file_priv->driver_priv;
	struct drm_panfrost_madvise *args = data;
	struct panfrost_device *pfdev = dev->dev_private;
	struct drm_gem_object *gem_obj;
	struct panfrost_gem_object *bo;
	int ret = 0;

	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
	if (!gem_obj) {
@@ -350,22 +397,48 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
		return -ENOENT;
	}

	bo = to_panfrost_bo(gem_obj);

	mutex_lock(&pfdev->shrinker_lock);
	mutex_lock(&bo->mappings.lock);
	if (args->madv == PANFROST_MADV_DONTNEED) {
		struct panfrost_gem_mapping *first;

		first = list_first_entry(&bo->mappings.list,
					 struct panfrost_gem_mapping,
					 node);

		/*
		 * If we want to mark the BO purgeable, there must be only one
		 * user: the caller FD.
		 * We could do something smarter and mark the BO purgeable only
		 * when all its users have marked it purgeable, but globally
		 * visible/shared BOs are likely to never be marked purgeable
		 * anyway, so let's not bother.
		 */
		if (!list_is_singular(&bo->mappings.list) ||
		    WARN_ON_ONCE(first->mmu != &priv->mmu)) {
			ret = -EINVAL;
			goto out_unlock_mappings;
		}
	}

	args->retained = drm_gem_shmem_madvise(gem_obj, args->madv);

	if (args->retained) {
		struct panfrost_gem_object *bo = to_panfrost_bo(gem_obj);

		if (args->madv == PANFROST_MADV_DONTNEED)
			list_add_tail(&bo->base.madv_list,
				      &pfdev->shrinker_list);
		else if (args->madv == PANFROST_MADV_WILLNEED)
			list_del_init(&bo->base.madv_list);
	}

out_unlock_mappings:
	mutex_unlock(&bo->mappings.lock);
	mutex_unlock(&pfdev->shrinker_lock);

	drm_gem_object_put_unlocked(gem_obj);
	return 0;
	return ret;
}

int panfrost_unstable_ioctl_check(void)
+108 −16
Original line number Diff line number Diff line
@@ -29,6 +29,12 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
	list_del_init(&bo->base.madv_list);
	mutex_unlock(&pfdev->shrinker_lock);

	/*
	 * If we still have mappings attached to the BO, there's a problem in
	 * our refcounting.
	 */
	WARN_ON_ONCE(!list_empty(&bo->mappings.list));

	if (bo->sgts) {
		int i;
		int n_sgt = bo->base.base.size / SZ_2M;
@@ -46,6 +52,69 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
	drm_gem_shmem_free_object(obj);
}

struct panfrost_gem_mapping *
panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
			 struct panfrost_file_priv *priv)
{
	struct panfrost_gem_mapping *iter, *mapping = NULL;

	mutex_lock(&bo->mappings.lock);
	list_for_each_entry(iter, &bo->mappings.list, node) {
		if (iter->mmu == &priv->mmu) {
			kref_get(&iter->refcount);
			mapping = iter;
			break;
		}
	}
	mutex_unlock(&bo->mappings.lock);

	return mapping;
}

static void
panfrost_gem_teardown_mapping(struct panfrost_gem_mapping *mapping)
{
	struct panfrost_file_priv *priv;

	if (mapping->active)
		panfrost_mmu_unmap(mapping);

	priv = container_of(mapping->mmu, struct panfrost_file_priv, mmu);
	spin_lock(&priv->mm_lock);
	if (drm_mm_node_allocated(&mapping->mmnode))
		drm_mm_remove_node(&mapping->mmnode);
	spin_unlock(&priv->mm_lock);
}

static void panfrost_gem_mapping_release(struct kref *kref)
{
	struct panfrost_gem_mapping *mapping;

	mapping = container_of(kref, struct panfrost_gem_mapping, refcount);

	panfrost_gem_teardown_mapping(mapping);
	drm_gem_object_put_unlocked(&mapping->obj->base.base);
	kfree(mapping);
}

void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping)
{
	if (!mapping)
		return;

	kref_put(&mapping->refcount, panfrost_gem_mapping_release);
}

void panfrost_gem_teardown_mappings(struct panfrost_gem_object *bo)
{
	struct panfrost_gem_mapping *mapping;

	mutex_lock(&bo->mappings.lock);
	list_for_each_entry(mapping, &bo->mappings.list, node)
		panfrost_gem_teardown_mapping(mapping);
	mutex_unlock(&bo->mappings.lock);
}

int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv)
{
	int ret;
@@ -54,6 +123,16 @@ int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv)
	struct panfrost_gem_object *bo = to_panfrost_bo(obj);
	unsigned long color = bo->noexec ? PANFROST_BO_NOEXEC : 0;
	struct panfrost_file_priv *priv = file_priv->driver_priv;
	struct panfrost_gem_mapping *mapping;

	mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
	if (!mapping)
		return -ENOMEM;

	INIT_LIST_HEAD(&mapping->node);
	kref_init(&mapping->refcount);
	drm_gem_object_get(obj);
	mapping->obj = bo;

	/*
	 * Executable buffers cannot cross a 16MB boundary as the program
@@ -66,37 +145,48 @@ int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv)
	else
		align = size >= SZ_2M ? SZ_2M >> PAGE_SHIFT : 0;

	bo->mmu = &priv->mmu;
	mapping->mmu = &priv->mmu;
	spin_lock(&priv->mm_lock);
	ret = drm_mm_insert_node_generic(&priv->mm, &bo->node,
	ret = drm_mm_insert_node_generic(&priv->mm, &mapping->mmnode,
					 size >> PAGE_SHIFT, align, color, 0);
	spin_unlock(&priv->mm_lock);
	if (ret)
		return ret;
		goto err;

	if (!bo->is_heap) {
		ret = panfrost_mmu_map(bo);
		if (ret) {
			spin_lock(&priv->mm_lock);
			drm_mm_remove_node(&bo->node);
			spin_unlock(&priv->mm_lock);
		}
		ret = panfrost_mmu_map(mapping);
		if (ret)
			goto err;
	}

	mutex_lock(&bo->mappings.lock);
	WARN_ON(bo->base.madv != PANFROST_MADV_WILLNEED);
	list_add_tail(&mapping->node, &bo->mappings.list);
	mutex_unlock(&bo->mappings.lock);

err:
	if (ret)
		panfrost_gem_mapping_put(mapping);
	return ret;
}

void panfrost_gem_close(struct drm_gem_object *obj, struct drm_file *file_priv)
{
	struct panfrost_gem_object *bo = to_panfrost_bo(obj);
	struct panfrost_file_priv *priv = file_priv->driver_priv;
	struct panfrost_gem_object *bo = to_panfrost_bo(obj);
	struct panfrost_gem_mapping *mapping = NULL, *iter;

	if (bo->is_mapped)
		panfrost_mmu_unmap(bo);
	mutex_lock(&bo->mappings.lock);
	list_for_each_entry(iter, &bo->mappings.list, node) {
		if (iter->mmu == &priv->mmu) {
			mapping = iter;
			list_del(&iter->node);
			break;
		}
	}
	mutex_unlock(&bo->mappings.lock);

	spin_lock(&priv->mm_lock);
	if (drm_mm_node_allocated(&bo->node))
		drm_mm_remove_node(&bo->node);
	spin_unlock(&priv->mm_lock);
	panfrost_gem_mapping_put(mapping);
}

static int panfrost_gem_pin(struct drm_gem_object *obj)
@@ -136,6 +226,8 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t
	if (!obj)
		return NULL;

	INIT_LIST_HEAD(&obj->mappings.list);
	mutex_init(&obj->mappings.lock);
	obj->base.base.funcs = &panfrost_gem_funcs;

	return &obj->base.base;
+35 −6
Original line number Diff line number Diff line
@@ -13,23 +13,46 @@ struct panfrost_gem_object {
	struct drm_gem_shmem_object base;
	struct sg_table *sgts;

	struct panfrost_mmu *mmu;
	struct drm_mm_node node;
	bool is_mapped		:1;
	/*
	 * Use a list for now. If searching a mapping ever becomes the
	 * bottleneck, we should consider using an RB-tree, or even better,
	 * let the core store drm_gem_object_mapping entries (where we
	 * could place driver specific data) instead of drm_gem_object ones
	 * in its drm_file->object_idr table.
	 *
	 * struct drm_gem_object_mapping {
	 *	struct drm_gem_object *obj;
	 *	void *driver_priv;
	 * };
	 */
	struct {
		struct list_head list;
		struct mutex lock;
	} mappings;

	bool noexec		:1;
	bool is_heap		:1;
};

struct panfrost_gem_mapping {
	struct list_head node;
	struct kref refcount;
	struct panfrost_gem_object *obj;
	struct drm_mm_node mmnode;
	struct panfrost_mmu *mmu;
	bool active		:1;
};

static inline
struct  panfrost_gem_object *to_panfrost_bo(struct drm_gem_object *obj)
{
	return container_of(to_drm_gem_shmem_obj(obj), struct panfrost_gem_object, base);
}

static inline
struct  panfrost_gem_object *drm_mm_node_to_panfrost_bo(struct drm_mm_node *node)
static inline struct panfrost_gem_mapping *
drm_mm_node_to_panfrost_mapping(struct drm_mm_node *node)
{
	return container_of(node, struct panfrost_gem_object, node);
	return container_of(node, struct panfrost_gem_mapping, mmnode);
}

struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size);
@@ -49,6 +72,12 @@ int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv);
void panfrost_gem_close(struct drm_gem_object *obj,
			struct drm_file *file_priv);

struct panfrost_gem_mapping *
panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
			 struct panfrost_file_priv *priv);
void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping);
void panfrost_gem_teardown_mappings(struct panfrost_gem_object *bo);

void panfrost_gem_shrinker_init(struct drm_device *dev);
void panfrost_gem_shrinker_cleanup(struct drm_device *dev);

+2 −1
Original line number Diff line number Diff line
@@ -39,11 +39,12 @@ panfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc
static bool panfrost_gem_purge(struct drm_gem_object *obj)
{
	struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
	struct panfrost_gem_object *bo = to_panfrost_bo(obj);

	if (!mutex_trylock(&shmem->pages_lock))
		return false;

	panfrost_mmu_unmap(to_panfrost_bo(obj));
	panfrost_gem_teardown_mappings(bo);
	drm_gem_shmem_purge_locked(obj);

	mutex_unlock(&shmem->pages_lock);
Loading