673 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			673 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**********************************************************************
 | |
|  * Author: Cavium, Inc.
 | |
|  *
 | |
|  * Contact: support@cavium.com
 | |
|  *          Please include "LiquidIO" in the subject.
 | |
|  *
 | |
|  * Copyright (c) 2003-2017 Cavium, Inc.
 | |
|  *
 | |
|  * This file is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License, Version 2, as
 | |
|  * published by the Free Software Foundation.
 | |
|  *
 | |
|  * This file is distributed in the hope that it will be useful, but
 | |
|  * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
 | |
|  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 | |
|  * NONINFRINGEMENT.  See the GNU General Public License for more details.
 | |
|  ***********************************************************************/
 | |
| #include <linux/pci.h>
 | |
| #include <linux/if_vlan.h>
 | |
| #include "liquidio_common.h"
 | |
| #include "octeon_droq.h"
 | |
| #include "octeon_iq.h"
 | |
| #include "response_manager.h"
 | |
| #include "octeon_device.h"
 | |
| #include "octeon_nic.h"
 | |
| #include "octeon_main.h"
 | |
| #include "octeon_network.h"
 | |
| #include "lio_vf_rep.h"
 | |
| 
 | |
| static int lio_vf_rep_open(struct net_device *ndev);
 | |
| static int lio_vf_rep_stop(struct net_device *ndev);
 | |
| static netdev_tx_t lio_vf_rep_pkt_xmit(struct sk_buff *skb,
 | |
| 				       struct net_device *ndev);
 | |
| static void lio_vf_rep_tx_timeout(struct net_device *netdev, unsigned int txqueue);
 | |
| static int lio_vf_rep_phys_port_name(struct net_device *dev,
 | |
| 				     char *buf, size_t len);
 | |
| static void lio_vf_rep_get_stats64(struct net_device *dev,
 | |
| 				   struct rtnl_link_stats64 *stats64);
 | |
| static int lio_vf_rep_change_mtu(struct net_device *ndev, int new_mtu);
 | |
| static int lio_vf_get_port_parent_id(struct net_device *dev,
 | |
| 				     struct netdev_phys_item_id *ppid);
 | |
| 
 | |
| static const struct net_device_ops lio_vf_rep_ndev_ops = {
 | |
| 	.ndo_open = lio_vf_rep_open,
 | |
| 	.ndo_stop = lio_vf_rep_stop,
 | |
| 	.ndo_start_xmit = lio_vf_rep_pkt_xmit,
 | |
| 	.ndo_tx_timeout = lio_vf_rep_tx_timeout,
 | |
| 	.ndo_get_phys_port_name = lio_vf_rep_phys_port_name,
 | |
| 	.ndo_get_stats64 = lio_vf_rep_get_stats64,
 | |
| 	.ndo_change_mtu = lio_vf_rep_change_mtu,
 | |
| 	.ndo_get_port_parent_id = lio_vf_get_port_parent_id,
 | |
| };
 | |
| 
 | |
| static int
 | |
| lio_vf_rep_send_soft_command(struct octeon_device *oct,
 | |
| 			     void *req, int req_size,
 | |
| 			     void *resp, int resp_size)
 | |
| {
 | |
| 	int tot_resp_size = sizeof(struct lio_vf_rep_resp) + resp_size;
 | |
| 	struct octeon_soft_command *sc = NULL;
 | |
| 	struct lio_vf_rep_resp *rep_resp;
 | |
| 	void *sc_req;
 | |
| 	int err;
 | |
| 
 | |
| 	sc = (struct octeon_soft_command *)
 | |
| 		octeon_alloc_soft_command(oct, req_size,
 | |
| 					  tot_resp_size, 0);
 | |
| 	if (!sc)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	init_completion(&sc->complete);
 | |
| 	sc->sc_status = OCTEON_REQUEST_PENDING;
 | |
| 
 | |
| 	sc_req = (struct lio_vf_rep_req *)sc->virtdptr;
 | |
| 	memcpy(sc_req, req, req_size);
 | |
| 
 | |
| 	rep_resp = (struct lio_vf_rep_resp *)sc->virtrptr;
 | |
| 	memset(rep_resp, 0, tot_resp_size);
 | |
| 	WRITE_ONCE(rep_resp->status, 1);
 | |
| 
 | |
| 	sc->iq_no = 0;
 | |
| 	octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
 | |
| 				    OPCODE_NIC_VF_REP_CMD, 0, 0, 0);
 | |
