Commit aa8f2cbd authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 's390-qeth-updates-2020-11-17'

Julian Wiedmann says:

====================
s390/qeth: updates 2020-11-17

This brings some cleanups, and a bunch of improvements for our
.get_link_ksettings() code.
====================

Link: https://lore.kernel.org/r/20201117161520.1089-1-jwi@linux.ibm.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents bd4bdeb4 235db527
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -701,6 +701,19 @@ enum qeth_pnso_mode {
	QETH_PNSO_ADDR_INFO,
};

enum qeth_link_mode {
	QETH_LINK_MODE_UNKNOWN,
	QETH_LINK_MODE_FIBRE_SHORT,
	QETH_LINK_MODE_FIBRE_LONG,
};

struct qeth_link_info {
	u32 speed;
	u8 duplex;
	u8 port;
	enum qeth_link_mode link_mode;
};

#define QETH_BROADCAST_WITH_ECHO    0x01
#define QETH_BROADCAST_WITHOUT_ECHO 0x02
struct qeth_card_info {
@@ -732,6 +745,7 @@ struct qeth_card_info {
	struct qeth_card_blkt blkt;
	__u32 diagass_support;
	__u32 hwtrap;
	struct qeth_link_info link_info;
};

enum qeth_discipline_id {
@@ -796,12 +810,6 @@ struct qeth_rx {
	u8 bufs_refill;
};

struct carrier_info {
	__u8  card_type;
	__u16 port_mode;
	__u32 port_speed;
};

struct qeth_switch_info {
	__u32 capabilities;
	__u32 settings;
@@ -1108,7 +1116,7 @@ void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
int qeth_query_switch_attributes(struct qeth_card *card,
				  struct qeth_switch_info *sw_info);
int qeth_query_card_info(struct qeth_card *card,
			 struct carrier_info *carrier_info);
			 struct qeth_link_info *link_info);
int qeth_setadpparms_set_access_ctrl(struct qeth_card *card,
				     enum qeth_ipa_isolation_modes mode);

+216 −7
Original line number Diff line number Diff line
@@ -4868,8 +4868,8 @@ static int qeth_query_oat_command(struct qeth_card *card, char __user *udata)
static int qeth_query_card_info_cb(struct qeth_card *card,
				   struct qeth_reply *reply, unsigned long data)
{
	struct carrier_info *carrier_info = (struct carrier_info *)reply->param;
	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
	struct qeth_link_info *link_info = reply->param;
	struct qeth_query_card_info *card_info;

	QETH_CARD_TEXT(card, 2, "qcrdincb");
@@ -4877,14 +4877,67 @@ static int qeth_query_card_info_cb(struct qeth_card *card,
		return -EIO;

	card_info = &cmd->data.setadapterparms.data.card_info;
	carrier_info->card_type = card_info->card_type;
	carrier_info->port_mode = card_info->port_mode;
	carrier_info->port_speed = card_info->port_speed;
	netdev_dbg(card->dev,
		   "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n",
		   card_info->card_type, card_info->port_mode,
		   card_info->port_speed);

	switch (card_info->port_mode) {
	case CARD_INFO_PORTM_FULLDUPLEX:
		link_info->duplex = DUPLEX_FULL;
		break;
	case CARD_INFO_PORTM_HALFDUPLEX:
		link_info->duplex = DUPLEX_HALF;
		break;
	default:
		link_info->duplex = DUPLEX_UNKNOWN;
	}

	switch (card_info->card_type) {
	case CARD_INFO_TYPE_1G_COPPER_A:
	case CARD_INFO_TYPE_1G_COPPER_B:
		link_info->speed = SPEED_1000;
		link_info->port = PORT_TP;
		break;
	case CARD_INFO_TYPE_1G_FIBRE_A:
	case CARD_INFO_TYPE_1G_FIBRE_B:
		link_info->speed = SPEED_1000;
		link_info->port = PORT_FIBRE;
		break;
	case CARD_INFO_TYPE_10G_FIBRE_A:
	case CARD_INFO_TYPE_10G_FIBRE_B:
		link_info->speed = SPEED_10000;
		link_info->port = PORT_FIBRE;
		break;
	default:
		switch (card_info->port_speed) {
		case CARD_INFO_PORTS_10M:
			link_info->speed = SPEED_10;
			break;
		case CARD_INFO_PORTS_100M:
			link_info->speed = SPEED_100;
			break;
		case CARD_INFO_PORTS_1G:
			link_info->speed = SPEED_1000;
			break;
		case CARD_INFO_PORTS_10G:
			link_info->speed = SPEED_10000;
			break;
		case CARD_INFO_PORTS_25G:
			link_info->speed = SPEED_25000;
			break;
		default:
			link_info->speed = SPEED_UNKNOWN;
		}

		link_info->port = PORT_OTHER;
	}

	return 0;
}

int qeth_query_card_info(struct qeth_card *card,
			 struct carrier_info *carrier_info)
			 struct qeth_link_info *link_info)
{
	struct qeth_cmd_buffer *iob;

@@ -4894,8 +4947,162 @@ int qeth_query_card_info(struct qeth_card *card,
	iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, 0);
	if (!iob)
		return -ENOMEM;
	return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb,
					(void *)carrier_info);

