mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
Merge branch 'eth-fbnic-update-bmc-mac-address-handling'
Alexander Duyck says: ==================== eth: fbnic: Update BMC MAC address handling Update the handling of BMC MAC addresses on fbnic, and in the process also address several issues identified with the changes. This patch series consists of 2 patches. The first patch addresses issues found in the bmc_present and bmc_tcam_reinit handling. It started out addressing the fact that present w/o a MAC address was flagged as an invalid message. However, we ended up also addressing a potential race in that setting bmc_present and need_bmc_tcam_reinit could potentially race against the bmc_rpc_check. To address that the bit flags were moved to a state bitmap and are now handled via the test_and_clear_bit and assign_bit operations. The second patch cleans up the BMC MAC addresses in the TCAMs if the BMC opts to disable the channel and the FW notifies us of the BMC removal. By doing this we can terminate the now defunct paths through our NIC data path to the BMC. Similarly with this a potential race was found due to recent changes that had moved the handling for the TCAMs out of the RTNL mutex and into the netdev instance lock. To maintain sync additional locking was added. ==================== Link: https://patch.msgid.link/179061185153.414394.13396934246780284806.stgit@ahduyck-xeon-server.home.arpa Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
7efc07ad22
|
|
@ -161,7 +161,12 @@ void fbnic_fw_wr32(struct fbnic_dev *fbd, u32 reg, u32 val);
|
|||
|
||||
static inline bool fbnic_bmc_present(struct fbnic_dev *fbd)
|
||||
{
|
||||
return fbd->fw_cap.bmc_present;
|
||||
return test_bit(FBNIC_FW_CAP_F_BMC_PRESENT, &fbd->fw_cap.state);
|
||||
}
|
||||
|
||||
static inline bool fbnic_bmc_all_multi(struct fbnic_dev *fbd)
|
||||
{
|
||||
return test_bit(FBNIC_FW_CAP_F_BMC_ALL_MULTI, &fbd->fw_cap.state);
|
||||
}
|
||||
|
||||
static inline bool fbnic_init_failure(struct fbnic_dev *fbd)
|
||||
|
|
|
|||
|
|
@ -620,11 +620,55 @@ static int fbnic_fw_parse_bmc_addrs(u8 bmc_mac_addr[][ETH_ALEN],
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fbnic_fw_parse_bmc_cap(struct fbnic_dev *fbd,
|
||||
struct fbnic_tlv_msg **results)
|
||||
{
|
||||
struct fbnic_tlv_msg *attr;
|
||||
bool bmc_present = false;
|
||||
u32 all_multi = 0;
|
||||
int err;
|
||||
|
||||
/* FW reports the BMC present once its NC-SI channel is enabled, before
|
||||
* it has a MAC. No MAC array means nothing to program, so treat it as
|
||||
* absent rather than rejecting the message.
|
||||
*/
|
||||
attr = results[FBNIC_FW_CAP_RESP_BMC_PRESENT] ?
|
||||
results[FBNIC_FW_CAP_RESP_BMC_MAC_ARRAY] : NULL;
|
||||
if (attr) {
|
||||
err = fbnic_fw_parse_bmc_addrs(fbd->fw_cap.bmc_mac_addr,
|
||||
attr, 4);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
all_multi = fta_get_uint(results,
|
||||
FBNIC_FW_CAP_RESP_BMC_ALL_MULTI);
|
||||
bmc_present = true;
|
||||
} else {
|
||||
memset(fbd->fw_cap.bmc_mac_addr, 0,
|
||||
sizeof(fbd->fw_cap.bmc_mac_addr));
|
||||
}
|
||||
|
||||
/* all_multi is only read out of the message when a BMC is present, so
|
||||
* it is never set on its own and callers can test it without pairing
|
||||
* it with the presence check.
|
||||
*/
|
||||
assign_bit(FBNIC_FW_CAP_F_BMC_PRESENT, &fbd->fw_cap.state, bmc_present);
|
||||
assign_bit(FBNIC_FW_CAP_F_BMC_ALL_MULTI, &fbd->fw_cap.state, all_multi);
|
||||
|
||||
/* Always assume we need a BMC reinit. The barrier orders the BMC state
|
||||
* published above ahead of the flag, pairing with the ordering implied
|
||||
* by the test_and_clear_bit() in fbnic_bmc_rpc_check().
|
||||
*/
|
||||
smp_mb__before_atomic();
|
||||
set_bit(FBNIC_FW_CAP_F_BMC_TCAM_REINIT, &fbd->fw_cap.state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fbnic_fw_parse_cap_resp(void *opaque, struct fbnic_tlv_msg **results)
|
||||
{
|
||||
u32 all_multi = 0, version = 0;
|
||||
struct fbnic_dev *fbd = opaque;
|
||||
bool bmc_present;
|
||||
u32 version = 0;
|
||||
int err;
|
||||
|
||||
version = fta_get_uint(results, FBNIC_FW_CAP_RESP_VERSION);
|
||||
|
|
@ -684,37 +728,13 @@ static int fbnic_fw_parse_cap_resp(void *opaque, struct fbnic_tlv_msg **results)
|
|||
fbd->fw_cap.link_fec =
|
||||
fta_get_uint(results, FBNIC_FW_CAP_RESP_FW_LINK_FEC);
|
||||
|
||||
bmc_present = !!results[FBNIC_FW_CAP_RESP_BMC_PRESENT];
|
||||
if (bmc_present) {
|
||||
struct fbnic_tlv_msg *attr;
|
||||
|
||||
attr = results[FBNIC_FW_CAP_RESP_BMC_MAC_ARRAY];
|
||||
if (!attr)
|
||||
return -EINVAL;
|
||||
|
||||
err = fbnic_fw_parse_bmc_addrs(fbd->fw_cap.bmc_mac_addr,
|
||||
attr, 4);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
all_multi =
|
||||
fta_get_uint(results, FBNIC_FW_CAP_RESP_BMC_ALL_MULTI);
|
||||
} else {
|
||||
memset(fbd->fw_cap.bmc_mac_addr, 0,
|
||||
sizeof(fbd->fw_cap.bmc_mac_addr));
|
||||
}
|
||||
|
||||
fbd->fw_cap.bmc_present = bmc_present;
|
||||
|
||||
if (results[FBNIC_FW_CAP_RESP_BMC_ALL_MULTI] || !bmc_present)
|
||||
fbd->fw_cap.all_multi = all_multi;
|
||||
err = fbnic_fw_parse_bmc_cap(fbd, results);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fbd->fw_cap.anti_rollback_version =
|
||||
fta_get_uint(results, FBNIC_FW_CAP_RESP_ANTI_ROLLBACK_VERSION);
|
||||
|
||||
/* Always assume we need a BMC reinit */
|
||||
fbd->fw_cap.need_bmc_tcam_reinit = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,22 @@ struct fbnic_fw_ver {
|
|||
char commit[FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE];
|
||||
};
|
||||
|
||||
/* Bits in fbnic_fw_cap.state. The firmware mailbox publishes the BMC state
|
||||
* and the service task consumes it, so they are updated with atomic bitops.
|
||||
*/
|
||||
enum {
|
||||
FBNIC_FW_CAP_F_BMC_PRESENT,
|
||||
FBNIC_FW_CAP_F_BMC_ALL_MULTI,
|
||||
FBNIC_FW_CAP_F_BMC_TCAM_REINIT,
|
||||
FBNIC_FW_CAP_F_BMC_MACDA_SYNC,
|
||||
};
|
||||
|
||||
#define FBNIC_FW_CAP_BMC_PENDING \
|
||||
(BIT(FBNIC_FW_CAP_F_BMC_TCAM_REINIT) | \
|
||||
BIT(FBNIC_FW_CAP_F_BMC_MACDA_SYNC))
|
||||
|
||||
struct fbnic_fw_cap {
|
||||
unsigned long state;
|
||||
struct {
|
||||
struct fbnic_fw_ver mgmt, bootloader;
|
||||
} running;
|
||||
|
|
@ -54,10 +69,6 @@ struct fbnic_fw_cap {
|
|||
} stored;
|
||||
u8 active_slot;
|
||||
u8 bmc_mac_addr[4][ETH_ALEN];
|
||||
u8 bmc_present : 1;
|
||||
u8 need_bmc_tcam_reinit : 1;
|
||||
u8 need_bmc_macda_sync : 1;
|
||||
u8 all_multi : 1;
|
||||
u8 link_speed;
|
||||
u8 link_fec;
|
||||
u32 anti_rollback_version;
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ void fbnic_bmc_rpc_all_multi_config(struct fbnic_dev *fbd,
|
|||
* BMC.
|
||||
*/
|
||||
mac_addr = &fbd->mac_addr[fbd->mac_addr_boundary - 1];
|
||||
if (fbnic_bmc_present(fbd) && fbd->fw_cap.all_multi) {
|
||||
if (fbnic_bmc_all_multi(fbd)) {
|
||||
if (mac_addr->state != FBNIC_TCAM_S_VALID) {
|
||||
eth_zero_addr(mac_addr->value.addr8);
|
||||
eth_broadcast_addr(mac_addr->mask.addr8);
|
||||
|
|
@ -143,9 +143,11 @@ void fbnic_bmc_rpc_all_multi_config(struct fbnic_dev *fbd,
|
|||
|
||||
/* If we are not enabling the rule just delete it. We will fall
|
||||
* back to the RSS rules that support the multicast addresses.
|
||||
* Delete in any live state, not just VALID, as ifdown demotes it to
|
||||
* UPDATE and a miss there leaves it to be rewritten to hardware.
|
||||
*/
|
||||
if (!fbnic_bmc_present(fbd) || !fbd->fw_cap.all_multi || enable_host) {
|
||||
if (act_tcam->state == FBNIC_TCAM_S_VALID)
|
||||
if (!fbnic_bmc_all_multi(fbd) || enable_host) {
|
||||
if (act_tcam->state != FBNIC_TCAM_S_DISABLED)
|
||||
act_tcam->state = FBNIC_TCAM_S_DELETE;
|
||||
return;
|
||||
}
|
||||
|
|
@ -234,25 +236,121 @@ void fbnic_bmc_rpc_init(struct fbnic_dev *fbd)
|
|||
act_tcam->state = FBNIC_TCAM_S_UPDATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* fbnic_bmc_rules_present - is the BMC currently programmed into the filters?
|
||||
* @fbd: Pointer to fbnic device struct
|
||||
*
|
||||
* The BMC tag is only set while a BMC is present, so it doubles as the state.
|
||||
*
|
||||
* Return: true if any MACDA entry carries the BMC tag, false otherwise.
|
||||
*/
|
||||
static bool fbnic_bmc_rules_present(struct fbnic_dev *fbd)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = ARRAY_SIZE(fbd->mac_addr); idx--;) {
|
||||
struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[idx];
|
||||
|
||||
if (mac_addr->state == FBNIC_TCAM_S_DISABLED)
|
||||
continue;
|
||||
|
||||
if (test_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* fbnic_bmc_rpc_disable - remove the BMC MAC and action rules
|
||||
* @fbd: Pointer to fbnic device struct
|
||||
*
|
||||
* Undo fbnic_bmc_rpc_init() when the BMC drops its NC-SI channel while the
|
||||
* host interface stays up. Only marks shadow state; the caller's
|
||||
* __fbnic_set_rx_mode() pushes it to hardware, so ordering here is moot.
|
||||
*/
|
||||
static void fbnic_bmc_rpc_disable(struct fbnic_dev *fbd)
|
||||
{
|
||||
struct fbnic_act_tcam *act_tcam;
|
||||
int idx;
|
||||
|
||||
/* Drop the BMC's claim; entries the host also uses survive */
|
||||
for (idx = ARRAY_SIZE(fbd->mac_addr); idx--;) {
|
||||
struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[idx];
|
||||
|
||||
if (mac_addr->state == FBNIC_TCAM_S_DISABLED)
|
||||
continue;
|
||||
|
||||
__fbnic_xc_unsync(mac_addr, FBNIC_MAC_ADDR_T_BMC);
|
||||
}
|
||||
|
||||
/* Delete in any live state, not just VALID: init leaves it UPDATE and
|
||||
* ifdown demotes VALID to UPDATE. A miss leaks the rule for good, as
|
||||
* fbnic_bmc_rules_present() keys off the MACDA tag just cleared above.
|
||||
*/
|
||||
act_tcam = &fbd->act_tcam[FBNIC_RPC_ACT_TBL_BMC_OFFSET];
|
||||
if (act_tcam->state != FBNIC_TCAM_S_DISABLED)
|
||||
act_tcam->state = FBNIC_TCAM_S_DELETE;
|
||||
|
||||
/* fbnic_rss_reinit() only programs the host-unicast entries while a BMC
|
||||
* is present, so the reinit after this would leave them stale and valid
|
||||
* in hardware. Delete them to converge on the no-BMC layout.
|
||||
*/
|
||||
for (idx = 0; idx < FBNIC_RSS_EN_NUM_UNICAST; idx++) {
|
||||
act_tcam = &fbd->act_tcam[FBNIC_RPC_ACT_TBL_RSS_OFFSET + idx];
|
||||
if (act_tcam->state != FBNIC_TCAM_S_DISABLED)
|
||||
act_tcam->state = FBNIC_TCAM_S_DELETE;
|
||||
}
|
||||
}
|
||||
|
||||
void fbnic_bmc_rpc_check(struct fbnic_dev *fbd)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(fbd->netdev);
|
||||
int err;
|
||||
|
||||
if (fbd->fw_cap.need_bmc_tcam_reinit) {
|
||||
fbnic_bmc_rpc_init(fbd);
|
||||
/* Nothing to do unless the firmware raised one of the flags. Test
|
||||
* before taking the lock to keep it off the common path; one raised
|
||||
* after this is picked up on the next pass.
|
||||
*/
|
||||
if (!(READ_ONCE(fbd->fw_cap.state) & FBNIC_FW_CAP_BMC_PENDING))
|
||||
return;
|
||||
|
||||
/* The rx mode work and the ethtool paths rewrite the MACDA and action
|
||||
* TCAM shadows under the instance lock, and this runs from the service
|
||||
* task under RTNL only, so the two do not exclude each other.
|
||||
*/
|
||||
netdev_lock(fbd->netdev);
|
||||
|
||||
/* Consume the flag before the state it advertises. The ordering
|
||||
* implied by test_and_clear_bit() pairs with the barrier in
|
||||
* fbnic_fw_parse_bmc_cap(), and claiming it atomically means a set
|
||||
* racing with us is kept and retried rather than overwritten.
|
||||
*/
|
||||
if (test_and_clear_bit(FBNIC_FW_CAP_F_BMC_TCAM_REINIT,
|
||||
&fbd->fw_cap.state)) {
|
||||
if (!fbnic_bmc_present(fbd) && fbnic_bmc_rules_present(fbd))
|
||||
fbnic_bmc_rpc_disable(fbd);
|
||||
else
|
||||
fbnic_bmc_rpc_init(fbd);
|
||||
|
||||
/* Neither path touches the RSS actions, which carry the mc/bc
|
||||
* copy to the BMC, so recompute them alongside.
|
||||
*/
|
||||
fbnic_rss_reinit(fbd, fbn);
|
||||
netif_addr_lock_bh(fbd->netdev);
|
||||
__fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc);
|
||||
netif_addr_unlock_bh(fbd->netdev);
|
||||
fbd->fw_cap.need_bmc_tcam_reinit = false;
|
||||
}
|
||||
|
||||
if (fbd->fw_cap.need_bmc_macda_sync) {
|
||||
if (test_and_clear_bit(FBNIC_FW_CAP_F_BMC_MACDA_SYNC,
|
||||
&fbd->fw_cap.state)) {
|
||||
err = fbnic_fw_xmit_rpc_macda_sync(fbd);
|
||||
if (err)
|
||||
dev_warn(fbd->dev,
|
||||
"Writing MACDA table to FW failed, err: %d\n", err);
|
||||
fbd->fw_cap.need_bmc_macda_sync = false;
|
||||
}
|
||||
|
||||
netdev_unlock(fbd->netdev);
|
||||
}
|
||||
|
||||
#define FBNIC_ACT1_INIT(_l4, _udp, _ip, _v6) \
|
||||
|
|
@ -484,8 +582,7 @@ void fbnic_promisc_sync(struct fbnic_dev *fbd,
|
|||
mac_addr->act_tcam);
|
||||
mac_addr->state = FBNIC_TCAM_S_ADD;
|
||||
}
|
||||
} else if (mc_promisc &&
|
||||
(!fbnic_bmc_present(fbd) || !fbd->fw_cap.all_multi)) {
|
||||
} else if (mc_promisc && !fbnic_bmc_all_multi(fbd)) {
|
||||
/* We have to add a special handler for multicast as the
|
||||
* BMC may have an all-multi rule already in place. As such
|
||||
* adding a rule ourselves won't do any good so we will have
|
||||
|
|
@ -656,12 +753,12 @@ void fbnic_write_macda(struct fbnic_dev *fbd)
|
|||
}
|
||||
|
||||
/* If reinitializing the BMC TCAM we are doing an initial update */
|
||||
if (fbd->fw_cap.need_bmc_tcam_reinit)
|
||||
if (test_bit(FBNIC_FW_CAP_F_BMC_TCAM_REINIT, &fbd->fw_cap.state))
|
||||
updates++;
|
||||
|
||||
/* If needed notify firmware of changes to MACDA TCAM */
|
||||
if (updates != 0 && fbnic_bmc_present(fbd))
|
||||
fbd->fw_cap.need_bmc_macda_sync = true;
|
||||
set_bit(FBNIC_FW_CAP_F_BMC_MACDA_SYNC, &fbd->fw_cap.state);
|
||||
}
|
||||
|
||||
static void fbnic_clear_act_tcam(struct fbnic_dev *fbd, unsigned int idx)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user