| 
 | |
| 	err = octeon_send_soft_command(oct, sc);
 | |
| 	if (err == IQ_SEND_FAILED)
 | |
| 		goto free_buff;
 | |
| 
 | |
| 	err = wait_for_sc_completion_timeout(oct, sc, 0);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	err = READ_ONCE(rep_resp->status) ? -EBUSY : 0;
 | |
| 	if (err)
 | |
| 		dev_err(&oct->pci_dev->dev, "VF rep send config failed\n");
 | |
| 	else if (resp)
 | |
| 		memcpy(resp, (rep_resp + 1), resp_size);
 | |
| 
 | |
| 	WRITE_ONCE(sc->caller_is_done, true);
 | |
| 	return err;
 | |
| 
 | |
| free_buff:
 | |
| 	octeon_free_soft_command(oct, sc);
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static int
 | |
| lio_vf_rep_open(struct net_device *ndev)
 | |
| {
 | |
| 	struct lio_vf_rep_desc *vf_rep = netdev_priv(ndev);
 | |
| 	struct lio_vf_rep_req rep_cfg;
 | |
| 	struct octeon_device *oct;
 | |
| 	int ret;
 | |
| 
 | |
| 	oct = vf_rep->oct;
 | |
| 
 | |
| 	memset(&rep_cfg, 0, sizeof(rep_cfg));
 | |
| 	rep_cfg.req_type = LIO_VF_REP_REQ_STATE;
 | |
| 	rep_cfg.ifidx = vf_rep->ifidx;
 | |
| 	rep_cfg.rep_state.state = LIO_VF_REP_STATE_UP;
 | |
| 
 | |
| 	ret = lio_vf_rep_send_soft_command(oct, &rep_cfg,
 | |
| 					   sizeof(rep_cfg), NULL, 0);
 | |
| 
 | |
| 	if (ret) {
 | |
| 		dev_err(&oct->pci_dev->dev,
 | |
| 			"VF_REP open failed with err %d\n", ret);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	atomic_set(&vf_rep->ifstate, (atomic_read(&vf_rep->ifstate) |
 | |
| 				      LIO_IFSTATE_RUNNING));
 | |
| 
 | |
| 	netif_carrier_on(ndev);
 | |
| 	netif_start_queue(ndev);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| lio_vf_rep_stop(struct net_device *ndev)
 | |
| {
 | |
| 	struct lio_vf_rep_desc *vf_rep = netdev_priv(ndev);
 | |
| 	struct lio_vf_rep_req rep_cfg;
 | |
| 	struct octeon_device *oct;
 | |
| 	int ret;
 | |
| 
 | |
| 	oct = vf_rep->oct;
 | |
| 
 | |
| 	memset(&rep_cfg, 0, sizeof(rep_cfg));
 | |
| 	rep_cfg.req_type = LIO_VF_REP_REQ_STATE;
 | |
| 	rep_cfg.ifidx = vf_rep->ifidx;
 | |
| 	rep_cfg.rep_state.state = LIO_VF_REP_STATE_DOWN;
 | |
| 
 | |
| 	ret = lio_vf_rep_send_soft_command(oct, &rep_cfg,
 | |
| 					   sizeof(rep_cfg), NULL, 0);
 | |
| 
 | |
| 	if (ret) {
 | |
| 		dev_err(&oct->pci_dev->dev,
 | |
| 			"VF_REP dev stop failed with err %d\n", ret);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	atomic_set(&vf_rep->ifstate, (atomic_read(&vf_rep->ifstate) &
 | |
| 				      ~LIO_IFSTATE_RUNNING));
 | |
| 
 | |
| 	netif_tx_disable(ndev);
 | |
| 	netif_carrier_off(ndev);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| lio_vf_rep_tx_timeout(struct net_device *ndev, unsigned int txqueue)
 | |
| {
 | |
| 	netif_trans_update(ndev);
 | |
| 
 | |
| 	netif_wake_queue(ndev);
 | |
| }
 | |
| 
 | |
| static void
 | |
| lio_vf_rep_get_stats64(struct net_device *dev,
 | |
| 		       struct rtnl_link_stats64 *stats64)
 | |
| {
 | |
| 	struct lio_vf_rep_desc *vf_rep = netdev_priv(dev);
 | |
| 
 | |
| 	/* Swap tx and rx stats as VF rep is a switch port */
 | |
| 	stats64->tx_packets = vf_rep->stats.rx_packets;
 | |
| 	stats64->tx_bytes   = vf_rep->stats.rx_bytes;
 | |
| 	stats64->tx_dropped = vf_rep->stats.rx_dropped;
 | |
| 
 | |
| 	stats64->rx_packets = vf_rep->stats.tx_packets;
 | |
| 	stats64->rx_bytes   = vf_rep->stats.tx_bytes;
 | |
| 	stats64->rx_dropped = vf_rep->stats.tx_dropped;
 | |
| }
 | |
| 
 | |
| static int
 | |
| lio_vf_rep_change_mtu(struct net_device *ndev, int new_mtu)
 | |
| {
 | |
| 	struct lio_vf_rep_desc *vf_rep = netdev_priv(ndev);
 | |
| 	struct lio_vf_rep_req rep_cfg;
 | |
| 	struct octeon_device *oct;
 | |
| 	int ret;
 | |
| 
 | |
| 	oct = vf_rep->oct;
 | |
| 
 | |
| 	memset(&rep_cfg, 0, sizeof(rep_cfg));
 | |
| 	rep_cfg.req_type = LIO_VF_REP_REQ_MTU;
 | |
| 	rep_cfg.ifidx = vf_rep->ifidx;
 | |
| 	rep_cfg.rep_mtu.mtu = cpu_to_be32(new_mtu);
 | |
| 
 | |
| 	ret = lio_vf_rep_send_soft_command(oct, &rep_cfg,
 | |
| 					   sizeof(rep_cfg), NULL, 0);
 | |
| 	if (ret) {
 | |
| 		dev_err(&oct->pci_dev->dev,
 | |
| 			"Change MTU failed with err %d\n", ret);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	WRITE_ONCE(ndev->mtu, new_mtu);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| lio_vf_rep_phys_port_name(struct net_device *dev,
 | |
| 			  char *buf, size_t len)
 | |
| {
 | |
| 	struct lio_vf_rep_desc *vf_rep = netdev_priv(dev);
 | |
| 	struct octeon_device *oct = vf_rep->oct;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = snprintf(buf, len, "pf%dvf%d", oct->pf_num,
 | |
| 		       vf_rep->ifidx - oct->pf_num * 64 - 1);
 | |
| 	if (ret >= len)
 | |
| 		return -EOPNOTSUPP;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct net_device *
 | |
| lio_vf_rep_get_ndev(struct octeon_device *oct, int ifidx)
 | |
| {
 | |
| 	int vf_id, max_vfs = CN23XX_MAX_VFS_PER_PF + 1;
 | |
| 	int vfid_mask = max_vfs - 1;
 | |
| 
 | |
| 	if (ifidx <= oct->pf_num * max_vfs ||
 | |
| 	    ifidx >= oct->pf_num * max_vfs + max_vfs)
 | |
| 		return NULL;
 | |
| 
 | |
| 	/* ifidx 1-63 for PF0 VFs
 | |
| 	 * ifidx 65-127 for PF1 VFs
 | |
| 	 */
 | |
| 	vf_id = (ifidx & vfid_mask) - 1;
 | |
| 
 | |
| 	return oct->vf_rep_list.ndev[vf_id];
 | |
| }
 | |
| 
 | |
| static void
 | |
| lio_vf_rep_copy_packet(struct octeon_device *oct,
 | |
| 		       struct sk_buff *skb,
 | |
| 		       int len)
 | |
| {
 | |
| 	if (likely(len > MIN_SKB_SIZE)) {
 | |
| 		struct octeon_skb_page_info *pg_info;
 | |
| 		unsigned char *va;
 | |
| 
 | |
| 		pg_info = ((struct octeon_skb_page_info *)(skb->cb));
 | |
| 		if (pg_info->page) {
 | |
| 			va = page_address(pg_info->page) +
 | |
| 				pg_info->page_offset;
 | |
| 			memcpy(skb->data, va, MIN_SKB_SIZE);
 | |
| 			skb_put(skb, MIN_SKB_SIZE);
 | |
| 			skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
 | |
| 					pg_info->page,
 | |
| 					pg_info->page_offset + MIN_SKB_SIZE,
 | |
| 					len - MIN_SKB_SIZE,
 | |
| 					LIO_RXBUFFER_SZ);
 | |
| 		}
 | |
| 	} else {
 | |
| 		struct octeon_skb_page_info *pg_info =
 | |
| 			((struct octeon_skb_page_info *)(skb->cb));
 | |
| 
 | |
| 		skb_copy_to_linear_data(skb, page_address(pg_info->page) +
 | |
| 					pg_info->page_offset, len);
 | |
| 		skb_put(skb, len);
 | |
| 		put_page(pg_info->page);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int
 | |
| lio_vf_rep_pkt_recv(struct octeon_recv_info *recv_info, void *buf)
 | |
| {
 | |
| 	struct octeon_recv_pkt *recv_pkt = recv_info->recv_pkt;
 | |
| 	struct lio_vf_rep_desc *vf_rep;
 | |
| 	struct net_device *vf_ndev;
 | |
| 	struct octeon_device *oct;
 | |
| 	union octeon_rh *rh;
 | |
| 	struct sk_buff *skb;
 | |
| 	int i, ifidx;
 | |
| 
 | |
| 	oct = lio_get_device(recv_pkt->octeon_id);
 | |
| 	if (!oct)
 | |
| 		goto free_buffers;
 | |
| 
 | |
| 	skb = recv_pkt->buffer_ptr[0];
 | |
| 	rh = &recv_pkt->rh;
 | |
| 	ifidx = rh->r.ossp;
 | |
| 
 | |
| 	vf_ndev = lio_vf_rep_get_ndev(oct, ifidx);
 | |
| 	if (!vf_ndev)
 | |
| 		goto free_buffers;
 | |
| 
 | |
| 	vf_rep = netdev_priv(vf_ndev);
 | |
| 	if (!(atomic_read(&vf_rep->ifstate) & LIO_IFSTATE_RUNNING) ||
 | |
| 	    recv_pkt->buffer_count > 1)
 | |
| 		goto free_buffers;
 | |
| 
 | |
| 	skb->dev = vf_ndev;
 | |
| 
 | |
| 	/* Multiple buffers are not used for vf_rep packets.
 | |
| 	 * So just buffer_size[0] is valid.
 | |
| 	 */
 | |
| 	lio_vf_rep_copy_packet(oct, skb, recv_pkt->buffer_size[0]);
 | |
| 
 | |
| 	skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT);
 | |
| 	skb->protocol = eth_type_trans(skb, skb->dev);
 | |
| 	skb->ip_summed = CHECKSUM_NONE;
 | |
| 
 | |
| 	netif_rx(skb);
 | |
| 
 | |
| 	octeon_free_recv_info(recv_info);
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| free_buffers:
 | |
| 	for (i = 0; i < recv_pkt->buffer_count; i++)
 | |
| 		recv_buffer_free(recv_pkt->buffer_ptr[i]);
 | |
| 
 | |
| 	octeon_free_recv_info(recv_info);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| lio_vf_rep_packet_sent_callback(struct octeon_device *oct,
 | |
| 				u32 status, void *buf)
 | |
| {
 | |
| 	struct octeon_soft_command *sc = (struct octeon_soft_command *)buf;
 | |
| 	struct sk_buff *skb = sc->ctxptr;
 | |
| 	struct net_device *ndev = skb->dev;
 | |
| 	u32 iq_no;
 | |
| 
 | |
| 	dma_unmap_single(&oct->pci_dev->dev, sc->dmadptr,
 | |
| 			 sc->datasize, DMA_TO_DEVICE);
 | |
| 	dev_kfree_skb_any(skb);
 | |
| 	iq_no = sc->iq_no;
 | |
| 	octeon_free_soft_command(oct, sc);
 | |
| 
 | |
| 	if (octnet_iq_is_full(oct, iq_no))
 | |
| 		return;
 | |
| 
 | |
| 	if (netif_queue_stopped(ndev))
 | |
| 		netif_wake_queue(ndev);
 | |
| }
 | |
| 
 | |
| static netdev_tx_t
 | |
| lio_vf_rep_pkt_xmit(struct sk_buff *skb, struct net_device *ndev)
 | |
| {
 | |
| 	struct lio_vf_rep_desc *vf_rep = netdev_priv(ndev);
 | |
| 	struct net_device *parent_ndev = vf_rep->parent_ndev;
 | |
| 	struct octeon_device *oct = vf_rep->oct;
 | |
| 	struct octeon_instr_pki_ih3 *pki_ih3;
 | |
| 	struct octeon_soft_command *sc;
 | |
| 	struct lio *parent_lio;
 | |
| 	int status;
 | |
| 
 | |
| 	parent_lio = GET_LIO(parent_ndev);
 | |
| 
 | |
| 	if (!(atomic_read(&vf_rep->ifstate) & LIO_IFSTATE_RUNNING) ||
 | |
| 	    skb->len <= 0)
 | |
| 		goto xmit_failed;
 | |
| 
 | |
| 	if (octnet_iq_is_full(vf_rep->oct, parent_lio->txq)) {
 | |
| 		dev_err(&oct->pci_dev->dev, "VF rep: Device IQ full\n");
 | |
| 		netif_stop_queue(ndev);
 | |
| 		return NETDEV_TX_BUSY;
 | |
| 	}
 | |
| 
 | |
| 	sc = (struct octeon_soft_command *)
 | |
| 		octeon_alloc_soft_command(oct, 0, 16, 0);
 | |
| 	if (!sc) {
 | |
| 		dev_err(&oct->pci_dev->dev, "VF rep: Soft command alloc failed\n");
 | |
| 		goto xmit_failed;
 | |
| 	}
 | |
| 
 | |
| 	/* Multiple buffers are not used for vf_rep packets. */
 | |
| 	if (skb_shinfo(skb)->nr_frags != 0) {
 | |
| 		dev_err(&oct->pci_dev->dev, "VF rep: nr_frags != 0. Dropping packet\n");
 | |
| 		octeon_free_soft_command(oct, sc);
 | |
| 		goto xmit_failed;
 | |
| 	}
 | |
| 
 | |
| 	sc->dmadptr = dma_map_single(&oct->pci_dev->dev,
 | |
| 				     skb->data, skb->len, DMA_TO_DEVICE);
 | |
| 	if (dma_mapping_error(&oct->pci_dev->dev, sc->dmadptr)) {
 | |
| 		dev_err(&oct->pci_dev->dev, "VF rep: DMA mapping failed\n");
 | |
| 		octeon_free_soft_command(oct, sc);
 | |
| 		goto xmit_failed;
 | |
| 	}
 | |
| 
 | |
| 	sc->virtdptr = skb->data;
 | |
| 	sc->datasize = skb->len;
 | |
| 	sc->ctxptr = skb;
 | |
| 	sc->iq_no = parent_lio->txq;
 | |
| 
 | |
| 	octeon_prepare_soft_command(oct, sc, OPCODE_NIC, OPCODE_NIC_VF_REP_PKT,
 | |
| 				    vf_rep->ifidx, 0, 0);
 | |
| 	pki_ih3 = (struct octeon_instr_pki_ih3 *)&sc->cmd.cmd3.pki_ih3;
 | |
| 	pki_ih3->tagtype = ORDERED_TAG;
 | |
| 
 | |
| 	sc->callback = lio_vf_rep_packet_sent_callback;
 | |
| 	sc->callback_arg = sc;
 | |
| 
 | |
| 	status = octeon_send_soft_command(oct, sc);
 | |
| 	if (status == IQ_SEND_FAILED) {
 | |
| 		dma_unmap_single(&oct->pci_dev->dev, sc->dmadptr,
 | |
| 				 sc->datasize, DMA_TO_DEVICE);
 | |
| 		octeon_free_soft_command(oct, sc);
 | |
| 		goto xmit_failed;
 | |
| 	}
 | |
| 
 | |
| 	if (status == IQ_SEND_STOP)
 | |
| 		netif_stop_queue(ndev);
 | |
| 
 | |
| 	netif_trans_update(ndev);
 | |
| 
 | |
| 	return NETDEV_TX_OK;
 | |
| 
 | |
| xmit_failed:
 | |
| 	dev_kfree_skb_any(skb);
 | |
| 
 | |
| 	return NETDEV_TX_OK;
 | |
| }
 | |
| 
 | |
| static int lio_vf_get_port_parent_id(struct net_device *dev,
 | |
| 				     struct netdev_phys_item_id *ppid)
 | |
| {
 | |
| 	struct lio_vf_rep_desc *vf_rep = netdev_priv(dev);
 | |
| 	struct net_device *parent_ndev = vf_rep->parent_ndev;
 | |
| 	struct lio *lio = GET_LIO(parent_ndev);
 | |
| 
 | |
| 	ppid->id_len = ETH_ALEN;
 | |
| 	ether_addr_copy(ppid->id, (void *)&lio->linfo.hw_addr + 2);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| lio_vf_rep_fetch_stats(struct work_struct *work)
 | |
| {
 | |
| 	struct cavium_wk *wk = (struct cavium_wk *)work;
 | |
| 	struct lio_vf_rep_desc *vf_rep = wk->ctxptr;
 | |
| 	struct lio_vf_rep_stats stats;
 | |
| 	struct lio_vf_rep_req rep_cfg;
 | |
| 	struct octeon_device *oct;
 | |
| 	int ret;
 | |
| 
 | |
| 	oct = vf_rep->oct;
 | |
| 
 | |
| 	memset(&rep_cfg, 0, sizeof(rep_cfg));
 | |
| 	rep_cfg.req_type = LIO_VF_REP_REQ_STATS;
 | |
| 	rep_cfg.ifidx = vf_rep->ifidx;
 | |
| 
 | |
| 	ret = lio_vf_rep_send_soft_command(oct, &rep_cfg, sizeof(rep_cfg),
 | |
| 					   &stats, sizeof(stats));
 | |
| 
 | |
| 	if (!ret) {
 | |
| 		octeon_swap_8B_data((u64 *)&stats, (sizeof(stats) >> 3));
 | |
| 		memcpy(&vf_rep->stats, &stats, sizeof(stats));
 | |
| 	}
 | |
| 
 | |
| 	schedule_delayed_work(&vf_rep->stats_wk.work,
 | |
| 			      msecs_to_jiffies(LIO_VF_REP_STATS_POLL_TIME_MS));
 | |
| }
 | |
| 
 | |
| int
 | |
| lio_vf_rep_create(struct octeon_device *oct)
 | |
| {
 | |
| 	struct lio_vf_rep_desc *vf_rep;
 | |
| 	struct net_device *ndev;
 | |
| 	int i, num_vfs;
 | |
| 
 | |
| 	if (oct->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (!oct->sriov_info.sriov_enabled)
 | |
| 		return 0;
 | |
| 
 | |
| 	num_vfs = oct->sriov_info.num_vfs_alloced;
 | |
| 
 | |
| 	oct->vf_rep_list.num_vfs = 0;
 | |
| 	for (i = 0; i < num_vfs; i++) {
 | |
| 		ndev = alloc_etherdev(sizeof(struct lio_vf_rep_desc));
 | |
| 
 | |
| 		if (!ndev) {
 | |
| 			dev_err(&oct->pci_dev->dev,
 | |
| 				"VF rep device %d creation failed\n", i);
 | |
| 			goto cleanup;
 | |
| 		}
 | |
| 
 | |
| 		ndev->min_mtu = LIO_MIN_MTU_SIZE;
 | |
| 		ndev->max_mtu = LIO_MAX_MTU_SIZE;
 | |
| 		ndev->netdev_ops = &lio_vf_rep_ndev_ops;
 | |
| 
 | |
| 		vf_rep = netdev_priv(ndev);
 | |
| 		memset(vf_rep, 0, sizeof(*vf_rep));
 | |
| 
 | |
| 		vf_rep->ndev = ndev;
 | |
| 		vf_rep->oct = oct;
 | |
| 		vf_rep->parent_ndev = oct->props[0].netdev;
 | |
| 		vf_rep->ifidx = (oct->pf_num * 64) + i + 1;
 | |
| 
 | |
| 		eth_hw_addr_random(ndev);
 | |
| 
 | |
| 		if (register_netdev(ndev)) {
 | |
| 			dev_err(&oct->pci_dev->dev, "VF rep nerdev registration failed\n");
 | |
| 
 | |
| 			free_netdev(ndev);
 | |
| 			goto cleanup;
 | |
| 		}
 | |
| 
 | |
| 		netif_carrier_off(ndev);
 | |
| 
 | |
| 		INIT_DELAYED_WORK(&vf_rep->stats_wk.work,
 | |
| 				  lio_vf_rep_fetch_stats);
 | |
| 		vf_rep->stats_wk.ctxptr = (void *)vf_rep;
 | |
| 		schedule_delayed_work(&vf_rep->stats_wk.work,
 | |
| 				      msecs_to_jiffies
 | |
| 				      (LIO_VF_REP_STATS_POLL_TIME_MS));
 | |
| 		oct->vf_rep_list.num_vfs++;
 | |
| 		oct->vf_rep_list.ndev[i] = ndev;
 | |
| 	}
 | |
| 
 | |
| 	if (octeon_register_dispatch_fn(oct, OPCODE_NIC,
 | |
| 					OPCODE_NIC_VF_REP_PKT,
 | |
| 					lio_vf_rep_pkt_recv, oct)) {
 | |
| 		dev_err(&oct->pci_dev->dev, "VF rep Dispatch func registration failed\n");
 | |
| 
 | |
| 		goto cleanup;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| cleanup:
 | |
| 	for (i = 0; i < oct->vf_rep_list.num_vfs; i++) {
 | |
| 		ndev = oct->vf_rep_list.ndev[i];
 | |
| 		oct->vf_rep_list.ndev[i] = NULL;
 | |
| 		if (ndev) {
 | |
| 			vf_rep = netdev_priv(ndev);
 | |
| 			cancel_delayed_work_sync
 | |
| 				(&vf_rep->stats_wk.work);
 | |
| 			unregister_netdev(ndev);
 | |
| 			free_netdev(ndev);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	oct->vf_rep_list.num_vfs = 0;
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| void
 | |
| lio_vf_rep_destroy(struct octeon_device *oct)
 | |
| {
 | |
| 	struct lio_vf_rep_desc *vf_rep;
 | |
| 	struct net_device *ndev;
 | |
| 	int i;
 | |
| 
 | |
| 	if (oct->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV)
 | |
| 		return;
 | |
| 
 | |
| 	if (!oct->sriov_info.sriov_enabled)
 | |
| 		return;
 | |
| 
 | |
| 	for (i = 0; i < oct->vf_rep_list.num_vfs; i++) {
 | |
| 		ndev = oct->vf_rep_list.ndev[i];
 | |
| 		oct->vf_rep_list.ndev[i] = NULL;
 | |
| 		if (ndev) {
 | |
| 			vf_rep = netdev_priv(ndev);
 | |
| 			cancel_delayed_work_sync
 | |
| 				(&vf_rep->stats_wk.work);
 | |
| 			netif_tx_disable(ndev);
 | |
| 			netif_carrier_off(ndev);
 | |
| 
 | |
| 			unregister_netdev(ndev);
 | |
| 			free_netdev(ndev);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	oct->vf_rep_list.num_vfs = 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| lio_vf_rep_netdev_event(struct notifier_block *nb,
 | |
| 			unsigned long event, void *ptr)
 | |
| {
 | |
| 	struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
 | |
| 	struct lio_vf_rep_desc *vf_rep;
 | |
| 	struct lio_vf_rep_req rep_cfg;
 | |
| 	struct octeon_device *oct;
 | |
| 	int ret;
 | |
| 
 | |
| 	switch (event) {
 | |
| 	case NETDEV_REGISTER:
 | |
| 	case NETDEV_CHANGENAME:
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		return NOTIFY_DONE;
 | |
| 	}
 | |
| 
 | |
| 	if (ndev->netdev_ops != &lio_vf_rep_ndev_ops)
 | |
| 		return NOTIFY_DONE;
 | |
| 
 | |
| 	vf_rep = netdev_priv(ndev);
 | |
| 	oct = vf_rep->oct;
 | |
| 
 | |
| 	if (strlen(ndev->name) > LIO_IF_NAME_SIZE) {
 | |
| 		dev_err(&oct->pci_dev->dev,
 | |
| 			"Device name change sync failed as the size is > %d\n",
 | |
| 			LIO_IF_NAME_SIZE);
 | |
| 		return NOTIFY_DONE;
 | |
| 	}
 | |
| 
 | |
| 	memset(&rep_cfg, 0, sizeof(rep_cfg));
 | |
| 	rep_cfg.req_type = LIO_VF_REP_REQ_DEVNAME;
 | |
| 	rep_cfg.ifidx = vf_rep->ifidx;
 | |
| 	strscpy(rep_cfg.rep_name.name, ndev->name,
 | |
| 		sizeof(rep_cfg.rep_name.name));
 | |
| 
 | |
| 	ret = lio_vf_rep_send_soft_command(oct, &rep_cfg,
 | |
| 					   sizeof(rep_cfg), NULL, 0);
 | |
| 	if (ret)
 | |
| 		dev_err(&oct->pci_dev->dev,
 | |
| 			"vf_rep netdev name change failed with err %d\n", ret);
 | |
| 
 | |
| 	return NOTIFY_DONE;
 | |
| }
 | |
| 
 | |
| static struct notifier_block lio_vf_rep_netdev_notifier = {
 | |
| 	.notifier_call = lio_vf_rep_netdev_event,
 | |
| };
 | |
| 
 | |
| int
 | |
| lio_vf_rep_modinit(void)
 | |
| {
 | |
| 	if (register_netdevice_notifier(&lio_vf_rep_netdev_notifier)) {
 | |
| 		pr_err("netdev notifier registration failed\n");
 | |
| 		return -EFAULT;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| lio_vf_rep_modexit(void)
 | |
| {
 | |
| 	if (unregister_netdevice_notifier(&lio_vf_rep_netdev_notifier))
 | |
| 		pr_err("netdev notifier unregister failed\n");
 | |
| }
 |