Commit 9a6e8c7c authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'drm-fixes-2024-12-07' of https://gitlab.freedesktop.org/drm/kernel

Pull drm fixes from Dave Airlie:
 "Pretty quiet week which is probably expected after US holidays, the
  dma-fence and displayport MST message handling fixes make up the bulk
  of this, along with a couple of minor xe and other driver fixes.

  dma-fence:
   - Fix reference leak on fence-merge failure path
   - Simplify fence merging with kernel's sort()
   - Fix dma_fence_array_signaled() to ensure forward progress

  dp_mst:
   - Fix MST sideband message body length check
   - Fix a bunch of locking/state handling with DP MST msgs

  sti:
   - Add __iomem for mixer_dbg_mxn()'s parameter

  xe:
   - Missing init value and 64-bit write-order check
   - Fix a memory allocation issue causing lockdep violation

  v3d:
   - Performance counter fix"

* tag 'drm-fixes-2024-12-07' of https://gitlab.freedesktop.org/drm/kernel:
  drm/v3d: Enable Performance Counters before clearing them
  drm/dp_mst: Use reset_msg_rx_state() instead of open coding it
  drm/dp_mst: Reset message rx state after OOM in drm_dp_mst_handle_up_req()
  drm/dp_mst: Ensure mst_primary pointer is valid in drm_dp_mst_handle_up_req()
  drm/dp_mst: Fix down request message timeout handling
  drm/dp_mst: Simplify error path in drm_dp_mst_handle_down_rep()
  drm/dp_mst: Verify request type in the corresponding down message reply
  drm/dp_mst: Fix resetting msg rx state after topology removal
  drm/xe: Move the coredump registration to the worker thread
  drm/xe/guc: Fix missing init value and add register order check
  drm/sti: Add __iomem for mixer_dbg_mxn's parameter
  drm/dp_mst: Fix MST sideband message body length check
  dma-buf: fix dma_fence_array_signaled v4
  dma-fence: Use kernel's sort for merging fences
  dma-fence: Fix reference leak on fence merge failure path
parents 2b90dcd5 471f3a21
Loading
Loading
Loading
Loading
+27 −1
Original line number Diff line number Diff line
@@ -103,10 +103,36 @@ static bool dma_fence_array_enable_signaling(struct dma_fence *fence)
static bool dma_fence_array_signaled(struct dma_fence *fence)
{
	struct dma_fence_array *array = to_dma_fence_array(fence);
	int num_pending;
	unsigned int i;

	if (atomic_read(&array->num_pending) > 0)
	/*
	 * We need to read num_pending before checking the enable_signal bit
	 * to avoid racing with the enable_signaling() implementation, which
	 * might decrement the counter, and cause a partial check.
	 * atomic_read_acquire() pairs with atomic_dec_and_test() in
	 * dma_fence_array_enable_signaling()
	 *
	 * The !--num_pending check is here to account for the any_signaled case
	 * if we race with enable_signaling(), that means the !num_pending check
	 * in the is_signalling_enabled branch might be outdated (num_pending
	 * might have been decremented), but that's fine. The user will get the
	 * right value when testing again later.
	 */
	num_pending = atomic_read_acquire(&array->num_pending);
	if (test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &array->base.flags)) {
		if (num_pending <= 0)
			goto signal;
		return false;
	}

	for (i = 0; i < array->num_fences; ++i) {
		if (dma_fence_is_signaled(array->fences[i]) && !--num_pending)
			goto signal;
	}
	return false;

signal:
	dma_fence_array_clear_pending_error(array);
	return true;
}
+61 −65
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include <linux/dma-fence-chain.h>
#include <linux/dma-fence-unwrap.h>
#include <linux/slab.h>
#include <linux/sort.h>

/* Internal helper to start new array iteration, don't use directly */
static struct dma_fence *
@@ -59,6 +60,25 @@ struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor)
}
EXPORT_SYMBOL_GPL(dma_fence_unwrap_next);


