Skip to content

Commit

Permalink
staging: fsl_qbman: don't dereference portal affine to CPU when it's …
Browse files Browse the repository at this point in the history
…redirected

When using a cmdline such as "bportals=s0 qportals=s0", Linux is given a
single QMan and a single BMan portal which is shared among all CPUs, and
accessed with locking.

This is only supported for the staging SDK QBMan driver and not for the
upstream variant.

In a strange twist of events, qman_create_affine_slave() also sets
affine_portals[] for CPUs which use the portal affine to a different CPU
(aka "slaves" here), and just have portal->sharing_redirect set to that
other portal.

But that panics the kernel hard, because these dummy portals, not
having been created by qman_create_portal(), have uninitialized struct
qm_portal :: addr, eqcr, dqrr, etc, but also portal->config. So any time
these are dereferenced, the kernel panics.

There are actually 2 code paths which are in this situation:

qman_enable_irqs()
-> qm_isr_status_clear()
   -> __qm_isr_write()
      -> __qm_out(&portal->addr, ...) // portal->addr uninitialized

qm_shutdown_fq()
-> qm_get_portal_for_channel()
   -> qman_p_get_portal_config()
      -> &p->config->public_cfg // p->config uninitialized

Both functions were actually copied over from the upstream QBMan driver
(for the purpose of kexec support), which does not support portal
sharing and thus the problem does not exist there.

Actually, we need to take into consideration in these code paths only
those affine portals created by qman_create_affine_portal(), and not the
fake ones with sharing_redirect. The qman_create_affine_portal() sets
the CPU in the &affine_mask retrievable through qman_affine_cpus().

This is also the way in which dpaa_eth_add_channel() from
drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.c avoids the
fake channels, when dereferencing the affine_cpus[] array through the
qman_get_affine_portal() API method.

Fixes: a218c90 ("staging: fsl_qbman: account for pre-initialized BARs in case of kexec")
Fixes: 78ff3aa0713b ("staging: fsl_qbman: use correct portal for static dequeues in qm_shutdown_fq()")
Signed-off-by: Vladimir Oltean <[email protected]>
  • Loading branch information
vladimiroltean committed Nov 19, 2024
1 parent 2abe654 commit e0f9e2a
Showing 1 changed file with 9 additions and 7 deletions.
16 changes: 9 additions & 7 deletions drivers/staging/fsl_qbman/qman_high.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,12 +449,13 @@ static inline void hw_ccgr_query_to_cpu(struct qm_mcr_ceetm_ccgr_query *ccgr_q)

void qman_enable_irqs(void)
{
const cpumask_t *cpus = qman_affine_cpus();
struct qman_portal *p;
int i;
int cpu;

for (i = 0; i < NR_CPUS; i++) {
if (affine_portals[i]) {
p = (struct qman_portal *)affine_portals[i];
for_each_cpu(cpu, cpus) {
p = affine_portals[cpu];
if (p) {
qm_isr_status_clear(&p->p, 0xffffffff);
qm_isr_uninhibit(&p->p);
}
Expand Down Expand Up @@ -928,12 +929,13 @@ EXPORT_SYMBOL(qman_get_portal_config);

struct qm_portal *qm_get_portal_for_channel(u16 channel)
{
const cpumask_t *cpus = qman_affine_cpus();
const struct qman_portal_config *pcfg;
struct qman_portal *p;
int i;
int cpu;

for (i = 0; i < num_possible_cpus(); i++) {
p = (struct qman_portal *)affine_portals[i];
for_each_cpu(cpu, cpus) {
p = affine_portals[cpu];
if (!p)
continue;

Expand Down

0 comments on commit e0f9e2a

Please sign in to comment.