forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
672 lines
16 KiB
672 lines
16 KiB
/********************************************************************** |
|
* Author: Cavium, Inc. |
|
* |
|
* Contact: [email protected] |
|
* 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; |
|
} |
|
|
|
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; |
|
strncpy(rep_cfg.rep_name.name, ndev->name, LIO_IF_NAME_SIZE); |
|
|
|
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"); |
|
}
|
|
|