static int fence_cmp(const void *_a, const void *_b)
{
	struct dma_fence *a = *(struct dma_fence **)_a;
	struct dma_fence *b = *(struct dma_fence **)_b;

	if (a->context < b->context)
		return -1;
	else if (a->context > b->context)
		return 1;

	if (dma_fence_is_later(b, a))
		return 1;
	else if (dma_fence_is_later(a, b))
		return -1;

	return 0;
}

/* Implementation for the dma_fence_merge() marco, don't use directly */
struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
					   struct dma_fence **fences,
@@ -67,8 +87,7 @@ struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
	struct dma_fence_array *result;
	struct dma_fence *tmp, **array;
	ktime_t timestamp;
	unsigned int i;
	size_t count;
	int i, j, count;

	count = 0;
	timestamp = ns_to_ktime(0);
@@ -96,78 +115,55 @@ struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
	if (!array)
		return NULL;

	/*
	 * This trashes the input fence array and uses it as position for the
	 * following merge loop. This works because the dma_fence_merge()
	 * wrapper macro is creating this temporary array on the stack together
	 * with the iterators.
	 */
	for (i = 0; i < num_fences; ++i)
		fences[i] = dma_fence_unwrap_first(fences[i], &iter[i]);

	count = 0;
	do {
		unsigned int sel;

restart:
		tmp = NULL;
	for (i = 0; i < num_fences; ++i) {
			struct dma_fence *next;

			while (fences[i] && dma_fence_is_signaled(fences[i]))
				fences[i] = dma_fence_unwrap_next(&iter[i]);

			next = fences[i];
			if (!next)
				continue;

			/*
			 * We can't guarantee that inpute fences are ordered by
			 * context, but it is still quite likely when this
			 * function is used multiple times. So attempt to order
			 * the fences by context as we pass over them and merge
			 * fences with the same context.
			 */
			if (!tmp || tmp->context > next->context) {
				tmp = next;
				sel = i;

			} else if (tmp->context < next->context) {
				continue;

			} else if (dma_fence_is_later(tmp, next)) {
				fences[i] = dma_fence_unwrap_next(&iter[i]);
				goto restart;
		dma_fence_unwrap_for_each(tmp, &iter[i], fences[i]) {
			if (!dma_fence_is_signaled(tmp)) {
				array[count++] = dma_fence_get(tmp);
			} else {
				fences[sel] = dma_fence_unwrap_next(&iter[sel]);
				goto restart;
				ktime_t t = dma_fence_timestamp(tmp);

				if (ktime_after(t, timestamp))
					timestamp = t;
			}
		}

		if (tmp) {
			array[count++] = dma_fence_get(tmp);
			fences[sel] = dma_fence_unwrap_next(&iter[sel]);
	}
	} while (tmp);

	if (count == 0) {
		tmp = dma_fence_allocate_private_stub(ktime_get());
		goto return_tmp;
	}
	if (count == 0 || count == 1)
		goto return_fastpath;

	if (count == 1) {
		tmp = array[0];
		goto return_tmp;
	sort(array, count, sizeof(*array), fence_cmp, NULL);

	/*
	 * Only keep the most recent fence for each context.
	 */
	j = 0;
	for (i = 1; i < count; i++) {
		if (array[i]->context == array[j]->context)
			dma_fence_put(array[i]);
		else
			array[++j] = array[i];
	}
	count = ++j;

	if (count > 1) {
		result = dma_fence_array_create(count, array,
						dma_fence_context_alloc(1),
						1, false);
		if (!result) {
			for (i = 0; i < count; i++)
				dma_fence_put(array[i]);
			tmp = NULL;
			goto return_tmp;
		}
		return &result->base;
	}

return_fastpath:
	if (count == 0)
		tmp = dma_fence_allocate_private_stub(timestamp);
	else
		tmp = array[0];

return_tmp:
	kfree(array);
+87 −20
Original line number Diff line number Diff line
@@ -320,6 +320,9 @@ static bool drm_dp_decode_sideband_msg_hdr(const struct drm_dp_mst_topology_mgr
	hdr->broadcast = (buf[idx] >> 7) & 0x1;
	hdr->path_msg = (buf[idx] >> 6) & 0x1;
	hdr->msg_len = buf[idx] & 0x3f;
	if (hdr->msg_len < 1)		/* min space for body CRC */
		return false;

	idx++;
	hdr->somt = (buf[idx] >> 7) & 0x1;
	hdr->eomt = (buf[idx] >> 6) & 0x1;
@@ -3697,8 +3700,7 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
		ret = 0;
		mgr->payload_id_table_cleared = false;

		memset(&mgr->down_rep_recv, 0, sizeof(mgr->down_rep_recv));
		memset(&mgr->up_req_recv, 0, sizeof(mgr->up_req_recv));
		mgr->reset_rx_state = true;
	}

out_unlock:
@@ -3856,6 +3858,11 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
}
EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);

static void reset_msg_rx_state(struct drm_dp_sideband_msg_rx *msg)
{
	memset(msg, 0, sizeof(*msg));
}

static bool
drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up,
		      struct drm_dp_mst_branch **mstb)
