mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 02:36:02 +00:00
drm fixes for 6.13-rc2
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. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmdTROUACgkQDHTzWXnE hr5vYw//TeMMwRs708c5RnH/1RUthChcStTqk/aVJvHkqjMiqUgEGxrW2OruNTIO TODPOWvCrb4AzvD21NKMfZx5dT2fBoq9yw40Qb3eXXSFodH9V4+teEeMh5UI1imJ OZvLXiXv+4jTuSwu/01YuiPR16j75QqrvLMvWrCWA7m8Of393uJUa570OQt6G7C3 lwsKVot16/xBQpUQkNQtch3HyMAvH+5pttnw7DOyZxpAuze9J5erEPicgIkN0j1V GB0zohDgqMmnqKLJrOZowt2ngZ6rT8uC6RlpV2+IquXZG3O+nCiwlsDmDeIE4mbO Rkur3TR7whp2VKkKRN+ll6kb0TDnPfj3ylFzIRfPyFZjT3JIV4ps5k8E9/tRyeGO HQ9D6DGjETw21rJlvq0YEotalP/EyIL8W7jaJgAMmHXTri/reF7NKyq6ngSeck7B aF3IFPwtZuJZawbecUChgWRC86NDBi4d8iUyZaaGGDxLQFjzVntUDQF9hrGFf1Vj aOLCS4GX1Gp8atv44PDO2KH+7X2t2CpzFY2F8GN4gv74iepeZfDW/bjZLwruVBZY Jk6UMlV1oIJB50FT1CtpfILm4B87Ks7mvL7unmY2hlqQz3xWE/2J8GhJcz+2Ww5R PNwN/c1mxNgk74dPEkDJh1BGKC4PyKVzwll3KIed5jatbqwqFM0= =NMaJ -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
9a6e8c7c3a
@ -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)
|
static bool dma_fence_array_signaled(struct dma_fence *fence)
|
||||||
{
|
{
|
||||||
struct dma_fence_array *array = to_dma_fence_array(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;
|
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);
|
dma_fence_array_clear_pending_error(array);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <linux/dma-fence-chain.h>
|
#include <linux/dma-fence-chain.h>
|
||||||
#include <linux/dma-fence-unwrap.h>
|
#include <linux/dma-fence-unwrap.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/sort.h>
|
||||||
|
|
||||||
/* Internal helper to start new array iteration, don't use directly */
|
/* Internal helper to start new array iteration, don't use directly */
|
||||||
static struct dma_fence *
|
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);
|
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 */
|
/* 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 *__dma_fence_unwrap_merge(unsigned int num_fences,
|
||||||
struct dma_fence **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_array *result;
|
||||||
struct dma_fence *tmp, **array;
|
struct dma_fence *tmp, **array;
|
||||||
ktime_t timestamp;
|
ktime_t timestamp;
|
||||||
unsigned int i;
|
int i, j, count;
|
||||||
size_t count;
|
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
timestamp = ns_to_ktime(0);
|
timestamp = ns_to_ktime(0);
|
||||||
@ -96,78 +115,55 @@ struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
|
|||||||
if (!array)
|
if (!array)
|
||||||
return NULL;
|
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;
|
count = 0;
|
||||||
do {
|
for (i = 0; i < num_fences; ++i) {
|
||||||
unsigned int sel;
|
dma_fence_unwrap_for_each(tmp, &iter[i], fences[i]) {
|
||||||
|
if (!dma_fence_is_signaled(tmp)) {
|
||||||
restart:
|
array[count++] = dma_fence_get(tmp);
|
||||||
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;
|
|
||||||
} else {
|
} else {
|
||||||
fences[sel] = dma_fence_unwrap_next(&iter[sel]);
|
ktime_t t = dma_fence_timestamp(tmp);
|
||||||
goto restart;
|
|
||||||
|
if (ktime_after(t, timestamp))
|
||||||
|
timestamp = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (tmp) {
|
if (count == 0 || count == 1)
|
||||||
array[count++] = dma_fence_get(tmp);
|
goto return_fastpath;
|
||||||
fences[sel] = dma_fence_unwrap_next(&iter[sel]);
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
} while (tmp);
|
return &result->base;
|
||||||
|
|
||||||
if (count == 0) {
|
|
||||||
tmp = dma_fence_allocate_private_stub(ktime_get());
|
|
||||||
goto return_tmp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count == 1) {
|
return_fastpath:
|
||||||
|
if (count == 0)
|
||||||
|
tmp = dma_fence_allocate_private_stub(timestamp);
|
||||||
|
else
|
||||||
tmp = array[0];
|
tmp = array[0];
|
||||||
goto return_tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = dma_fence_array_create(count, array,
|
|
||||||
dma_fence_context_alloc(1),
|
|
||||||
1, false);
|
|
||||||
if (!result) {
|
|
||||||
tmp = NULL;
|
|
||||||
goto return_tmp;
|
|
||||||
}
|
|
||||||
return &result->base;
|
|
||||||
|
|
||||||
return_tmp:
|
return_tmp:
|
||||||
kfree(array);
|
kfree(array);
|
||||||
|
@ -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->broadcast = (buf[idx] >> 7) & 0x1;
|
||||||
hdr->path_msg = (buf[idx] >> 6) & 0x1;
|
hdr->path_msg = (buf[idx] >> 6) & 0x1;
|
||||||
hdr->msg_len = buf[idx] & 0x3f;
|
hdr->msg_len = buf[idx] & 0x3f;
|
||||||
|
if (hdr->msg_len < 1) /* min space for body CRC */
|
||||||
|
return false;
|
||||||
|
|
||||||
idx++;
|
idx++;
|
||||||
hdr->somt = (buf[idx] >> 7) & 0x1;
|
hdr->somt = (buf[idx] >> 7) & 0x1;
|
||||||
hdr->eomt = (buf[idx] >> 6) & 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;
|
ret = 0;
|
||||||
mgr->payload_id_table_cleared = false;
|
mgr->payload_id_table_cleared = false;
|
||||||
|
|
||||||
memset(&mgr->down_rep_recv, 0, sizeof(mgr->down_rep_recv));
|
mgr->reset_rx_state = true;
|
||||||
memset(&mgr->up_req_recv, 0, sizeof(mgr->up_req_recv));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out_unlock:
|
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);
|
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
|
static bool
|
||||||
drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up,
|
drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up,
|
||||||
struct drm_dp_mst_branch **mstb)
|
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;
|
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)
|
static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
|
||||||
{
|
{
|
||||||
struct drm_dp_sideband_msg_tx *txmsg;
|
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 */
|
/* find the message */
|
||||||
mutex_lock(&mgr->qlock);
|
mutex_lock(&mgr->qlock);
|
||||||
|
|
||||||
txmsg = list_first_entry_or_null(&mgr->tx_msg_downq,
|
txmsg = list_first_entry_or_null(&mgr->tx_msg_downq,
|
||||||
struct drm_dp_sideband_msg_tx, next);
|
struct drm_dp_sideband_msg_tx, next);
|
||||||
mutex_unlock(&mgr->qlock);
|
|
||||||
|
|
||||||
/* Were we actually expecting a response, and from this mstb? */
|
/* Were we actually expecting a response, and from this mstb? */
|
||||||
if (!txmsg || txmsg->dst != 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;
|
hdr = &msg->initial_hdr;
|
||||||
drm_dbg_kms(mgr->dev, "Got MST reply with no msg %p %d %d %02x %02x\n",
|
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]);
|
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;
|
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);
|
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;
|
txmsg->state = DRM_DP_SIDEBAND_TX_RX;
|
||||||
list_del(&txmsg->next);
|
list_del(&txmsg->next);
|
||||||
|
|
||||||
mutex_unlock(&mgr->qlock);
|
mutex_unlock(&mgr->qlock);
|
||||||
|
|
||||||
wake_up_all(&mgr->tx_waitq);
|
wake_up_all(&mgr->tx_waitq);
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_clear_reply:
|
out_clear_reply:
|
||||||
memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx));
|
reset_msg_rx_state(msg);
|
||||||
out:
|
out:
|
||||||
if (mstb)
|
if (mstb)
|
||||||
drm_dp_mst_topology_put_mstb(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)
|
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_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))
|
if (!drm_dp_get_one_sb_msg(mgr, true, NULL))
|
||||||
goto out;
|
goto out_clear_reply;
|
||||||
|
|
||||||
if (!mgr->up_req_recv.have_eomt)
|
if (!mgr->up_req_recv.have_eomt)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
up_req = kzalloc(sizeof(*up_req), GFP_KERNEL);
|
up_req = kzalloc(sizeof(*up_req), GFP_KERNEL);
|
||||||
if (!up_req)
|
if (!up_req) {
|
||||||
return -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
goto out_clear_reply;
|
||||||
|
}
|
||||||
|
|
||||||
INIT_LIST_HEAD(&up_req->next);
|
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",
|
drm_dbg_kms(mgr->dev, "Received unknown up req type, ignoring: %x\n",
|
||||||
up_req->msg.req_type);
|
up_req->msg.req_type);
|
||||||
kfree(up_req);
|
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);
|
false);
|
||||||
|
|
||||||
if (up_req->msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
|
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);
|
conn_stat->peer_device_type);
|
||||||
|
|
||||||
mutex_lock(&mgr->probe_lock);
|
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);
|
mutex_unlock(&mgr->probe_lock);
|
||||||
|
|
||||||
if (!handle_csn) {
|
if (!handle_csn) {
|
||||||
drm_dbg_kms(mgr->dev, "Got CSN before finish topology probing. Skip it.");
|
drm_dbg_kms(mgr->dev, "Got CSN before finish topology probing. Skip it.");
|
||||||
kfree(up_req);
|
kfree(up_req);
|
||||||
goto out;
|
goto out_put_primary;
|
||||||
}
|
}
|
||||||
} else if (up_req->msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
|
} else if (up_req->msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
|
||||||
const struct drm_dp_resource_status_notify *res_stat =
|
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);
|
mutex_unlock(&mgr->up_req_lock);
|
||||||
queue_work(system_long_wq, &mgr->up_req_work);
|
queue_work(system_long_wq, &mgr->up_req_work);
|
||||||
|
|
||||||
out:
|
out_put_primary:
|
||||||
memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
|
drm_dp_mst_topology_put_mstb(mst_primary);
|
||||||
return 0;
|
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;
|
*handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_msg_rx_state(mgr);
|
||||||
|
|
||||||
if (esi[1] & DP_DOWN_REP_MSG_RDY) {
|
if (esi[1] & DP_DOWN_REP_MSG_RDY) {
|
||||||
ret = drm_dp_mst_handle_down_rep(mgr);
|
ret = drm_dp_mst_handle_down_rep(mgr);
|
||||||
*handled = true;
|
*handled = true;
|
||||||
|
@ -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;
|
int i;
|
||||||
|
|
||||||
|
@ -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_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_V4_PCTR_0_CLR, mask);
|
||||||
V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
|
V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
|
||||||
V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, mask);
|
|
||||||
|
|
||||||
v3d->active_perfmon = perfmon;
|
v3d->active_perfmon = perfmon;
|
||||||
}
|
}
|
||||||
|
@ -155,36 +155,6 @@ static void xe_devcoredump_snapshot_free(struct xe_devcoredump_snapshot *ss)
|
|||||||
ss->vm = NULL;
|
ss->vm = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct xe_devcoredump_snapshot *ss = container_of(work, typeof(*ss), work);
|
|
||||||
struct xe_devcoredump *coredump = container_of(ss, typeof(*coredump), snapshot);
|
|
||||||
struct xe_device *xe = coredump_to_xe(coredump);
|
|
||||||
unsigned int fw_ref;
|
|
||||||
|
|
||||||
xe_pm_runtime_get(xe);
|
|
||||||
|
|
||||||
/* keep going if fw fails as we still want to save the memory and SW data */
|
|
||||||
fw_ref = xe_force_wake_get(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL);
|
|
||||||
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
|
|
||||||
xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n");
|
|
||||||
xe_vm_snapshot_capture_delayed(ss->vm);
|
|
||||||
xe_guc_exec_queue_snapshot_capture_delayed(ss->ge);
|
|
||||||
xe_force_wake_put(gt_to_fw(ss->gt), fw_ref);
|
|
||||||
|
|
||||||
xe_pm_runtime_put(xe);
|
|
||||||
|
|
||||||
/* Calculate devcoredump size */
|
|
||||||
ss->read.size = __xe_devcoredump_read(NULL, INT_MAX, coredump);
|
|
||||||
|
|
||||||
ss->read.buffer = kvmalloc(ss->read.size, GFP_USER);
|
|
||||||
if (!ss->read.buffer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
__xe_devcoredump_read(ss->read.buffer, ss->read.size, coredump);
|
|
||||||
xe_devcoredump_snapshot_free(ss);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
|
static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
|
||||||
size_t count, void *data, size_t datalen)
|
size_t count, void *data, size_t datalen)
|
||||||
{
|
{
|
||||||
@ -234,6 +204,45 @@ static void xe_devcoredump_free(void *data)
|
|||||||
"Xe device coredump has been deleted.\n");
|
"Xe device coredump has been deleted.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct xe_devcoredump_snapshot *ss = container_of(work, typeof(*ss), work);
|
||||||
|
struct xe_devcoredump *coredump = container_of(ss, typeof(*coredump), snapshot);
|
||||||
|
struct xe_device *xe = coredump_to_xe(coredump);
|
||||||
|
unsigned int fw_ref;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NB: Despite passing a GFP_ flags parameter here, more allocations are done
|
||||||
|
* internally using GFP_KERNEL expliictly. Hence this call must be in the worker
|
||||||
|
* thread and not in the initial capture call.
|
||||||
|
*/
|
||||||
|
dev_coredumpm_timeout(gt_to_xe(ss->gt)->drm.dev, THIS_MODULE, coredump, 0, GFP_KERNEL,
|
||||||
|
xe_devcoredump_read, xe_devcoredump_free,
|
||||||
|
XE_COREDUMP_TIMEOUT_JIFFIES);
|
||||||
|
|
||||||
|
xe_pm_runtime_get(xe);
|
||||||
|
|
||||||
|
/* keep going if fw fails as we still want to save the memory and SW data */
|
||||||
|
fw_ref = xe_force_wake_get(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL);
|
||||||
|
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
|
||||||
|
xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n");
|
||||||
|
xe_vm_snapshot_capture_delayed(ss->vm);
|
||||||
|
xe_guc_exec_queue_snapshot_capture_delayed(ss->ge);
|
||||||
|
xe_force_wake_put(gt_to_fw(ss->gt), fw_ref);
|
||||||
|
|
||||||
|
xe_pm_runtime_put(xe);
|
||||||
|
|
||||||
|
/* Calculate devcoredump size */
|
||||||
|
ss->read.size = __xe_devcoredump_read(NULL, INT_MAX, coredump);
|
||||||
|
|
||||||
|
ss->read.buffer = kvmalloc(ss->read.size, GFP_USER);
|
||||||
|
if (!ss->read.buffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
__xe_devcoredump_read(ss->read.buffer, ss->read.size, coredump);
|
||||||
|
xe_devcoredump_snapshot_free(ss);
|
||||||
|
}
|
||||||
|
|
||||||
static void devcoredump_snapshot(struct xe_devcoredump *coredump,
|
static void devcoredump_snapshot(struct xe_devcoredump *coredump,
|
||||||
struct xe_sched_job *job)
|
struct xe_sched_job *job)
|
||||||
{
|
{
|
||||||
@ -310,10 +319,6 @@ void xe_devcoredump(struct xe_sched_job *job)
|
|||||||
drm_info(&xe->drm, "Xe device coredump has been created\n");
|
drm_info(&xe->drm, "Xe device coredump has been created\n");
|
||||||
drm_info(&xe->drm, "Check your /sys/class/drm/card%d/device/devcoredump/data\n",
|
drm_info(&xe->drm, "Check your /sys/class/drm/card%d/device/devcoredump/data\n",
|
||||||
xe->drm.primary->index);
|
xe->drm.primary->index);
|
||||||
|
|
||||||
dev_coredumpm_timeout(xe->drm.dev, THIS_MODULE, coredump, 0, GFP_KERNEL,
|
|
||||||
xe_devcoredump_read, xe_devcoredump_free,
|
|
||||||
XE_COREDUMP_TIMEOUT_JIFFIES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xe_driver_devcoredump_fini(void *arg)
|
static void xe_driver_devcoredump_fini(void *arg)
|
||||||
|
@ -102,6 +102,7 @@ struct __guc_capture_parsed_output {
|
|||||||
* A 64 bit register define requires 2 consecutive entries,
|
* A 64 bit register define requires 2 consecutive entries,
|
||||||
* with low dword first and hi dword the second.
|
* with low dword first and hi dword the second.
|
||||||
* 2. Register name: null for incompleted define
|
* 2. Register name: null for incompleted define
|
||||||
|
* 3. Incorrect order will trigger XE_WARN.
|
||||||
*/
|
*/
|
||||||
#define COMMON_XELP_BASE_GLOBAL \
|
#define COMMON_XELP_BASE_GLOBAL \
|
||||||
{ FORCEWAKE_GT, REG_32BIT, 0, 0, "FORCEWAKE_GT"}
|
{ FORCEWAKE_GT, REG_32BIT, 0, 0, "FORCEWAKE_GT"}
|
||||||
@ -1675,10 +1676,10 @@ snapshot_print_by_list_order(struct xe_hw_engine_snapshot *snapshot, struct drm_
|
|||||||
struct xe_devcoredump *devcoredump = &xe->devcoredump;
|
struct xe_devcoredump *devcoredump = &xe->devcoredump;
|
||||||
struct xe_devcoredump_snapshot *devcore_snapshot = &devcoredump->snapshot;
|
struct xe_devcoredump_snapshot *devcore_snapshot = &devcoredump->snapshot;
|
||||||
struct gcap_reg_list_info *reginfo = NULL;
|
struct gcap_reg_list_info *reginfo = NULL;
|
||||||
u32 last_value, i;
|
u32 i, last_value = 0;
|
||||||
bool is_ext;
|
bool is_ext, low32_ready = false;
|
||||||
|
|
||||||
if (!list || list->num_regs == 0)
|
if (!list || !list->list || list->num_regs == 0)
|
||||||
return;
|
return;
|
||||||
XE_WARN_ON(!devcore_snapshot->matched_node);
|
XE_WARN_ON(!devcore_snapshot->matched_node);
|
||||||
|
|
||||||
@ -1701,29 +1702,75 @@ snapshot_print_by_list_order(struct xe_hw_engine_snapshot *snapshot, struct drm_
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
value = reg->value;
|
value = reg->value;
|
||||||
if (reg_desc->data_type == REG_64BIT_LOW_DW) {
|
switch (reg_desc->data_type) {
|
||||||
|
case REG_64BIT_LOW_DW:
|
||||||
last_value = value;
|
last_value = value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A 64 bit register define requires 2 consecutive
|
||||||
|
* entries in register list, with low dword first
|
||||||
|
* and hi dword the second, like:
|
||||||
|
* { XXX_REG_LO(0), REG_64BIT_LOW_DW, 0, 0, NULL},
|
||||||
|
* { XXX_REG_HI(0), REG_64BIT_HI_DW, 0, 0, "XXX_REG"},
|
||||||
|
*
|
||||||
|
* Incorrect order will trigger XE_WARN.
|
||||||
|
*
|
||||||
|
* Possible double low here, for example:
|
||||||
|
* { XXX_REG_LO(0), REG_64BIT_LOW_DW, 0, 0, NULL},
|
||||||
|
* { XXX_REG_LO(0), REG_64BIT_LOW_DW, 0, 0, NULL},
|
||||||
|
*/
|
||||||
|
XE_WARN_ON(low32_ready);
|
||||||
|
low32_ready = true;
|
||||||
/* Low 32 bit dword saved, continue for high 32 bit */
|
/* Low 32 bit dword saved, continue for high 32 bit */
|
||||||
continue;
|
break;
|
||||||
} else if (reg_desc->data_type == REG_64BIT_HI_DW) {
|
|
||||||
|
case REG_64BIT_HI_DW: {
|
||||||
u64 value_qw = ((u64)value << 32) | last_value;
|
u64 value_qw = ((u64)value << 32) | last_value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Incorrect 64bit register order. Possible missing low.
|
||||||
|
* for example:
|
||||||
|
* { XXX_REG(0), REG_32BIT, 0, 0, NULL},
|
||||||
|
* { XXX_REG_HI(0), REG_64BIT_HI_DW, 0, 0, NULL},
|
||||||
|
*/
|
||||||
|
XE_WARN_ON(!low32_ready);
|
||||||
|
low32_ready = false;
|
||||||
|
|
||||||
drm_printf(p, "\t%s: 0x%016llx\n", reg_desc->regname, value_qw);
|
drm_printf(p, "\t%s: 0x%016llx\n", reg_desc->regname, value_qw);
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_ext) {
|
case REG_32BIT:
|
||||||
int dss, group, instance;
|
/*
|
||||||
|
* Incorrect 64bit register order. Possible missing high.
|
||||||
|
* for example:
|
||||||
|
* { XXX_REG_LO(0), REG_64BIT_LOW_DW, 0, 0, NULL},
|
||||||
|
* { XXX_REG(0), REG_32BIT, 0, 0, "XXX_REG"},
|
||||||
|
*/
|
||||||
|
XE_WARN_ON(low32_ready);
|
||||||
|
|
||||||
group = FIELD_GET(GUC_REGSET_STEERING_GROUP, reg_desc->flags);
|
if (is_ext) {
|
||||||
instance = FIELD_GET(GUC_REGSET_STEERING_INSTANCE, reg_desc->flags);
|
int dss, group, instance;
|
||||||
dss = xe_gt_mcr_steering_info_to_dss_id(gt, group, instance);
|
|
||||||
|
|
||||||
drm_printf(p, "\t%s[%u]: 0x%08x\n", reg_desc->regname, dss, value);
|
group = FIELD_GET(GUC_REGSET_STEERING_GROUP, reg_desc->flags);
|
||||||
} else {
|
instance = FIELD_GET(GUC_REGSET_STEERING_INSTANCE, reg_desc->flags);
|
||||||
drm_printf(p, "\t%s: 0x%08x\n", reg_desc->regname, value);
|
dss = xe_gt_mcr_steering_info_to_dss_id(gt, group, instance);
|
||||||
|
|
||||||
|
drm_printf(p, "\t%s[%u]: 0x%08x\n", reg_desc->regname, dss, value);
|
||||||
|
} else {
|
||||||
|
drm_printf(p, "\t%s: 0x%08x\n", reg_desc->regname, value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Incorrect 64bit register order. Possible missing high.
|
||||||
|
* for example:
|
||||||
|
* { XXX_REG_LO(0), REG_64BIT_LOW_DW, 0, 0, NULL},
|
||||||
|
* } // <- Register list end
|
||||||
|
*/
|
||||||
|
XE_WARN_ON(low32_ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -699,6 +699,13 @@ struct drm_dp_mst_topology_mgr {
|
|||||||
*/
|
*/
|
||||||
bool payload_id_table_cleared : 1;
|
bool payload_id_table_cleared : 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reset_rx_state: The down request's reply and up request message
|
||||||
|
* receiver state must be reset, after the topology manager got
|
||||||
|
* removed. Protected by @lock.
|
||||||
|
*/
|
||||||
|
bool reset_rx_state : 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @payload_count: The number of currently active payloads in hardware. This value is only
|
* @payload_count: The number of currently active payloads in hardware. This value is only
|
||||||
* intended to be used internally by MST helpers for payload tracking, and is only safe to
|
* intended to be used internally by MST helpers for payload tracking, and is only safe to
|
||||||
|
Loading…
Reference in New Issue
Block a user