	return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, link_info);
}

static int qeth_init_link_info_oat_cb(struct qeth_card *card,
				      struct qeth_reply *reply_priv,
				      unsigned long data)
{
	struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *)data;
	struct qeth_link_info *link_info = reply_priv->param;
	struct qeth_query_oat_physical_if *phys_if;
	struct qeth_query_oat_reply *reply;

	if (qeth_setadpparms_inspect_rc(cmd))
		return -EIO;

	/* Multi-part reply is unexpected, don't bother: */
	if (cmd->data.setadapterparms.hdr.used_total > 1)
		return -EINVAL;

	/* Expect the reply to start with phys_if data: */
	reply = &cmd->data.setadapterparms.data.query_oat.reply[0];
	if (reply->type != QETH_QOAT_REPLY_TYPE_PHYS_IF ||
	    reply->length < sizeof(*reply))
		return -EINVAL;

	phys_if = &reply->phys_if;

	switch (phys_if->speed_duplex) {
	case QETH_QOAT_PHYS_SPEED_10M_HALF:
		link_info->speed = SPEED_10;
		link_info->duplex = DUPLEX_HALF;
		break;
	case QETH_QOAT_PHYS_SPEED_10M_FULL:
		link_info->speed = SPEED_10;
		link_info->duplex = DUPLEX_FULL;
		break;
	case QETH_QOAT_PHYS_SPEED_100M_HALF:
		link_info->speed = SPEED_100;
		link_info->duplex = DUPLEX_HALF;
		break;
	case QETH_QOAT_PHYS_SPEED_100M_FULL:
		link_info->speed = SPEED_100;
		link_info->duplex = DUPLEX_FULL;
		break;
	case QETH_QOAT_PHYS_SPEED_1000M_HALF:
		link_info->speed = SPEED_1000;
		link_info->duplex = DUPLEX_HALF;
		break;
	case QETH_QOAT_PHYS_SPEED_1000M_FULL:
		link_info->speed = SPEED_1000;
		link_info->duplex = DUPLEX_FULL;
		break;
	case QETH_QOAT_PHYS_SPEED_10G_FULL:
		link_info->speed = SPEED_10000;
		link_info->duplex = DUPLEX_FULL;
		break;
	case QETH_QOAT_PHYS_SPEED_25G_FULL:
		link_info->speed = SPEED_25000;
		link_info->duplex = DUPLEX_FULL;
		break;
	case QETH_QOAT_PHYS_SPEED_UNKNOWN:
	default:
		link_info->speed = SPEED_UNKNOWN;
		link_info->duplex = DUPLEX_UNKNOWN;
		break;
	}

	switch (phys_if->media_type) {
	case QETH_QOAT_PHYS_MEDIA_COPPER:
		link_info->port = PORT_TP;
		link_info->link_mode = QETH_LINK_MODE_UNKNOWN;
		break;
	case QETH_QOAT_PHYS_MEDIA_FIBRE_SHORT:
		link_info->port = PORT_FIBRE;
		link_info->link_mode = QETH_LINK_MODE_FIBRE_SHORT;
		break;
	case QETH_QOAT_PHYS_MEDIA_FIBRE_LONG:
		link_info->port = PORT_FIBRE;
		link_info->link_mode = QETH_LINK_MODE_FIBRE_LONG;
		break;
	default:
		link_info->port = PORT_OTHER;
		link_info->link_mode = QETH_LINK_MODE_UNKNOWN;
		break;
	}

	return 0;
}