@@ -3934,6 +3941,34 @@ drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up,
	return true;
}

static int get_msg_request_type(u8 data)
{
	return data & 0x7f;
}

static bool verify_rx_request_type(struct drm_dp_mst_topology_mgr *mgr,
				   const struct drm_dp_sideband_msg_tx *txmsg,
				   const struct drm_dp_sideband_msg_rx *rxmsg)
{
	const struct drm_dp_sideband_msg_hdr *hdr = &rxmsg->initial_hdr;
	const struct drm_dp_mst_branch *mstb = txmsg->dst;
	int tx_req_type = get_msg_request_type(txmsg->msg[0]);
	int rx_req_type = get_msg_request_type(rxmsg->msg[0]);
	char rad_str[64];

	if (tx_req_type == rx_req_type)
		return true;

	drm_dp_mst_rad_to_str(mstb->rad, mstb->lct, rad_str, sizeof(rad_str));
	drm_dbg_kms(mgr->dev,
		    "Got unexpected MST reply, mstb: %p seqno: %d lct: %d rad: %s rx_req_type: %s (%02x) != tx_req_type: %s (%02x)\n",
		    mstb, hdr->seqno, mstb->lct, rad_str,
		    drm_dp_mst_req_type_str(rx_req_type), rx_req_type,
		    drm_dp_mst_req_type_str(tx_req_type), tx_req_type);

	return false;
}

static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
{
	struct drm_dp_sideband_msg_tx *txmsg;
@@ -3949,9 +3984,9 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)

	/* find the message */
	mutex_lock(&mgr->qlock);

	txmsg = list_first_entry_or_null(&mgr->tx_msg_downq,
					 struct drm_dp_sideband_msg_tx, next);
	mutex_unlock(&mgr->qlock);

	/* Were we actually expecting a response, and from this mstb? */
	if (!txmsg || txmsg->dst != mstb) {
@@ -3960,6 +3995,15 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
		hdr = &msg->initial_hdr;
		drm_dbg_kms(mgr->dev, "Got MST reply with no msg %p %d %d %02x %02x\n",
			    mstb, hdr->seqno, hdr->lct, hdr->rad[0], msg->msg[0]);

		mutex_unlock(&mgr->qlock);

		goto out_clear_reply;
	}

	if (!verify_rx_request_type(mgr, txmsg, msg)) {
		mutex_unlock(&mgr->qlock);

		goto out_clear_reply;
	}

@@ -3975,20 +4019,15 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
			    txmsg->reply.u.nak.nak_data);
	}

	memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx));
	drm_dp_mst_topology_put_mstb(mstb);

	mutex_lock(&mgr->qlock);
	txmsg->state = DRM_DP_SIDEBAND_TX_RX;
	list_del(&txmsg->next);

	mutex_unlock(&mgr->qlock);

	wake_up_all(&mgr->tx_waitq);

	return 0;

out_clear_reply:
	memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx));
	reset_msg_rx_state(msg);
