Commit bcacfcbc authored by Julian Wiedmann's avatar Julian Wiedmann Committed by David S. Miller

s390/qeth: fix MAC address update sequence

When changing the MAC address on a L2 qeth device, current code first
unregisters the old address, then registers the new one.
If HW rejects the new address (or the IO fails), the device ends up with
no operable address at all.

Re-order the code flow so that the old address only gets dropped if the
new address was registered successfully. While at it, add logic to catch
some corner-cases.
Signed-off-by: default avatarJulian Wiedmann <jwi@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a936b1ef
...@@ -121,13 +121,10 @@ static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac) ...@@ -121,13 +121,10 @@ static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
QETH_CARD_TEXT(card, 2, "L2Setmac"); QETH_CARD_TEXT(card, 2, "L2Setmac");
rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC); rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC);
if (rc == 0) { if (rc == 0) {
card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
ether_addr_copy(card->dev->dev_addr, mac);
dev_info(&card->gdev->dev, dev_info(&card->gdev->dev,
"MAC address %pM successfully registered on device %s\n", "MAC address %pM successfully registered on device %s\n",
card->dev->dev_addr, card->dev->name); mac, card->dev->name);
} else { } else {
card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
switch (rc) { switch (rc) {
case -EEXIST: case -EEXIST:
dev_warn(&card->gdev->dev, dev_warn(&card->gdev->dev,
...@@ -142,19 +139,6 @@ static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac) ...@@ -142,19 +139,6 @@ static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac)
return rc; return rc;
} }
static int qeth_l2_send_delmac(struct qeth_card *card, __u8 *mac)
{
int rc;
QETH_CARD_TEXT(card, 2, "L2Delmac");
if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
return 0;
rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELVMAC);
if (rc == 0)
card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
return rc;
}
static int qeth_l2_write_mac(struct qeth_card *card, u8 *mac) static int qeth_l2_write_mac(struct qeth_card *card, u8 *mac)
{ {
enum qeth_ipa_cmds cmd = is_multicast_ether_addr_64bits(mac) ? enum qeth_ipa_cmds cmd = is_multicast_ether_addr_64bits(mac) ?
...@@ -519,6 +503,7 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p) ...@@ -519,6 +503,7 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
{ {
struct sockaddr *addr = p; struct sockaddr *addr = p;
struct qeth_card *card = dev->ml_priv; struct qeth_card *card = dev->ml_priv;
u8 old_addr[ETH_ALEN];
int rc = 0; int rc = 0;
QETH_CARD_TEXT(card, 3, "setmac"); QETH_CARD_TEXT(card, 3, "setmac");
...@@ -530,14 +515,35 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p) ...@@ -530,14 +515,35 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
QETH_CARD_HEX(card, 3, addr->sa_data, ETH_ALEN); QETH_CARD_HEX(card, 3, addr->sa_data, ETH_ALEN);
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) {
QETH_CARD_TEXT(card, 3, "setmcREC"); QETH_CARD_TEXT(card, 3, "setmcREC");
return -ERESTARTSYS; return -ERESTARTSYS;
} }
rc = qeth_l2_send_delmac(card, &card->dev->dev_addr[0]);
if (!rc || (rc == -ENOENT)) if (!qeth_card_hw_is_reachable(card)) {
ether_addr_copy(dev->dev_addr, addr->sa_data);
return 0;
}
/* don't register the same address twice */
if (ether_addr_equal_64bits(dev->dev_addr, addr->sa_data) &&
(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
return 0;
/* add the new address, switch over, drop the old */
rc = qeth_l2_send_setmac(card, addr->sa_data); rc = qeth_l2_send_setmac(card, addr->sa_data);
return rc ? -EINVAL : 0; if (rc)
return rc;
ether_addr_copy(old_addr, dev->dev_addr);
ether_addr_copy(dev->dev_addr, addr->sa_data);
if (card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED)
qeth_l2_remove_mac(card, old_addr);
card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
return 0;
} }
static void qeth_promisc_to_bridge(struct qeth_card *card) static void qeth_promisc_to_bridge(struct qeth_card *card)
...@@ -1067,8 +1073,9 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) ...@@ -1067,8 +1073,9 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
goto out_remove; goto out_remove;
} }
if (card->info.type != QETH_CARD_TYPE_OSN) if (card->info.type != QETH_CARD_TYPE_OSN &&
qeth_l2_send_setmac(card, &card->dev->dev_addr[0]); !qeth_l2_send_setmac(card, card->dev->dev_addr))
card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) {
if (card->info.hwtrap && if (card->info.hwtrap &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment