Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c8431622 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 's390-qeth-fixes'



Julian Wiedmann says:

====================
s390/qeth: fixes 2018-02-27

please apply some more qeth patches for -net and stable.

One patch fixes a performance bug in the TSO path. Then there's several
more fixes for IP management on L3 devices - including a revert, so that
the subsequent fix cleanly applies to earlier kernels.
The final patch takes care of a race in the control IO code that causes
qeth to miss the cmd response, and subsequently trigger device recovery.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 590399dd d22ffb5a
Loading
Loading
Loading
Loading
+16 −13
Original line number Diff line number Diff line
@@ -2134,24 +2134,25 @@ int qeth_send_control_data(struct qeth_card *card, int len,
	}
	reply->callback = reply_cb;
	reply->param = reply_param;
	if (card->state == CARD_STATE_DOWN)
		reply->seqno = QETH_IDX_COMMAND_SEQNO;
	else
		reply->seqno = card->seqno.ipa++;

	init_waitqueue_head(&reply->wait_q);
	spin_lock_irqsave(&card->lock, flags);
	list_add_tail(&reply->list, &card->cmd_waiter_list);
	spin_unlock_irqrestore(&card->lock, flags);

	while (atomic_cmpxchg(&card->write.irq_pending, 0, 1)) ;
	qeth_prepare_control_data(card, len, iob);

	if (IS_IPA(iob->data)) {
		cmd = __ipa_cmd(iob);
		cmd->hdr.seqno = card->seqno.ipa++;
		reply->seqno = cmd->hdr.seqno;
		event_timeout = QETH_IPA_TIMEOUT;
	} else {
		reply->seqno = QETH_IDX_COMMAND_SEQNO;
		event_timeout = QETH_TIMEOUT;
	}
	qeth_prepare_control_data(card, len, iob);

	spin_lock_irqsave(&card->lock, flags);
	list_add_tail(&reply->list, &card->cmd_waiter_list);
	spin_unlock_irqrestore(&card->lock, flags);

	timeout = jiffies + event_timeout;

@@ -2933,7 +2934,7 @@ static void qeth_fill_ipacmd_header(struct qeth_card *card,
	memset(cmd, 0, sizeof(struct qeth_ipa_cmd));
	cmd->hdr.command = command;
	cmd->hdr.initiator = IPA_CMD_INITIATOR_HOST;
	cmd->hdr.seqno = card->seqno.ipa;
	/* cmd->hdr.seqno is set by qeth_send_control_data() */
	cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type);
	cmd->hdr.rel_adapter_no = (__u8) card->info.portno;
	if (card->options.layer2)
@@ -3898,10 +3899,12 @@ EXPORT_SYMBOL_GPL(qeth_get_elements_for_frags);
int qeth_get_elements_no(struct qeth_card *card,
		     struct sk_buff *skb, int extra_elems, int data_offset)
{
	int elements = qeth_get_elements_for_range(
				(addr_t)skb->data + data_offset,
				(addr_t)skb->data + skb_headlen(skb)) +
			qeth_get_elements_for_frags(skb);
	addr_t end = (addr_t)skb->data + skb_headlen(skb);
	int elements = qeth_get_elements_for_frags(skb);
	addr_t start = (addr_t)skb->data + data_offset;

	if (start != end)
		elements += qeth_get_elements_for_range(start, end);

	if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
		QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
+33 −1
Original line number Diff line number Diff line
@@ -40,8 +40,40 @@ struct qeth_ipaddr {
			unsigned int pfxlen;
		} a6;
	} u;

};

static inline bool qeth_l3_addr_match_ip(struct qeth_ipaddr *a1,
					 struct qeth_ipaddr *a2)
{
	if (a1->proto != a2->proto)
		return false;
	if (a1->proto == QETH_PROT_IPV6)
		return ipv6_addr_equal(&a1->u.a6.addr, &a2->u.a6.addr);
	return a1->u.a4.addr == a2->u.a4.addr;
}