static void qeth_init_link_info(struct qeth_card *card)
{
	card->info.link_info.duplex = DUPLEX_FULL;

	if (IS_IQD(card) || IS_VM_NIC(card)) {
		card->info.link_info.speed = SPEED_10000;
		card->info.link_info.port = PORT_FIBRE;
		card->info.link_info.link_mode = QETH_LINK_MODE_FIBRE_SHORT;
	} else {
		switch (card->info.link_type) {
		case QETH_LINK_TYPE_FAST_ETH:
		case QETH_LINK_TYPE_LANE_ETH100:
			card->info.link_info.speed = SPEED_100;
			card->info.link_info.port = PORT_TP;
			break;
		case QETH_LINK_TYPE_GBIT_ETH:
		case QETH_LINK_TYPE_LANE_ETH1000:
			card->info.link_info.speed = SPEED_1000;
			card->info.link_info.port = PORT_FIBRE;
			break;
		case QETH_LINK_TYPE_10GBIT_ETH:
			card->info.link_info.speed = SPEED_10000;
			card->info.link_info.port = PORT_FIBRE;
			break;
		case QETH_LINK_TYPE_25GBIT_ETH:
			card->info.link_info.speed = SPEED_25000;
			card->info.link_info.port = PORT_FIBRE;
			break;
		default:
			dev_info(&card->gdev->dev, "Unknown link type %x\n",
				 card->info.link_type);
			card->info.link_info.speed = SPEED_UNKNOWN;
			card->info.link_info.port = PORT_OTHER;
		}

		card->info.link_info.link_mode = QETH_LINK_MODE_UNKNOWN;
	}

	/* Get more accurate data via QUERY OAT: */
	if (qeth_adp_supported(card, IPA_SETADP_QUERY_OAT)) {
		struct qeth_link_info link_info;
		struct qeth_cmd_buffer *iob;

		iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_OAT,
					   SETADP_DATA_SIZEOF(query_oat));
		if (iob) {
			struct qeth_ipa_cmd *cmd = __ipa_cmd(iob);
			struct qeth_query_oat *oat_req;

			oat_req = &cmd->data.setadapterparms.data.query_oat;
			oat_req->subcmd_code = QETH_QOAT_SCOPE_INTERFACE;

			if (!qeth_send_ipa_cmd(card, iob,
					       qeth_init_link_info_oat_cb,
					       &link_info)) {
				if (link_info.speed != SPEED_UNKNOWN)
					card->info.link_info.speed = link_info.speed;
				if (link_info.duplex != DUPLEX_UNKNOWN)
					card->info.link_info.duplex = link_info.duplex;
				if (link_info.port != PORT_OTHER)
					card->info.link_info.port = link_info.port;
				if (link_info.link_mode != QETH_LINK_MODE_UNKNOWN)
					card->info.link_info.link_mode = link_info.link_mode;
			}
		}
	}
}

