diff options
Diffstat (limited to 'drivers/net/ethernet/ibm')
| -rw-r--r-- | drivers/net/ethernet/ibm/emac/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/ibm/ibmvnic.c | 520 | ||||
| -rw-r--r-- | drivers/net/ethernet/ibm/ibmvnic.h | 60 | 
3 files changed, 535 insertions, 46 deletions
diff --git a/drivers/net/ethernet/ibm/emac/Makefile b/drivers/net/ethernet/ibm/emac/Makefile index 98768ba0955a..ddf1ce3c8cca 100644 --- a/drivers/net/ethernet/ibm/emac/Makefile +++ b/drivers/net/ethernet/ibm/emac/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0  #  # Makefile for the PowerPC 4xx on-chip ethernet driver  # diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index c66abd476023..1dc4aef37d3a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -75,6 +75,7 @@  #include <asm/firmware.h>  #include <linux/workqueue.h>  #include <linux/if_vlan.h> +#include <linux/utsname.h>  #include "ibmvnic.h" @@ -115,6 +116,7 @@ static int init_sub_crqs(struct ibmvnic_adapter *);  static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter);  static int ibmvnic_init(struct ibmvnic_adapter *);  static void release_crq_queue(struct ibmvnic_adapter *); +static int __ibmvnic_set_mac(struct net_device *netdev, struct sockaddr *p);  struct ibmvnic_stat {  	char name[ETH_GSTRING_LEN]; @@ -553,6 +555,10 @@ static int reset_tx_pools(struct ibmvnic_adapter *adapter)  		if (rc)  			return rc; +		rc = reset_long_term_buff(adapter, &tx_pool->tso_ltb); +		if (rc) +			return rc; +  		memset(tx_pool->tx_buff, 0,  		       adapter->req_tx_entries_per_subcrq *  		       sizeof(struct ibmvnic_tx_buff)); @@ -562,11 +568,21 @@ static int reset_tx_pools(struct ibmvnic_adapter *adapter)  		tx_pool->consumer_index = 0;  		tx_pool->producer_index = 0; +		tx_pool->tso_index = 0;  	}  	return 0;  } +static void release_vpd_data(struct ibmvnic_adapter *adapter) +{ +	if (!adapter->vpd) +		return; + +	kfree(adapter->vpd->buff); +	kfree(adapter->vpd); +} +  static void release_tx_pools(struct ibmvnic_adapter *adapter)  {  	struct ibmvnic_tx_pool *tx_pool; @@ -581,6 +597,7 @@ static void release_tx_pools(struct ibmvnic_adapter *adapter)  		tx_pool = &adapter->tx_pool[i];  		kfree(tx_pool->tx_buff);  		free_long_term_buff(adapter, &tx_pool->long_term_buff); +		free_long_term_buff(adapter, &tx_pool->tso_ltb);  		kfree(tx_pool->free_map);  	} @@ -625,6 +642,16 @@ static int init_tx_pools(struct net_device *netdev)  			return -1;  		} +		/* alloc TSO ltb */ +		if (alloc_long_term_buff(adapter, &tx_pool->tso_ltb, +					 IBMVNIC_TSO_BUFS * +					 IBMVNIC_TSO_BUF_SZ)) { +			release_tx_pools(adapter); +			return -1; +		} + +		tx_pool->tso_index = 0; +  		tx_pool->free_map = kcalloc(adapter->req_tx_entries_per_subcrq,  					    sizeof(int), GFP_KERNEL);  		if (!tx_pool->free_map) { @@ -736,6 +763,8 @@ static void release_resources(struct ibmvnic_adapter *adapter)  {  	int i; +	release_vpd_data(adapter); +  	release_tx_pools(adapter);  	release_rx_pools(adapter); @@ -816,6 +845,56 @@ static int set_real_num_queues(struct net_device *netdev)  	return rc;  } +static int ibmvnic_get_vpd(struct ibmvnic_adapter *adapter) +{ +	struct device *dev = &adapter->vdev->dev; +	union ibmvnic_crq crq; +	int len = 0; + +	if (adapter->vpd->buff) +		len = adapter->vpd->len; + +	reinit_completion(&adapter->fw_done); +	crq.get_vpd_size.first = IBMVNIC_CRQ_CMD; +	crq.get_vpd_size.cmd = GET_VPD_SIZE; +	ibmvnic_send_crq(adapter, &crq); +	wait_for_completion(&adapter->fw_done); + +	if (!adapter->vpd->len) +		return -ENODATA; + +	if (!adapter->vpd->buff) +		adapter->vpd->buff = kzalloc(adapter->vpd->len, GFP_KERNEL); +	else if (adapter->vpd->len != len) +		adapter->vpd->buff = +			krealloc(adapter->vpd->buff, +				 adapter->vpd->len, GFP_KERNEL); + +	if (!adapter->vpd->buff) { +		dev_err(dev, "Could allocate VPD buffer\n"); +		return -ENOMEM; +	} + +	adapter->vpd->dma_addr = +		dma_map_single(dev, adapter->vpd->buff, adapter->vpd->len, +			       DMA_FROM_DEVICE); +	if (dma_mapping_error(dev, adapter->vpd->dma_addr)) { +		dev_err(dev, "Could not map VPD buffer\n"); +		kfree(adapter->vpd->buff); +		return -ENOMEM; +	} + +	reinit_completion(&adapter->fw_done); +	crq.get_vpd.first = IBMVNIC_CRQ_CMD; +	crq.get_vpd.cmd = GET_VPD; +	crq.get_vpd.ioba = cpu_to_be32(adapter->vpd->dma_addr); +	crq.get_vpd.len = cpu_to_be32((u32)adapter->vpd->len); +	ibmvnic_send_crq(adapter, &crq); +	wait_for_completion(&adapter->fw_done); + +	return 0; +} +  static int init_resources(struct ibmvnic_adapter *adapter)  {  	struct net_device *netdev = adapter->netdev; @@ -833,6 +912,10 @@ static int init_resources(struct ibmvnic_adapter *adapter)  	if (rc)  		return rc; +	adapter->vpd = kzalloc(sizeof(*adapter->vpd), GFP_KERNEL); +	if (!adapter->vpd) +		return -ENOMEM; +  	adapter->map_id = 1;  	adapter->napi = kcalloc(adapter->req_rx_queues,  				sizeof(struct napi_struct), GFP_KERNEL); @@ -906,10 +989,15 @@ static int __ibmvnic_open(struct net_device *netdev)  static int ibmvnic_open(struct net_device *netdev)  {  	struct ibmvnic_adapter *adapter = netdev_priv(netdev); -	int rc; +	int rc, vpd;  	mutex_lock(&adapter->reset_lock); +	if (adapter->mac_change_pending) { +		__ibmvnic_set_mac(netdev, &adapter->desired.mac); +		adapter->mac_change_pending = false; +	} +  	if (adapter->state != VNIC_CLOSED) {  		rc = ibmvnic_login(netdev);  		if (rc) { @@ -927,6 +1015,13 @@ static int ibmvnic_open(struct net_device *netdev)  	}  	rc = __ibmvnic_open(netdev); +	netif_carrier_on(netdev); + +	/* Vital Product Data (VPD) */ +	vpd = ibmvnic_get_vpd(adapter); +	if (vpd) +		netdev_err(netdev, "failed to initialize Vital Product Data (VPD)\n"); +  	mutex_unlock(&adapter->reset_lock);  	return rc; @@ -1200,11 +1295,41 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)  		be32_to_cpu(adapter->login_rsp_buf->off_txsubm_subcrqs));  	index = tx_pool->free_map[tx_pool->consumer_index]; -	offset = index * adapter->req_mtu; -	dst = tx_pool->long_term_buff.buff + offset; -	memset(dst, 0, adapter->req_mtu); -	skb_copy_from_linear_data(skb, dst, skb->len); -	data_dma_addr = tx_pool->long_term_buff.addr + offset; + +	if (skb_is_gso(skb)) { +		offset = tx_pool->tso_index * IBMVNIC_TSO_BUF_SZ; +		dst = tx_pool->tso_ltb.buff + offset; +		memset(dst, 0, IBMVNIC_TSO_BUF_SZ); +		data_dma_addr = tx_pool->tso_ltb.addr + offset; +		tx_pool->tso_index++; +		if (tx_pool->tso_index == IBMVNIC_TSO_BUFS) +			tx_pool->tso_index = 0; +	} else { +		offset = index * adapter->req_mtu; +		dst = tx_pool->long_term_buff.buff + offset; +		memset(dst, 0, adapter->req_mtu); +		data_dma_addr = tx_pool->long_term_buff.addr + offset; +	} + +	if (skb_shinfo(skb)->nr_frags) { +		int cur, i; + +		/* Copy the head */ +		skb_copy_from_linear_data(skb, dst, skb_headlen(skb)); +		cur = skb_headlen(skb); + +		/* Copy the frags */ +		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { +			const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + +			memcpy(dst + cur, +			       page_address(skb_frag_page(frag)) + +			       frag->page_offset, skb_frag_size(frag)); +			cur += skb_frag_size(frag); +		} +	} else { +		skb_copy_from_linear_data(skb, dst, skb->len); +	}  	tx_pool->consumer_index =  	    (tx_pool->consumer_index + 1) % @@ -1225,7 +1350,10 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)  	tx_crq.v1.n_sge = 1;  	tx_crq.v1.flags1 = IBMVNIC_TX_COMP_NEEDED;  	tx_crq.v1.correlator = cpu_to_be32(index); -	tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->long_term_buff.map_id); +	if (skb_is_gso(skb)) +		tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->tso_ltb.map_id); +	else +		tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->long_term_buff.map_id);  	tx_crq.v1.sge_len = cpu_to_be32(skb->len);  	tx_crq.v1.ioba = cpu_to_be64(data_dma_addr); @@ -1250,6 +1378,11 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)  		tx_crq.v1.flags1 |= IBMVNIC_TX_CHKSUM_OFFLOAD;  		hdrs += 2;  	} +	if (skb_is_gso(skb)) { +		tx_crq.v1.flags1 |= IBMVNIC_TX_LSO; +		tx_crq.v1.mss = cpu_to_be16(skb_shinfo(skb)->gso_size); +		hdrs += 2; +	}  	/* determine if l2/3/4 headers are sent to firmware */  	if ((*hdrs >> 7) & 1 &&  	    (skb->protocol == htons(ETH_P_IP) || @@ -1371,7 +1504,7 @@ static void ibmvnic_set_multi(struct net_device *netdev)  	}  } -static int ibmvnic_set_mac(struct net_device *netdev, void *p) +static int __ibmvnic_set_mac(struct net_device *netdev, struct sockaddr *p)  {  	struct ibmvnic_adapter *adapter = netdev_priv(netdev);  	struct sockaddr *addr = p; @@ -1389,6 +1522,22 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p)  	return 0;  } +static int ibmvnic_set_mac(struct net_device *netdev, void *p) +{ +	struct ibmvnic_adapter *adapter = netdev_priv(netdev); +	struct sockaddr *addr = p; + +	if (adapter->state != VNIC_OPEN) { +		memcpy(&adapter->desired.mac, addr, sizeof(struct sockaddr)); +		adapter->mac_change_pending = true; +		return 0; +	} + +	__ibmvnic_set_mac(netdev, addr); + +	return 0; +} +  /**   * do_reset returns zero if we are able to keep processing reset events, or   * non-zero if we hit a fatal error and must halt. @@ -1415,6 +1564,13 @@ static int do_reset(struct ibmvnic_adapter *adapter,  	if (rc)  		return rc; +	if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM || +	    adapter->wait_for_reset) { +		release_resources(adapter); +		release_sub_crqs(adapter); +		release_crq_queue(adapter); +	} +  	if (adapter->reset_reason != VNIC_RESET_NON_FATAL) {  		/* remove the closed state so when we call open it appears  		 * we are coming from the probed state. @@ -1423,7 +1579,7 @@ static int do_reset(struct ibmvnic_adapter *adapter,  		rc = ibmvnic_init(adapter);  		if (rc) -			return 0; +			return IBMVNIC_INIT_FAILED;  		/* If the adapter was in PROBE state prior to the reset,  		 * exit here. @@ -1437,16 +1593,23 @@ static int do_reset(struct ibmvnic_adapter *adapter,  			return 0;  		} -		rc = reset_tx_pools(adapter); -		if (rc) -			return rc; +		if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM || +		    adapter->wait_for_reset) { +			rc = init_resources(adapter); +			if (rc) +				return rc; +		} else { +			rc = reset_tx_pools(adapter); +			if (rc) +				return rc; -		rc = reset_rx_pools(adapter); -		if (rc) -			return rc; +			rc = reset_rx_pools(adapter); +			if (rc) +				return rc; -		if (reset_state == VNIC_CLOSED) -			return 0; +			if (reset_state == VNIC_CLOSED) +				return 0; +		}  	}  	rc = __ibmvnic_open(netdev); @@ -1506,7 +1669,7 @@ static void __ibmvnic_reset(struct work_struct *work)  	struct ibmvnic_adapter *adapter;  	struct net_device *netdev;  	u32 reset_state; -	int rc; +	int rc = 0;  	adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset);  	netdev = adapter->netdev; @@ -1519,12 +1682,18 @@ static void __ibmvnic_reset(struct work_struct *work)  	while (rwi) {  		rc = do_reset(adapter, rwi, reset_state);  		kfree(rwi); -		if (rc) +		if (rc && rc != IBMVNIC_INIT_FAILED)  			break;  		rwi = get_next_rwi(adapter);  	} +	if (adapter->wait_for_reset) { +		adapter->wait_for_reset = false; +		adapter->reset_done_rc = rc; +		complete(&adapter->reset_done); +	} +  	if (rc) {  		netdev_dbg(adapter->netdev, "Reset failed\n");  		free_all_rwi(adapter); @@ -1704,9 +1873,42 @@ static void ibmvnic_netpoll_controller(struct net_device *dev)  }  #endif +static int wait_for_reset(struct ibmvnic_adapter *adapter) +{ +	adapter->fallback.mtu = adapter->req_mtu; +	adapter->fallback.rx_queues = adapter->req_rx_queues; +	adapter->fallback.tx_queues = adapter->req_tx_queues; +	adapter->fallback.rx_entries = adapter->req_rx_add_entries_per_subcrq; +	adapter->fallback.tx_entries = adapter->req_tx_entries_per_subcrq; + +	init_completion(&adapter->reset_done); +	ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM); +	adapter->wait_for_reset = true; +	wait_for_completion(&adapter->reset_done); + +	if (adapter->reset_done_rc) { +		adapter->desired.mtu = adapter->fallback.mtu; +		adapter->desired.rx_queues = adapter->fallback.rx_queues; +		adapter->desired.tx_queues = adapter->fallback.tx_queues; +		adapter->desired.rx_entries = adapter->fallback.rx_entries; +		adapter->desired.tx_entries = adapter->fallback.tx_entries; + +		init_completion(&adapter->reset_done); +		ibmvnic_reset(adapter, VNIC_RESET_CHANGE_PARAM); +		wait_for_completion(&adapter->reset_done); +	} +	adapter->wait_for_reset = false; + +	return adapter->reset_done_rc; +} +  static int ibmvnic_change_mtu(struct net_device *netdev, int new_mtu)  { -	return -EOPNOTSUPP; +	struct ibmvnic_adapter *adapter = netdev_priv(netdev); + +	adapter->desired.mtu = new_mtu + ETH_HLEN; + +	return wait_for_reset(adapter);  }  static const struct net_device_ops ibmvnic_netdev_ops = { @@ -1748,11 +1950,15 @@ static int ibmvnic_get_link_ksettings(struct net_device *netdev,  	return 0;  } -static void ibmvnic_get_drvinfo(struct net_device *dev, +static void ibmvnic_get_drvinfo(struct net_device *netdev,  				struct ethtool_drvinfo *info)  { +	struct ibmvnic_adapter *adapter = netdev_priv(netdev); +  	strlcpy(info->driver, ibmvnic_driver_name, sizeof(info->driver));  	strlcpy(info->version, IBMVNIC_DRIVER_VERSION, sizeof(info->version)); +	strlcpy(info->fw_version, adapter->fw_version, +		sizeof(info->fw_version));  }  static u32 ibmvnic_get_msglevel(struct net_device *netdev) @@ -1794,6 +2000,27 @@ static void ibmvnic_get_ringparam(struct net_device *netdev,  	ring->rx_jumbo_pending = 0;  } +static int ibmvnic_set_ringparam(struct net_device *netdev, +				 struct ethtool_ringparam *ring) +{ +	struct ibmvnic_adapter *adapter = netdev_priv(netdev); + +	if (ring->rx_pending > adapter->max_rx_add_entries_per_subcrq  || +	    ring->tx_pending > adapter->max_tx_entries_per_subcrq) { +		netdev_err(netdev, "Invalid request.\n"); +		netdev_err(netdev, "Max tx buffers = %llu\n", +			   adapter->max_rx_add_entries_per_subcrq); +		netdev_err(netdev, "Max rx buffers = %llu\n", +			   adapter->max_tx_entries_per_subcrq); +		return -EINVAL; +	} + +	adapter->desired.rx_entries = ring->rx_pending; +	adapter->desired.tx_entries = ring->tx_pending; + +	return wait_for_reset(adapter); +} +  static void ibmvnic_get_channels(struct net_device *netdev,  				 struct ethtool_channels *channels)  { @@ -1809,6 +2036,17 @@ static void ibmvnic_get_channels(struct net_device *netdev,  	channels->combined_count = 0;  } +static int ibmvnic_set_channels(struct net_device *netdev, +				struct ethtool_channels *channels) +{ +	struct ibmvnic_adapter *adapter = netdev_priv(netdev); + +	adapter->desired.rx_queues = channels->rx_count; +	adapter->desired.tx_queues = channels->tx_count; + +	return wait_for_reset(adapter); +} +  static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)  {  	struct ibmvnic_adapter *adapter = netdev_priv(dev); @@ -1905,7 +2143,9 @@ static const struct ethtool_ops ibmvnic_ethtool_ops = {  	.set_msglevel		= ibmvnic_set_msglevel,  	.get_link		= ibmvnic_get_link,  	.get_ringparam		= ibmvnic_get_ringparam, +	.set_ringparam		= ibmvnic_set_ringparam,  	.get_channels		= ibmvnic_get_channels, +	.set_channels		= ibmvnic_set_channels,  	.get_strings            = ibmvnic_get_strings,  	.get_sset_count         = ibmvnic_get_sset_count,  	.get_ethtool_stats	= ibmvnic_get_ethtool_stats, @@ -2371,6 +2611,7 @@ static void ibmvnic_send_req_caps(struct ibmvnic_adapter *adapter, int retry)  {  	struct device *dev = &adapter->vdev->dev;  	union ibmvnic_crq crq; +	int max_entries;  	if (!retry) {  		/* Sub-CRQ entries are 32 byte long */ @@ -2382,21 +2623,60 @@ static void ibmvnic_send_req_caps(struct ibmvnic_adapter *adapter, int retry)  			return;  		} -		/* Get the minimum between the queried max and the entries -		 * that fit in our PAGE_SIZE -		 */ -		adapter->req_tx_entries_per_subcrq = -		    adapter->max_tx_entries_per_subcrq > entries_page ? -		    entries_page : adapter->max_tx_entries_per_subcrq; -		adapter->req_rx_add_entries_per_subcrq = -		    adapter->max_rx_add_entries_per_subcrq > entries_page ? -		    entries_page : adapter->max_rx_add_entries_per_subcrq; - -		adapter->req_tx_queues = adapter->opt_tx_comp_sub_queues; -		adapter->req_rx_queues = adapter->opt_rx_comp_queues; -		adapter->req_rx_add_queues = adapter->max_rx_add_queues; +		if (adapter->desired.mtu) +			adapter->req_mtu = adapter->desired.mtu; +		else +			adapter->req_mtu = adapter->netdev->mtu + ETH_HLEN; + +		if (!adapter->desired.tx_entries) +			adapter->desired.tx_entries = +					adapter->max_tx_entries_per_subcrq; +		if (!adapter->desired.rx_entries) +			adapter->desired.rx_entries = +					adapter->max_rx_add_entries_per_subcrq; + +		max_entries = IBMVNIC_MAX_LTB_SIZE / +			      (adapter->req_mtu + IBMVNIC_BUFFER_HLEN); -		adapter->req_mtu = adapter->netdev->mtu + ETH_HLEN; +		if ((adapter->req_mtu + IBMVNIC_BUFFER_HLEN) * +			adapter->desired.tx_entries > IBMVNIC_MAX_LTB_SIZE) { +			adapter->desired.tx_entries = max_entries; +		} + +		if ((adapter->req_mtu + IBMVNIC_BUFFER_HLEN) * +			adapter->desired.rx_entries > IBMVNIC_MAX_LTB_SIZE) { +			adapter->desired.rx_entries = max_entries; +		} + +		if (adapter->desired.tx_entries) +			adapter->req_tx_entries_per_subcrq = +					adapter->desired.tx_entries; +		else +			adapter->req_tx_entries_per_subcrq = +					adapter->max_tx_entries_per_subcrq; + +		if (adapter->desired.rx_entries) +			adapter->req_rx_add_entries_per_subcrq = +					adapter->desired.rx_entries; +		else +			adapter->req_rx_add_entries_per_subcrq = +					adapter->max_rx_add_entries_per_subcrq; + +		if (adapter->desired.tx_queues) +			adapter->req_tx_queues = +					adapter->desired.tx_queues; +		else +			adapter->req_tx_queues = +					adapter->opt_tx_comp_sub_queues; + +		if (adapter->desired.rx_queues) +			adapter->req_rx_queues = +					adapter->desired.rx_queues; +		else +			adapter->req_rx_queues = +					adapter->opt_rx_comp_queues; + +		adapter->req_rx_add_queues = adapter->max_rx_add_queues;  	}  	memset(&crq, 0, sizeof(crq)); @@ -2609,6 +2889,55 @@ static int send_version_xchg(struct ibmvnic_adapter *adapter)  	return ibmvnic_send_crq(adapter, &crq);  } +struct vnic_login_client_data { +	u8	type; +	__be16	len; +	char	name; +} __packed; + +static int vnic_client_data_len(struct ibmvnic_adapter *adapter) +{ +	int len; + +	/* Calculate the amount of buffer space needed for the +	 * vnic client data in the login buffer. There are four entries, +	 * OS name, LPAR name, device name, and a null last entry. +	 */ +	len = 4 * sizeof(struct vnic_login_client_data); +	len += 6; /* "Linux" plus NULL */ +	len += strlen(utsname()->nodename) + 1; +	len += strlen(adapter->netdev->name) + 1; + +	return len; +} + +static void vnic_add_client_data(struct ibmvnic_adapter *adapter, +				 struct vnic_login_client_data *vlcd) +{ +	const char *os_name = "Linux"; +	int len; + +	/* Type 1 - LPAR OS */ +	vlcd->type = 1; +	len = strlen(os_name) + 1; +	vlcd->len = cpu_to_be16(len); +	strncpy(&vlcd->name, os_name, len); +	vlcd = (struct vnic_login_client_data *)((char *)&vlcd->name + len); + +	/* Type 2 - LPAR name */ +	vlcd->type = 2; +	len = strlen(utsname()->nodename) + 1; +	vlcd->len = cpu_to_be16(len); +	strncpy(&vlcd->name, utsname()->nodename, len); +	vlcd = (struct vnic_login_client_data *)((char *)&vlcd->name + len); + +	/* Type 3 - device name */ +	vlcd->type = 3; +	len = strlen(adapter->netdev->name) + 1; +	vlcd->len = cpu_to_be16(len); +	strncpy(&vlcd->name, adapter->netdev->name, len); +} +  static void send_login(struct ibmvnic_adapter *adapter)  {  	struct ibmvnic_login_rsp_buffer *login_rsp_buffer; @@ -2621,13 +2950,18 @@ static void send_login(struct ibmvnic_adapter *adapter)  	size_t buffer_size;  	__be64 *tx_list_p;  	__be64 *rx_list_p; +	int client_data_len; +	struct vnic_login_client_data *vlcd;  	int i; +	client_data_len = vnic_client_data_len(adapter); +  	buffer_size =  	    sizeof(struct ibmvnic_login_buffer) + -	    sizeof(u64) * (adapter->req_tx_queues + adapter->req_rx_queues); +	    sizeof(u64) * (adapter->req_tx_queues + adapter->req_rx_queues) + +	    client_data_len; -	login_buffer = kmalloc(buffer_size, GFP_ATOMIC); +	login_buffer = kzalloc(buffer_size, GFP_ATOMIC);  	if (!login_buffer)  		goto buf_alloc_failed; @@ -2694,6 +3028,15 @@ static void send_login(struct ibmvnic_adapter *adapter)  		}  	} +	/* Insert vNIC login client data */ +	vlcd = (struct vnic_login_client_data *) +		((char *)rx_list_p + (sizeof(u64) * adapter->req_rx_queues)); +	login_buffer->client_data_offset = +			cpu_to_be32((char *)vlcd - (char *)login_buffer); +	login_buffer->client_data_len = cpu_to_be32(client_data_len); + +	vnic_add_client_data(adapter, vlcd); +  	netdev_dbg(adapter->netdev, "Login Buffer:\n");  	for (i = 0; i < (adapter->login_buf_sz - 1) / 8 + 1; i++) {  		netdev_dbg(adapter->netdev, "%016lx\n", @@ -2872,6 +3215,73 @@ static void send_cap_queries(struct ibmvnic_adapter *adapter)  	ibmvnic_send_crq(adapter, &crq);  } +static void handle_vpd_size_rsp(union ibmvnic_crq *crq, +				struct ibmvnic_adapter *adapter) +{ +	struct device *dev = &adapter->vdev->dev; + +	if (crq->get_vpd_size_rsp.rc.code) { +		dev_err(dev, "Error retrieving VPD size, rc=%x\n", +			crq->get_vpd_size_rsp.rc.code); +		complete(&adapter->fw_done); +		return; +	} + +	adapter->vpd->len = be64_to_cpu(crq->get_vpd_size_rsp.len); +	complete(&adapter->fw_done); +} + +static void handle_vpd_rsp(union ibmvnic_crq *crq, +			   struct ibmvnic_adapter *adapter) +{ +	struct device *dev = &adapter->vdev->dev; +	unsigned char *substr = NULL, *ptr = NULL; +	u8 fw_level_len = 0; + +	memset(adapter->fw_version, 0, 32); + +	dma_unmap_single(dev, adapter->vpd->dma_addr, adapter->vpd->len, +			 DMA_FROM_DEVICE); + +	if (crq->get_vpd_rsp.rc.code) { +		dev_err(dev, "Error retrieving VPD from device, rc=%x\n", +			crq->get_vpd_rsp.rc.code); +		goto complete; +	} + +	/* get the position of the firmware version info +	 * located after the ASCII 'RM' substring in the buffer +	 */ +	substr = strnstr(adapter->vpd->buff, "RM", adapter->vpd->len); +	if (!substr) { +		dev_info(dev, "No FW level provided by VPD\n"); +		goto complete; +	} + +	/* get length of firmware level ASCII substring */ +	if ((substr + 2) < (adapter->vpd->buff + adapter->vpd->len)) { +		fw_level_len = *(substr + 2); +	} else { +		dev_info(dev, "Length of FW substr extrapolated VDP buff\n"); +		goto complete; +	} + +	/* copy firmware version string from vpd into adapter */ +	if ((substr + 3 + fw_level_len) < +	    (adapter->vpd->buff + adapter->vpd->len)) { +		ptr = strncpy((char *)adapter->fw_version, +			      substr + 3, fw_level_len); + +		if (!ptr) +			dev_err(dev, "Failed to isolate FW level string\n"); +	} else { +		dev_info(dev, "FW substr extrapolated VPD buff\n"); +	} + +complete: +	complete(&adapter->fw_done); +} +  static void handle_query_ip_offload_rsp(struct ibmvnic_adapter *adapter)  {  	struct device *dev = &adapter->vdev->dev; @@ -2940,14 +3350,14 @@ static void handle_query_ip_offload_rsp(struct ibmvnic_adapter *adapter)  	adapter->ip_offload_ctrl.udp_ipv4_chksum = buf->udp_ipv4_chksum;  	adapter->ip_offload_ctrl.tcp_ipv6_chksum = buf->tcp_ipv6_chksum;  	adapter->ip_offload_ctrl.udp_ipv6_chksum = buf->udp_ipv6_chksum; +	adapter->ip_offload_ctrl.large_tx_ipv4 = buf->large_tx_ipv4; +	adapter->ip_offload_ctrl.large_tx_ipv6 = buf->large_tx_ipv6; -	/* large_tx/rx disabled for now, additional features needed */ -	adapter->ip_offload_ctrl.large_tx_ipv4 = 0; -	adapter->ip_offload_ctrl.large_tx_ipv6 = 0; +	/* large_rx disabled for now, additional features needed */  	adapter->ip_offload_ctrl.large_rx_ipv4 = 0;  	adapter->ip_offload_ctrl.large_rx_ipv6 = 0; -	adapter->netdev->features = NETIF_F_GSO; +	adapter->netdev->features = NETIF_F_SG | NETIF_F_GSO;  	if (buf->tcp_ipv4_chksum || buf->udp_ipv4_chksum)  		adapter->netdev->features |= NETIF_F_IP_CSUM; @@ -2959,6 +3369,13 @@ static void handle_query_ip_offload_rsp(struct ibmvnic_adapter *adapter)  	    (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)))  		adapter->netdev->features |= NETIF_F_RXCSUM; +	if (buf->large_tx_ipv4) +		adapter->netdev->features |= NETIF_F_TSO; +	if (buf->large_tx_ipv6) +		adapter->netdev->features |= NETIF_F_TSO6; + +	adapter->netdev->hw_features |= adapter->netdev->features; +  	memset(&crq, 0, sizeof(crq));  	crq.control_ip_offload.first = IBMVNIC_CRQ_CMD;  	crq.control_ip_offload.cmd = CONTROL_IP_OFFLOAD; @@ -3210,6 +3627,7 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq,  			    struct ibmvnic_adapter *adapter)  {  	struct device *dev = &adapter->vdev->dev; +	struct net_device *netdev = adapter->netdev;  	struct ibmvnic_login_rsp_buffer *login_rsp = adapter->login_rsp_buf;  	struct ibmvnic_login_buffer *login = adapter->login_buf;  	int i; @@ -3229,6 +3647,8 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq,  		return 0;  	} +	netdev->mtu = adapter->req_mtu - ETH_HLEN; +  	netdev_dbg(adapter->netdev, "Login Response Buffer:\n");  	for (i = 0; i < (adapter->login_rsp_buf_sz - 1) / 8 + 1; i++) {  		netdev_dbg(adapter->netdev, "%016lx\n", @@ -3593,6 +4013,12 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq,  		netdev_dbg(netdev, "Got Collect firmware trace Response\n");  		complete(&adapter->fw_done);  		break; +	case GET_VPD_SIZE_RSP: +		handle_vpd_size_rsp(crq, adapter); +		break; +	case GET_VPD_RSP: +		handle_vpd_rsp(crq, adapter); +		break;  	default:  		netdev_err(netdev, "Got an invalid cmd type 0x%02x\n",  			   gen_crq->cmd); @@ -3784,7 +4210,7 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter)  	unsigned long timeout = msecs_to_jiffies(30000);  	int rc; -	if (adapter->resetting) { +	if (adapter->resetting && !adapter->wait_for_reset) {  		rc = ibmvnic_reset_crq(adapter);  		if (!rc)  			rc = vio_enable_interrupts(adapter->vdev); @@ -3818,7 +4244,7 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter)  		return -1;  	} -	if (adapter->resetting) +	if (adapter->resetting && !adapter->wait_for_reset)  		rc = reset_sub_crq_queues(adapter);  	else  		rc = init_sub_crqs(adapter); @@ -3887,6 +4313,8 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)  	mutex_init(&adapter->rwi_lock);  	adapter->resetting = false; +	adapter->mac_change_pending = false; +  	do {  		rc = ibmvnic_init(adapter);  		if (rc && rc != EAGAIN) @@ -3894,11 +4322,14 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)  	} while (rc == EAGAIN);  	netdev->mtu = adapter->req_mtu - ETH_HLEN; +	netdev->min_mtu = adapter->min_mtu - ETH_HLEN; +	netdev->max_mtu = adapter->max_mtu - ETH_HLEN;  	rc = device_create_file(&dev->dev, &dev_attr_failover);  	if (rc)  		goto ibmvnic_init_fail; +	netif_carrier_off(netdev);  	rc = register_netdev(netdev);  	if (rc) {  		dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc); @@ -3907,6 +4338,9 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)  	dev_info(&dev->dev, "ibmvnic registered\n");  	adapter->state = VNIC_PROBED; + +	adapter->wait_for_reset = false; +  	return 0;  ibmvnic_register_fail: diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index d02257ccc377..4487f1e2c266 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -30,6 +30,8 @@  #define IBMVNIC_DRIVER_VERSION	"1.0.1"  #define IBMVNIC_INVALID_MAP	-1  #define IBMVNIC_STATS_TIMEOUT	1 +#define IBMVNIC_INIT_FAILED	2 +  /* basic structures plus 100 2k buffers */  #define IBMVNIC_IO_ENTITLEMENT_DEFAULT	610305 @@ -39,6 +41,12 @@  #define IBMVNIC_BUFFS_PER_POOL	100  #define IBMVNIC_MAX_TX_QUEUES	5 +#define IBMVNIC_TSO_BUF_SZ	65536 +#define IBMVNIC_TSO_BUFS	64 + +#define IBMVNIC_MAX_LTB_SIZE ((1 << (MAX_ORDER - 1)) * PAGE_SIZE) +#define IBMVNIC_BUFFER_HLEN 500 +  struct ibmvnic_login_buffer {  	__be32 len;  	__be32 version; @@ -49,6 +57,8 @@ struct ibmvnic_login_buffer {  	__be32 off_rxcomp_subcrqs;  	__be32 login_rsp_ioba;  	__be32 login_rsp_len; +	__be32 client_data_offset; +	__be32 client_data_len;  } __packed __aligned(8);  struct ibmvnic_login_rsp_buffer { @@ -550,6 +560,12 @@ struct ibmvnic_multicast_ctrl {  	struct ibmvnic_rc rc;  } __packed __aligned(8); +struct ibmvnic_get_vpd_size { +	u8 first; +	u8 cmd; +	u8 reserved[14]; +} __packed __aligned(8); +  struct ibmvnic_get_vpd_size_rsp {  	u8 first;  	u8 cmd; @@ -567,6 +583,13 @@ struct ibmvnic_get_vpd {  	u8 reserved[4];  } __packed __aligned(8); +struct ibmvnic_get_vpd_rsp { +	u8 first; +	u8 cmd; +	u8 reserved[10]; +	struct ibmvnic_rc rc; +} __packed __aligned(8); +  struct ibmvnic_acl_change_indication {  	u8 first;  	u8 cmd; @@ -692,10 +715,10 @@ union ibmvnic_crq {  	struct ibmvnic_change_mac_addr change_mac_addr_rsp;  	struct ibmvnic_multicast_ctrl multicast_ctrl;  	struct ibmvnic_multicast_ctrl multicast_ctrl_rsp; -	struct ibmvnic_generic_crq get_vpd_size; +	struct ibmvnic_get_vpd_size get_vpd_size;  	struct ibmvnic_get_vpd_size_rsp get_vpd_size_rsp;  	struct ibmvnic_get_vpd get_vpd; -	struct ibmvnic_generic_crq get_vpd_rsp; +	struct ibmvnic_get_vpd_rsp get_vpd_rsp;  	struct ibmvnic_acl_change_indication acl_change_indication;  	struct ibmvnic_acl_query acl_query;  	struct ibmvnic_generic_crq acl_query_rsp; @@ -896,6 +919,8 @@ struct ibmvnic_tx_pool {  	wait_queue_head_t ibmvnic_tx_comp_q;  	struct task_struct *work_thread;  	struct ibmvnic_long_term_buff long_term_buff; +	struct ibmvnic_long_term_buff tso_ltb; +	int tso_index;  };  struct ibmvnic_rx_buff { @@ -927,6 +952,12 @@ struct ibmvnic_error_buff {  	__be32 error_id;  }; +struct ibmvnic_vpd { +	unsigned char *buff; +	dma_addr_t dma_addr; +	u64 len; +}; +  enum vnic_state {VNIC_PROBING = 1,  		 VNIC_PROBED,  		 VNIC_OPENING, @@ -940,13 +971,23 @@ enum ibmvnic_reset_reason {VNIC_RESET_FAILOVER = 1,  			   VNIC_RESET_MOBILITY,  			   VNIC_RESET_FATAL,  			   VNIC_RESET_NON_FATAL, -			   VNIC_RESET_TIMEOUT}; +			   VNIC_RESET_TIMEOUT, +			   VNIC_RESET_CHANGE_PARAM};  struct ibmvnic_rwi {  	enum ibmvnic_reset_reason reset_reason;  	struct list_head list;  }; +struct ibmvnic_tunables { +	u64 rx_queues; +	u64 tx_queues; +	u64 rx_entries; +	u64 tx_entries; +	u64 mtu; +	struct sockaddr mac; +}; +  struct ibmvnic_adapter {  	struct vio_dev *vdev;  	struct net_device *netdev; @@ -958,6 +999,10 @@ struct ibmvnic_adapter {  	dma_addr_t ip_offload_ctrl_tok;  	u32 msg_enable; +	/* Vital Product Data (VPD) */ +	struct ibmvnic_vpd *vpd; +	char fw_version[32]; +  	/* Statistics */  	struct ibmvnic_statistics stats;  	dma_addr_t stats_token; @@ -1007,6 +1052,10 @@ struct ibmvnic_adapter {  	struct completion fw_done;  	int fw_done_rc; +	struct completion reset_done; +	int reset_done_rc; +	bool wait_for_reset; +  	/* partner capabilities */  	u64 min_tx_queues;  	u64 min_rx_queues; @@ -1051,4 +1100,9 @@ struct ibmvnic_adapter {  	struct work_struct ibmvnic_reset;  	bool resetting;  	bool napi_enabled, from_passive_init; + +	bool mac_change_pending; + +	struct ibmvnic_tunables desired; +	struct ibmvnic_tunables fallback;  };  | 