out:
	if (mstb)
		drm_dp_mst_topology_put_mstb(mstb);
@@ -4070,16 +4109,20 @@ static void drm_dp_mst_up_req_work(struct work_struct *work)
static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
{
	struct drm_dp_pending_up_req *up_req;
	struct drm_dp_mst_branch *mst_primary;
	int ret = 0;

	if (!drm_dp_get_one_sb_msg(mgr, true, NULL))
		goto out;
		goto out_clear_reply;

	if (!mgr->up_req_recv.have_eomt)
		return 0;

	up_req = kzalloc(sizeof(*up_req), GFP_KERNEL);
	if (!up_req)
		return -ENOMEM;
	if (!up_req) {
		ret = -ENOMEM;
		goto out_clear_reply;
	}

	INIT_LIST_HEAD(&up_req->next);

@@ -4090,10 +4133,19 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
		drm_dbg_kms(mgr->dev, "Received unknown up req type, ignoring: %x\n",
			    up_req->msg.req_type);
		kfree(up_req);
		goto out;
		goto out_clear_reply;
	}

	drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, up_req->msg.req_type,
	mutex_lock(&mgr->lock);
	mst_primary = mgr->mst_primary;
	if (!mst_primary || !drm_dp_mst_topology_try_get_mstb(mst_primary)) {
		mutex_unlock(&mgr->lock);
		kfree(up_req);
		goto out_clear_reply;
	}
	mutex_unlock(&mgr->lock);

	drm_dp_send_up_ack_reply(mgr, mst_primary, up_req->msg.req_type,
				 false);

	if (up_req->msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
@@ -4110,13 +4162,13 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
			    conn_stat->peer_device_type);

		mutex_lock(&mgr->probe_lock);
		handle_csn = mgr->mst_primary->link_address_sent;
		handle_csn = mst_primary->link_address_sent;
		mutex_unlock(&mgr->probe_lock);

		if (!handle_csn) {
			drm_dbg_kms(mgr->dev, "Got CSN before finish topology probing. Skip it.");
			kfree(up_req);
			goto out;
			goto out_put_primary;
		}
	} else if (up_req->msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
		const struct drm_dp_resource_status_notify *res_stat =
@@ -4133,9 +4185,22 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
	mutex_unlock(&mgr->up_req_lock);
	queue_work(system_long_wq, &mgr->up_req_work);

out:
	memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
	return 0;
out_put_primary:
	drm_dp_mst_topology_put_mstb(mst_primary);
out_clear_reply:
	reset_msg_rx_state(&mgr->up_req_recv);
	return ret;
}

static void update_msg_rx_state(struct drm_dp_mst_topology_mgr *mgr)
{
	mutex_lock(&mgr->lock);
	if (mgr->reset_rx_state) {
		mgr->reset_rx_state = false;
		reset_msg_rx_state(&mgr->down_rep_recv);
		reset_msg_rx_state(&mgr->up_req_recv);
	}
	mutex_unlock(&mgr->lock);
}

/**
@@ -4172,6 +4237,8 @@ int drm_dp_mst_hpd_irq_handle_event(struct drm_dp_mst_topology_mgr *mgr, const u
		*handled = true;
	}

	update_msg_rx_state(mgr);

	if (esi[1] & DP_DOWN_REP_MSG_RDY) {
		ret = drm_dp_mst_handle_down_rep(mgr);
		*handled = true;
+1 −1
Original line number Diff line number Diff line
@@ -137,7 +137,7 @@ static void mixer_dbg_crb(struct seq_file *s, int val)
	}
}

static void mixer_dbg_mxn(struct seq_file *s, void *addr)
static void mixer_dbg_mxn(struct seq_file *s, void __iomem *addr)
{
	int i;

+1 −1
Original line number Diff line number Diff line
@@ -254,9 +254,9 @@ void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
		V3D_CORE_WRITE(0, V3D_V4_PCTR_0_SRC_X(source), channel);
	}

	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, mask);
	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_CLR, mask);
	V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, mask);

	v3d->active_perfmon = perfmon;
}
Loading