/**
@@ -5282,6 +5489,8 @@ static int qeth_hardsetup_card(struct qeth_card *card, bool *carrier_ok)
			goto out;
	}

	qeth_init_link_info(card);

	rc = qeth_init_qdio_queues(card);
	if (rc) {
		QETH_CARD_TEXT_(card, 2, "9err%d", rc);
+38 −2
Original line number Diff line number Diff line
@@ -489,9 +489,45 @@ struct qeth_set_access_ctrl {
	__u8 reserved[8];
} __attribute__((packed));

#define QETH_QOAT_PHYS_SPEED_UNKNOWN		0x00
#define QETH_QOAT_PHYS_SPEED_10M_HALF		0x01
#define QETH_QOAT_PHYS_SPEED_10M_FULL		0x02
#define QETH_QOAT_PHYS_SPEED_100M_HALF		0x03
#define QETH_QOAT_PHYS_SPEED_100M_FULL		0x04
#define QETH_QOAT_PHYS_SPEED_1000M_HALF		0x05
#define QETH_QOAT_PHYS_SPEED_1000M_FULL		0x06
// n/a						0x07
#define QETH_QOAT_PHYS_SPEED_10G_FULL		0x08
// n/a						0x09
#define QETH_QOAT_PHYS_SPEED_25G_FULL		0x0A

#define QETH_QOAT_PHYS_MEDIA_COPPER		0x01
#define QETH_QOAT_PHYS_MEDIA_FIBRE_SHORT	0x02
#define QETH_QOAT_PHYS_MEDIA_FIBRE_LONG		0x04

struct qeth_query_oat_physical_if {
	u8 res_head[33];
	u8 speed_duplex;
	u8 media_type;
	u8 res_tail[29];
};

#define QETH_QOAT_REPLY_TYPE_PHYS_IF		0x0004

struct qeth_query_oat_reply {
	u16 type;
	u16 length;
	u16 version;
	u8 res[10];
	struct qeth_query_oat_physical_if phys_if;
};

#define QETH_QOAT_SCOPE_INTERFACE		0x00000001

struct qeth_query_oat {
	__u32 subcmd_code;
	__u8 reserved[12];
	u32 subcmd_code;
	u8 reserved[12];
	struct qeth_query_oat_reply reply[];
} __packed;

struct qeth_qoat_priv {
+88 −155
Original line number Diff line number Diff line
@@ -324,8 +324,8 @@ static int qeth_set_per_queue_coalesce(struct net_device *dev, u32 queue,
/* Autoneg and full-duplex are supported and advertised unconditionally.     */
/* Always advertise and support all speeds up to specified, and only one     */
/* specified port type.							     */
static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd,
				int maxspeed, int porttype)
static void qeth_set_ethtool_link_modes(struct ethtool_link_ksettings *cmd,
					enum qeth_link_mode link_mode)
{
	ethtool_link_ksettings_zero_link_mode(cmd, supported);
	ethtool_link_ksettings_zero_link_mode(cmd, advertising);
@@ -334,29 +334,12 @@ static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd,
	ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
	ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);

	switch (porttype) {
	switch (cmd->base.port) {
	case PORT_TP:
		ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
		ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
		break;
	case PORT_FIBRE:
		ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
		ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE);
		break;
	default:
		ethtool_link_ksettings_add_link_mode(cmd, supported, TP);
		ethtool_link_ksettings_add_link_mode(cmd, advertising, TP);
		WARN_ON_ONCE(1);
	}

	/* partially does fall through, to also select lower speeds */
	switch (maxspeed) {
	case SPEED_25000:
		ethtool_link_ksettings_add_link_mode(cmd, supported,
						     25000baseSR_Full);
		ethtool_link_ksettings_add_link_mode(cmd, advertising,
						     25000baseSR_Full);
		break;
		switch (cmd->base.speed) {
		case SPEED_10000:
			ethtool_link_ksettings_add_link_mode(cmd, supported,
							     10000baseT_Full);
@@ -394,126 +377,76 @@ static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd,
							     10baseT_Half);
			break;
		default:
			break;
		}

		break;
	case PORT_FIBRE:
		ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
		ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE);

		switch (cmd->base.speed) {
		case SPEED_25000:
			ethtool_link_ksettings_add_link_mode(cmd, supported,
						     10baseT_Full);
							     25000baseSR_Full);
			ethtool_link_ksettings_add_link_mode(cmd, advertising,
						     10baseT_Full);
							     25000baseSR_Full);
			break;
		case SPEED_10000:
			if (link_mode == QETH_LINK_MODE_FIBRE_LONG) {
				ethtool_link_ksettings_add_link_mode(cmd, supported,
						     10baseT_Half);
								     10000baseLR_Full);
				ethtool_link_ksettings_add_link_mode(cmd, advertising,
						     10baseT_Half);
		WARN_ON_ONCE(1);
								     10000baseLR_Full);
			} else if (link_mode == QETH_LINK_MODE_FIBRE_SHORT) {
				ethtool_link_ksettings_add_link_mode(cmd, supported,
								     10000baseSR_Full);
				ethtool_link_ksettings_add_link_mode(cmd, advertising,
								     10000baseSR_Full);
			}
			break;
		case SPEED_1000:
			ethtool_link_ksettings_add_link_mode(cmd, supported,
							     1000baseX_Full);
			ethtool_link_ksettings_add_link_mode(cmd, advertising,
							     1000baseX_Full);
			break;
		default:
			break;
		}

		break;
	default:
		break;
	}
}

