Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: dsa: lantiq_gswip: serialize access to the PCE registers

The GSWIP switch accesses various bridging layer tables (VLANs, FDBs,
forwarding rules) indirectly through PCE registers. These hardware
accesses are non-atomic, being comprised of several register reads and
writes.

These accesses are currently serialized by the rtnl_lock, but DSA is
changing its driver API and that lock will no longer be held when
calling ->port_fdb_add() and ->port_fdb_del().

So this driver needs to serialize the access to the PCE registers using
its own locking scheme. This patch adds that.

Note that the driver also uses the gswip_pce_load_microcode() function
to load a static configuration for the packet classification engine into
a table using the same registers. It is currently not protected, but
since that configuration is only done from the dsa_switch_ops :: setup
method, there is no risk of it being concurrent with other operations.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Acked-by: Hauke Mehrtens <hauke@hauke-m.de>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Vladimir Oltean and committed by
David S. Miller
cf231b43 f7eb4a1c

+23 -5
+23 -5
drivers/net/dsa/lantiq_gswip.c
··· 276 276 int num_gphy_fw; 277 277 struct gswip_gphy_fw *gphy_fw; 278 278 u32 port_vlan_filter; 279 + struct mutex pce_table_lock; 279 280 }; 280 281 281 282 struct gswip_pce_table_entry { ··· 524 523 u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSRD : 525 524 GSWIP_PCE_TBL_CTRL_OPMOD_ADRD; 526 525 526 + mutex_lock(&priv->pce_table_lock); 527 + 527 528 err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL, 528 529 GSWIP_PCE_TBL_CTRL_BAS); 529 - if (err) 530 + if (err) { 531 + mutex_unlock(&priv->pce_table_lock); 530 532 return err; 533 + } 531 534 532 535 gswip_switch_w(priv, tbl->index, GSWIP_PCE_TBL_ADDR); 533 536 gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK | ··· 541 536 542 537 err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL, 543 538 GSWIP_PCE_TBL_CTRL_BAS); 544 - if (err) 539 + if (err) { 540 + mutex_unlock(&priv->pce_table_lock); 545 541 return err; 542 + } 546 543 547 544 for (i = 0; i < ARRAY_SIZE(tbl->key); i++) 548 545 tbl->key[i] = gswip_switch_r(priv, GSWIP_PCE_TBL_KEY(i)); ··· 560 553 tbl->valid = !!(crtl & GSWIP_PCE_TBL_CTRL_VLD); 561 554 tbl->gmap = (crtl & GSWIP_PCE_TBL_CTRL_GMAP_MASK) >> 7; 562 555 556 + mutex_unlock(&priv->pce_table_lock); 557 + 563 558 return 0; 564 559 } 565 560 ··· 574 565 u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSWR : 575 566 GSWIP_PCE_TBL_CTRL_OPMOD_ADWR; 576 567 568 + mutex_lock(&priv->pce_table_lock); 569 + 577 570 err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL, 578 571 GSWIP_PCE_TBL_CTRL_BAS); 579 - if (err) 572 + if (err) { 573 + mutex_unlock(&priv->pce_table_lock); 580 574 return err; 575 + } 581 576 582 577 gswip_switch_w(priv, tbl->index, GSWIP_PCE_TBL_ADDR); 583 578 gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK | ··· 613 600 crtl |= GSWIP_PCE_TBL_CTRL_BAS; 614 601 gswip_switch_w(priv, crtl, GSWIP_PCE_TBL_CTRL); 615 602 616 - return gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL, 617 - GSWIP_PCE_TBL_CTRL_BAS); 603 + err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL, 604 + GSWIP_PCE_TBL_CTRL_BAS); 605 + 606 + mutex_unlock(&priv->pce_table_lock); 607 + 608 + return err; 618 609 } 619 610 620 611 /* Add the LAN port into a bridge with the CPU port by ··· 2121 2104 priv->ds->priv = priv; 2122 2105 priv->ds->ops = priv->hw_info->ops; 2123 2106 priv->dev = dev; 2107 + mutex_init(&priv->pce_table_lock); 2124 2108 version = gswip_switch_r(priv, GSWIP_VERSION); 2125 2109 2126 2110 np = dev->of_node;