static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1,
					  struct qeth_ipaddr *a2)
{
	/* Assumes that the pair was obtained via qeth_l3_addr_find_by_ip(),
	 * so 'proto' and 'addr' match for sure.
	 *
	 * For ucast:
	 * -	'mac' is always 0.
	 * -	'mask'/'pfxlen' for RXIP/VIPA is always 0. For NORMAL, matching
	 *	values are required to avoid mixups in takeover eligibility.
	 *
	 * For mcast,
	 * -	'mac' is mapped from the IP, and thus always matches.
	 * -	'mask'/'pfxlen' is always 0.
	 */
	if (a1->type != a2->type)
		return false;
	if (a1->proto == QETH_PROT_IPV6)
		return a1->u.a6.pfxlen == a2->u.a6.pfxlen;
	return a1->u.a4.mask == a2->u.a4.mask;
}

static inline  u64 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr)
{
	u64  ret = 0;
+53 −70
Original line number Diff line number Diff line
@@ -67,6 +67,24 @@ void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr,
		qeth_l3_ipaddr6_to_string(addr, buf);
}

static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
						   struct qeth_ipaddr *query)
{
	u64 key = qeth_l3_ipaddr_hash(query);
	struct qeth_ipaddr *addr;

	if (query->is_multicast) {
		hash_for_each_possible(card->ip_mc_htable, addr, hnode, key)
			if (qeth_l3_addr_match_ip(addr, query))
				return addr;
	} else {
		hash_for_each_possible(card->ip_htable,  addr, hnode, key)
			if (qeth_l3_addr_match_ip(addr, query))
				return addr;
	}
	return NULL;
}

static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len)
{
	int i, j;
@@ -120,34 +138,6 @@ static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
	return rc;
}

inline int
qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2)
{
	return addr1->proto == addr2->proto &&
	       !memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) &&
	       ether_addr_equal_64bits(addr1->mac, addr2->mac);
}

static struct qeth_ipaddr *
qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
{
	struct qeth_ipaddr *addr;

	if (tmp_addr->is_multicast) {
		hash_for_each_possible(card->ip_mc_htable,  addr,
				hnode, qeth_l3_ipaddr_hash(tmp_addr))
			if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
				return addr;
	} else {
		hash_for_each_possible(card->ip_htable,  addr,
				hnode, qeth_l3_ipaddr_hash(tmp_addr))
			if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
				return addr;
	}

	return NULL;
}

int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
{
	int rc = 0;
@@ -162,22 +152,17 @@ int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
		QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
	}

	addr = qeth_l3_ip_from_hash(card, tmp_addr);
	if (!addr)
	addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
	if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr))
		return -ENOENT;

	addr->ref_counter--;
	if (addr->ref_counter > 0 && (addr->type == QETH_IP_TYPE_NORMAL ||
				      addr->type == QETH_IP_TYPE_RXIP))
	if (addr->type == QETH_IP_TYPE_NORMAL && addr->ref_counter > 0)
		return rc;
	if (addr->in_progress)
		return -EINPROGRESS;

	if (!qeth_card_hw_is_reachable(card)) {
		addr->disp_flag = QETH_DISP_ADDR_DELETE;
		return 0;
	}

	if (qeth_card_hw_is_reachable(card))
		rc = qeth_l3_deregister_addr_entry(card, addr);

	hash_del(&addr->hnode);