static int qeth_get_link_ksettings(struct net_device *netdev,
				   struct ethtool_link_ksettings *cmd)
{
	struct qeth_card *card = netdev->ml_priv;
	enum qeth_link_types link_type;
	struct carrier_info carrier_info;
	int rc;
	struct qeth_link_info link_info;

	if (IS_IQD(card) || IS_VM_NIC(card))
		link_type = QETH_LINK_TYPE_10GBIT_ETH;
	else
		link_type = card->info.link_type;

	cmd->base.duplex = DUPLEX_FULL;
	cmd->base.speed = card->info.link_info.speed;
	cmd->base.duplex = card->info.link_info.duplex;
	cmd->base.port = card->info.link_info.port;
	cmd->base.autoneg = AUTONEG_ENABLE;
	cmd->base.phy_address = 0;
	cmd->base.mdio_support = 0;
	cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID;
	cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;

	switch (link_type) {
	case QETH_LINK_TYPE_FAST_ETH:
	case QETH_LINK_TYPE_LANE_ETH100:
		cmd->base.speed = SPEED_100;
		cmd->base.port = PORT_TP;
		break;
	case QETH_LINK_TYPE_GBIT_ETH:
	case QETH_LINK_TYPE_LANE_ETH1000:
		cmd->base.speed = SPEED_1000;
		cmd->base.port = PORT_FIBRE;
		break;
	case QETH_LINK_TYPE_10GBIT_ETH:
		cmd->base.speed = SPEED_10000;
		cmd->base.port = PORT_FIBRE;
		break;
	case QETH_LINK_TYPE_25GBIT_ETH:
		cmd->base.speed = SPEED_25000;
		cmd->base.port = PORT_FIBRE;
		break;
	default:
		cmd->base.speed = SPEED_10;
		cmd->base.port = PORT_TP;
	}
	qeth_set_cmd_adv_sup(cmd, cmd->base.speed, cmd->base.port);

	/* Check if we can obtain more accurate information.	 */
	/* If QUERY_CARD_INFO command is not supported or fails, */
	/* just return the heuristics that was filled above.	 */
	rc = qeth_query_card_info(card, &carrier_info);
	if (rc == -EOPNOTSUPP) /* for old hardware, return heuristic */
		return 0;
	if (rc) /* report error from the hardware operation */
		return rc;
	/* on success, fill in the information got from the hardware */

	netdev_dbg(netdev,
	"card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n",
			carrier_info.card_type,
			carrier_info.port_mode,
			carrier_info.port_speed);

	/* Update attributes for which we've obtained more authoritative */
	/* information, leave the rest the way they where filled above.  */
	switch (carrier_info.card_type) {
	case CARD_INFO_TYPE_1G_COPPER_A:
	case CARD_INFO_TYPE_1G_COPPER_B:
		cmd->base.port = PORT_TP;
		qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port);
		break;
	case CARD_INFO_TYPE_1G_FIBRE_A:
	case CARD_INFO_TYPE_1G_FIBRE_B:
		cmd->base.port = PORT_FIBRE;
		qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port);
		break;
	case CARD_INFO_TYPE_10G_FIBRE_A:
	case CARD_INFO_TYPE_10G_FIBRE_B:
		cmd->base.port = PORT_FIBRE;
		qeth_set_cmd_adv_sup(cmd, SPEED_10000, cmd->base.port);
		break;
	if (!qeth_query_card_info(card, &link_info)) {
		if (link_info.speed != SPEED_UNKNOWN)
			cmd->base.speed = link_info.speed;
		if (link_info.duplex != DUPLEX_UNKNOWN)
			cmd->base.duplex = link_info.duplex;
		if (link_info.port != PORT_OTHER)
			cmd->base.port = link_info.port;
	}

	switch (carrier_info.port_mode) {
	case CARD_INFO_PORTM_FULLDUPLEX:
		cmd->base.duplex = DUPLEX_FULL;
		break;
	case CARD_INFO_PORTM_HALFDUPLEX:
		cmd->base.duplex = DUPLEX_HALF;
		break;
	}

	switch (carrier_info.port_speed) {
	case CARD_INFO_PORTS_10M:
		cmd->base.speed = SPEED_10;
		break;
	case CARD_INFO_PORTS_100M:
		cmd->base.speed = SPEED_100;
		break;
	case CARD_INFO_PORTS_1G:
		cmd->base.speed = SPEED_1000;
		break;
	case CARD_INFO_PORTS_10G:
		cmd->base.speed = SPEED_10000;
		break;
	case CARD_INFO_PORTS_25G:
		cmd->base.speed = SPEED_25000;
		break;
	}
	qeth_set_ethtool_link_modes(cmd, card->info.link_info.link_mode);

	return 0;
}
+15 −18
Original line number Diff line number Diff line
@@ -737,8 +737,6 @@ static void qeth_l2_dev2br_an_set_cb(void *priv,
 *
 *	On enable, emits a series of address notifications for all
 *	currently registered hosts.
 *
 *	Must be called under rtnl_lock
 */
static int qeth_l2_dev2br_an_set(struct qeth_card *card, bool enable)
{
@@ -1289,16 +1287,19 @@ static void qeth_l2_dev2br_worker(struct work_struct *work)
	if (READ_ONCE(card->info.pnso_mode) == QETH_PNSO_NONE)
		goto free;

	if (data->ac_event.lost_event_mask) {
		/* Potential re-config in progress, try again later: */
		if (!rtnl_trylock()) {
			queue_delayed_work(card->event_wq, dwork,
					   msecs_to_jiffies(100));
			return;
		}
	if (!netif_device_present(card->dev))
		goto out_unlock;

	if (data->ac_event.lost_event_mask) {
		if (!netif_device_present(card->dev)) {
			rtnl_unlock();
			goto free;
		}

		QETH_DBF_MESSAGE(3,
				 "Address change notification overflow on device %x\n",
				 CARD_DEVID(card));
@@ -1328,6 +1329,8 @@ static void qeth_l2_dev2br_worker(struct work_struct *work)
					 "Address Notification resynced on device %x\n",
					 CARD_DEVID(card));
		}

		rtnl_unlock();
	} else {
		for (i = 0; i < data->ac_event.num_entries; i++) {
			struct qeth_ipacmd_addr_change_entry *entry =
@@ -1339,9 +1342,6 @@ static void qeth_l2_dev2br_worker(struct work_struct *work)
		}
	}

out_unlock:
	rtnl_unlock();

free:
	kfree(data);
}
@@ -2310,11 +2310,8 @@ static void qeth_l2_set_offline(struct qeth_card *card)
		card->state = CARD_STATE_DOWN;

	qeth_l2_set_pnso_mode(card, QETH_PNSO_NONE);
	if (priv->brport_features & BR_LEARNING_SYNC) {
		rtnl_lock();
	if (priv->brport_features & BR_LEARNING_SYNC)
		qeth_l2_dev2br_fdb_flush(card);
		rtnl_unlock();
	}
}

/* Returns zero if the command is successfully "consumed" */
Loading