@@ -190,6 +175,7 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
{
	int rc = 0;
	struct qeth_ipaddr *addr;
	char buf[40];

	QETH_CARD_TEXT(card, 4, "addip");

@@ -200,8 +186,20 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
		QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
	}

	addr = qeth_l3_ip_from_hash(card, tmp_addr);
	if (!addr) {
	addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
	if (addr) {
		if (tmp_addr->type != QETH_IP_TYPE_NORMAL)
			return -EADDRINUSE;
		if (qeth_l3_addr_match_all(addr, tmp_addr)) {
			addr->ref_counter++;
			return 0;
		}
		qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u,
					 buf);
		dev_warn(&card->gdev->dev,
			 "Registering IP address %s failed\n", buf);
		return -EADDRINUSE;
	} else {
		addr = qeth_l3_get_addr_buffer(tmp_addr->proto);
		if (!addr)
			return -ENOMEM;
@@ -241,19 +239,15 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
				(rc == IPA_RC_LAN_OFFLINE)) {
			addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
			if (addr->ref_counter < 1) {
				qeth_l3_delete_ip(card, addr);
				qeth_l3_deregister_addr_entry(card, addr);
				hash_del(&addr->hnode);
				kfree(addr);
			}
		} else {
			hash_del(&addr->hnode);
			kfree(addr);
		}
	} else {
		if (addr->type == QETH_IP_TYPE_NORMAL ||
		    addr->type == QETH_IP_TYPE_RXIP)
			addr->ref_counter++;
	}

	return rc;
}

@@ -321,11 +315,7 @@ static void qeth_l3_recover_ip(struct qeth_card *card)
	spin_lock_bh(&card->ip_lock);

	hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
		if (addr->disp_flag == QETH_DISP_ADDR_DELETE) {
			qeth_l3_deregister_addr_entry(card, addr);
			hash_del(&addr->hnode);
			kfree(addr);
		} else if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
		if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
			if (addr->proto == QETH_PROT_IPV4) {
				addr->in_progress = 1;
				spin_unlock_bh(&card->ip_lock);
@@ -643,12 +633,7 @@ int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
		return -ENOMEM;

	spin_lock_bh(&card->ip_lock);

	if (qeth_l3_ip_from_hash(card, ipaddr))
		rc = -EEXIST;
	else
	rc = qeth_l3_add_ip(card, ipaddr);

	spin_unlock_bh(&card->ip_lock);

	kfree(ipaddr);
@@ -713,12 +698,7 @@ int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
		return -ENOMEM;

	spin_lock_bh(&card->ip_lock);

	if (qeth_l3_ip_from_hash(card, ipaddr))
		rc = -EEXIST;
	else
	rc = qeth_l3_add_ip(card, ipaddr);

	spin_unlock_bh(&card->ip_lock);

	kfree(ipaddr);
@@ -1239,8 +1219,9 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev)
		tmp->u.a4.addr = be32_to_cpu(im4->multiaddr);
		tmp->is_multicast = 1;

		ipm = qeth_l3_ip_from_hash(card, tmp);
		ipm = qeth_l3_find_addr_by_ip(card, tmp);
		if (ipm) {
			/* for mcast, by-IP match means full match */
			ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
		} else {
			ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
@@ -1319,8 +1300,9 @@ static void qeth_l3_add_mc6_to_hash(struct qeth_card *card,
		       sizeof(struct in6_addr));
		tmp->is_multicast = 1;

		ipm = qeth_l3_ip_from_hash(card, tmp);
		ipm = qeth_l3_find_addr_by_ip(card, tmp);
		if (ipm) {
			/* for mcast, by-IP match means full match */
			ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
			continue;
		}
@@ -2450,11 +2432,12 @@ static void qeth_tso_fill_header(struct qeth_card *card,
static int qeth_l3_get_elements_no_tso(struct qeth_card *card,
			struct sk_buff *skb, int extra_elems)
{
	addr_t tcpdptr = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
	int elements = qeth_get_elements_for_range(
				tcpdptr,
				(addr_t)skb->data + skb_headlen(skb)) +
				qeth_get_elements_for_frags(skb);
	addr_t start = (addr_t)tcp_hdr(skb) + tcp_hdrlen(skb);
	addr_t end = (addr_t)skb->data + skb_headlen(skb);
	int elements = qeth_get_elements_for_frags(skb);

	if (start != end)
		elements += qeth_get_elements_for_range(start, end);

	if ((elements + extra_elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
		QETH_DBF_MESSAGE(2,