liquidio CN23XX: VF xmit

Adds support for transmit functionality in VF.

Signed-off-by: Raghu Vatsavayi <raghu.vatsavayi@caviumnetworks.com>
Signed-off-by: Derek Chickles <derek.chickles@caviumnetworks.com>
Signed-off-by: Satanand Burla <satananda.burla@caviumnetworks.com>
Signed-off-by: Felix Manlunas <felix.manlunas@caviumnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Raghu Vatsavayi 2016-12-07 08:54:35 -08:00 committed by David S. Miller
parent beea50a5d5
commit 9981328a87
3 changed files with 364 additions and 3 deletions

View File

@ -529,6 +529,26 @@ static u64 cn23xx_vf_msix_interrupt_handler(void *dev)
return ret; return ret;
} }
static u32 cn23xx_update_read_index(struct octeon_instr_queue *iq)
{
u32 pkt_in_done = readl(iq->inst_cnt_reg);
u32 last_done;
u32 new_idx;
last_done = pkt_in_done - iq->pkt_in_done;
iq->pkt_in_done = pkt_in_done;
/* Modulo of the new index with the IQ size will give us
* the new index. The iq->reset_instr_cnt is always zero for
* cn23xx, so no extra adjustments are needed.
*/
new_idx = (iq->octeon_read_index +
(u32)(last_done & CN23XX_PKT_IN_DONE_CNT_MASK)) %
iq->max_count;
return new_idx;
}
static void cn23xx_enable_vf_interrupt(struct octeon_device *oct, u8 intr_flag) static void cn23xx_enable_vf_interrupt(struct octeon_device *oct, u8 intr_flag)
{ {
struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip; struct octeon_cn23xx_vf *cn23xx = (struct octeon_cn23xx_vf *)oct->chip;
@ -660,6 +680,7 @@ int cn23xx_setup_octeon_vf_device(struct octeon_device *oct)
oct->fn_list.msix_interrupt_handler = cn23xx_vf_msix_interrupt_handler; oct->fn_list.msix_interrupt_handler = cn23xx_vf_msix_interrupt_handler;
oct->fn_list.setup_device_regs = cn23xx_setup_vf_device_regs; oct->fn_list.setup_device_regs = cn23xx_setup_vf_device_regs;
oct->fn_list.update_iq_read_idx = cn23xx_update_read_index;
oct->fn_list.enable_interrupt = cn23xx_enable_vf_interrupt; oct->fn_list.enable_interrupt = cn23xx_enable_vf_interrupt;
oct->fn_list.disable_interrupt = cn23xx_disable_vf_interrupt; oct->fn_list.disable_interrupt = cn23xx_disable_vf_interrupt;

View File

@ -55,6 +55,21 @@ struct liquidio_if_cfg_resp {
u64 status; u64 status;
}; };
union tx_info {
u64 u64;
struct {
#ifdef __BIG_ENDIAN_BITFIELD
u16 gso_size;
u16 gso_segs;
u32 reserved;
#else
u32 reserved;
u16 gso_segs;
u16 gso_size;
#endif
} s;
};
#define OCTNIC_MAX_SG (MAX_SKB_FRAGS) #define OCTNIC_MAX_SG (MAX_SKB_FRAGS)
#define OCTNIC_GSO_MAX_HEADER_SIZE 128 #define OCTNIC_GSO_MAX_HEADER_SIZE 128
@ -254,6 +269,19 @@ static void start_txq(struct net_device *netdev)
} }
} }
/**
* \brief Stop a queue
* @param netdev network device
* @param q which queue to stop
*/
static void stop_q(struct net_device *netdev, int q)
{
if (netif_is_multiqueue(netdev))
netif_stop_subqueue(netdev, q);
else
netif_stop_queue(netdev);
}
/** /**
* Remove the node at the head of the list. The list would be empty at * Remove the node at the head of the list. The list would be empty at
* the end of this call if there are no more nodes in the list. * the end of this call if there are no more nodes in the list.
@ -944,6 +972,45 @@ static u16 select_q(struct net_device *dev, struct sk_buff *skb,
return (u16)(qindex % (lio->linfo.num_txpciq)); return (u16)(qindex % (lio->linfo.num_txpciq));
} }
/**
* \brief Setup input and output queues
* @param octeon_dev octeon device
* @param ifidx Interface index
*
* Note: Queues are with respect to the octeon device. Thus
* an input queue is for egress packets, and output queues
* are for ingress packets.
*/
static int setup_io_queues(struct octeon_device *octeon_dev, int ifidx)
{
struct net_device *netdev;
int num_tx_descs;
struct lio *lio;
int retval = 0;
int q;
netdev = octeon_dev->props[ifidx].netdev;
lio = GET_LIO(netdev);
/* set up IQs. */
for (q = 0; q < lio->linfo.num_txpciq; q++) {
num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF(
octeon_get_conf(octeon_dev), lio->ifidx);
retval = octeon_setup_iq(octeon_dev, ifidx, q,
lio->linfo.txpciq[q], num_tx_descs,
netdev_get_tx_queue(netdev, q));
if (retval) {
dev_err(&octeon_dev->pci_dev->dev,
" %s : Runtime IQ(TxQ) creation failed.\n",
__func__);
return 1;
}
}
return 0;
}
/** /**
* \brief Net device open for LiquidIO * \brief Net device open for LiquidIO
* @param netdev network device * @param netdev network device
@ -1180,6 +1247,259 @@ static int liquidio_change_mtu(struct net_device *netdev, int new_mtu)
return 0; return 0;
} }
/** \brief Transmit networks packets to the Octeon interface
* @param skbuff skbuff struct to be passed to network layer.
* @param netdev pointer to network device
* @returns whether the packet was transmitted to the device okay or not
* (NETDEV_TX_OK or NETDEV_TX_BUSY)
*/
static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct octnet_buf_free_info *finfo;
union octnic_cmd_setup cmdsetup;
struct octnic_data_pkt ndata;
struct octeon_instr_irh *irh;
struct oct_iq_stats *stats;
struct octeon_device *oct;
int q_idx = 0, iq_no = 0;
union tx_info *tx_info;
struct lio *lio;
int status = 0;
u64 dptr = 0;
u32 tag = 0;
int j;
lio = GET_LIO(netdev);
oct = lio->oct_dev;
if (netif_is_multiqueue(netdev)) {
q_idx = skb->queue_mapping;
q_idx = (q_idx % (lio->linfo.num_txpciq));
tag = q_idx;
iq_no = lio->linfo.txpciq[q_idx].s.q_no;
} else {
iq_no = lio->txq;
}
stats = &oct->instr_queue[iq_no]->stats;
/* Check for all conditions in which the current packet cannot be
* transmitted.
*/
if (!(atomic_read(&lio->ifstate) & LIO_IFSTATE_RUNNING) ||
(!lio->linfo.link.s.link_up) || (skb->len <= 0)) {
netif_info(lio, tx_err, lio->netdev, "Transmit failed link_status : %d\n",
lio->linfo.link.s.link_up);
goto lio_xmit_failed;
}
/* Use space in skb->cb to store info used to unmap and
* free the buffers.
*/
finfo = (struct octnet_buf_free_info *)skb->cb;
finfo->lio = lio;
finfo->skb = skb;
finfo->sc = NULL;
/* Prepare the attributes for the data to be passed to OSI. */
memset(&ndata, 0, sizeof(struct octnic_data_pkt));
ndata.buf = finfo;
ndata.q_no = iq_no;
if (netif_is_multiqueue(netdev)) {
if (octnet_iq_is_full(oct, ndata.q_no)) {
/* defer sending if queue is full */
netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n",
ndata.q_no);
stats->tx_iq_busy++;
return NETDEV_TX_BUSY;
}
} else {
if (octnet_iq_is_full(oct, lio->txq)) {
/* defer sending if queue is full */
stats->tx_iq_busy++;
netif_info(lio, tx_err, lio->netdev, "Transmit failed iq:%d full\n",
ndata.q_no);
return NETDEV_TX_BUSY;
}
}
ndata.datasize = skb->len;
cmdsetup.u64 = 0;
cmdsetup.s.iq_no = iq_no;
if (skb->ip_summed == CHECKSUM_PARTIAL)
cmdsetup.s.transport_csum = 1;
if (!skb_shinfo(skb)->nr_frags) {
cmdsetup.s.u.datasize = skb->len;
octnet_prepare_pci_cmd(oct, &ndata.cmd, &cmdsetup, tag);
/* Offload checksum calculation for TCP/UDP packets */
dptr = dma_map_single(&oct->pci_dev->dev,
skb->data,
skb->len,
DMA_TO_DEVICE);
if (dma_mapping_error(&oct->pci_dev->dev, dptr)) {
dev_err(&oct->pci_dev->dev, "%s DMA mapping error 1\n",
__func__);
return NETDEV_TX_BUSY;
}
ndata.cmd.cmd3.dptr = dptr;
finfo->dptr = dptr;
ndata.reqtype = REQTYPE_NORESP_NET;
} else {
struct skb_frag_struct *frag;
struct octnic_gather *g;
int i, frags;
spin_lock(&lio->glist_lock[q_idx]);
g = (struct octnic_gather *)list_delete_head(
&lio->glist[q_idx]);
spin_unlock(&lio->glist_lock[q_idx]);
if (!g) {
netif_info(lio, tx_err, lio->netdev,
"Transmit scatter gather: glist null!\n");
goto lio_xmit_failed;
}
cmdsetup.s.gather = 1;
cmdsetup.s.u.gatherptrs = (skb_shinfo(skb)->nr_frags + 1);
octnet_prepare_pci_cmd(oct, &ndata.cmd, &cmdsetup, tag);
memset(g->sg, 0, g->sg_size);
g->sg[0].ptr[0] = dma_map_single(&oct->pci_dev->dev,
skb->data,
(skb->len - skb->data_len),
DMA_TO_DEVICE);
if (dma_mapping_error(&oct->pci_dev->dev, g->sg[0].ptr[0])) {
dev_err(&oct->pci_dev->dev, "%s DMA mapping error 2\n",
__func__);
return NETDEV_TX_BUSY;
}
add_sg_size(&g->sg[0], (skb->len - skb->data_len), 0);
frags = skb_shinfo(skb)->nr_frags;
i = 1;
while (frags--) {
frag = &skb_shinfo(skb)->frags[i - 1];
g->sg[(i >> 2)].ptr[(i & 3)] =
dma_map_page(&oct->pci_dev->dev,
frag->page.p,
frag->page_offset,
frag->size,
DMA_TO_DEVICE);
if (dma_mapping_error(&oct->pci_dev->dev,
g->sg[i >> 2].ptr[i & 3])) {
dma_unmap_single(&oct->pci_dev->dev,
g->sg[0].ptr[0],
skb->len - skb->data_len,
DMA_TO_DEVICE);
for (j = 1; j < i; j++) {
frag = &skb_shinfo(skb)->frags[j - 1];
dma_unmap_page(&oct->pci_dev->dev,
g->sg[j >> 2].ptr[j & 3],
frag->size,
DMA_TO_DEVICE);
}
dev_err(&oct->pci_dev->dev, "%s DMA mapping error 3\n",
__func__);
return NETDEV_TX_BUSY;
}
add_sg_size(&g->sg[(i >> 2)], frag->size, (i & 3));
i++;
}
dptr = dma_map_single(&oct->pci_dev->dev,
g->sg, g->sg_size,
DMA_TO_DEVICE);
if (dma_mapping_error(&oct->pci_dev->dev, dptr)) {
dev_err(&oct->pci_dev->dev, "%s DMA mapping error 4\n",
__func__);
dma_unmap_single(&oct->pci_dev->dev, g->sg[0].ptr[0],
skb->len - skb->data_len,
DMA_TO_DEVICE);
for (j = 1; j <= frags; j++) {
frag = &skb_shinfo(skb)->frags[j - 1];
dma_unmap_page(&oct->pci_dev->dev,
g->sg[j >> 2].ptr[j & 3],
frag->size, DMA_TO_DEVICE);
}
return NETDEV_TX_BUSY;
}
ndata.cmd.cmd3.dptr = dptr;
finfo->dptr = dptr;
finfo->g = g;
ndata.reqtype = REQTYPE_NORESP_NET_SG;
}
irh = (struct octeon_instr_irh *)&ndata.cmd.cmd3.irh;
tx_info = (union tx_info *)&ndata.cmd.cmd3.ossp[0];
if (skb_shinfo(skb)->gso_size) {
tx_info->s.gso_size = skb_shinfo(skb)->gso_size;
tx_info->s.gso_segs = skb_shinfo(skb)->gso_segs;
}
status = octnet_send_nic_data_pkt(oct, &ndata);
if (status == IQ_SEND_FAILED)
goto lio_xmit_failed;
netif_info(lio, tx_queued, lio->netdev, "Transmit queued successfully\n");
if (status == IQ_SEND_STOP) {
dev_err(&oct->pci_dev->dev, "Rcvd IQ_SEND_STOP signal; stopping IQ-%d\n",
iq_no);
stop_q(lio->netdev, q_idx);
}
netif_trans_update(netdev);
if (skb_shinfo(skb)->gso_size)
stats->tx_done += skb_shinfo(skb)->gso_segs;
else
stats->tx_done++;
stats->tx_tot_bytes += skb->len;
return NETDEV_TX_OK;
lio_xmit_failed:
stats->tx_dropped++;
netif_info(lio, tx_err, lio->netdev, "IQ%d Transmit dropped:%llu\n",
iq_no, stats->tx_dropped);
if (dptr)
dma_unmap_single(&oct->pci_dev->dev, dptr,
ndata.datasize, DMA_TO_DEVICE);
tx_buffer_free(skb);
return NETDEV_TX_OK;
}
/** \brief Network device Tx timeout
* @param netdev pointer to network device
*/
static void liquidio_tx_timeout(struct net_device *netdev)
{
struct lio *lio;
lio = GET_LIO(netdev);
netif_info(lio, tx_err, lio->netdev,
"Transmit timeout tx_dropped:%ld, waking up queues now!!\n",
netdev->stats.tx_dropped);
netif_trans_update(netdev);
txqs_wake(netdev);
}
/** Sending command to enable/disable RX checksum offload /** Sending command to enable/disable RX checksum offload
* @param netdev pointer to network device * @param netdev pointer to network device
* @param command OCTNET_CMD_TNL_RX_CSUM_CTL * @param command OCTNET_CMD_TNL_RX_CSUM_CTL
@ -1282,8 +1602,10 @@ static int liquidio_set_features(struct net_device *netdev,
static const struct net_device_ops lionetdevops = { static const struct net_device_ops lionetdevops = {
.ndo_open = liquidio_open, .ndo_open = liquidio_open,
.ndo_stop = liquidio_stop, .ndo_stop = liquidio_stop,
.ndo_start_xmit = liquidio_xmit,
.ndo_set_mac_address = liquidio_set_mac, .ndo_set_mac_address = liquidio_set_mac,
.ndo_set_rx_mode = liquidio_set_mcast_list, .ndo_set_rx_mode = liquidio_set_mcast_list,
.ndo_tx_timeout = liquidio_tx_timeout,
.ndo_change_mtu = liquidio_change_mtu, .ndo_change_mtu = liquidio_change_mtu,
.ndo_fix_features = liquidio_fix_features, .ndo_fix_features = liquidio_fix_features,
.ndo_set_features = liquidio_set_features, .ndo_set_features = liquidio_set_features,
@ -1507,6 +1829,24 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
/* Copy MAC Address to OS network device structure */ /* Copy MAC Address to OS network device structure */
ether_addr_copy(netdev->dev_addr, mac); ether_addr_copy(netdev->dev_addr, mac);
if (setup_io_queues(octeon_dev, i)) {
dev_err(&octeon_dev->pci_dev->dev, "I/O queues creation failed\n");
goto setup_nic_dev_fail;
}
/* For VFs, enable Octeon device interrupts here,
* as this is contingent upon IO queue setup
*/
octeon_dev->fn_list.enable_interrupt(octeon_dev,
OCTEON_ALL_INTR);
/* By default all interfaces on a single Octeon uses the same
* tx and rx queues
*/
lio->txq = lio->linfo.txpciq[0].s.q_no;
lio->tx_qsize = octeon_get_tx_qsize(octeon_dev, lio->txq);
if (setup_glists(lio, num_iqueues)) { if (setup_glists(lio, num_iqueues)) {
dev_err(&octeon_dev->pci_dev->dev, dev_err(&octeon_dev->pci_dev->dev,
"Gather list allocation failed\n"); "Gather list allocation failed\n");

View File

@ -394,7 +394,7 @@ lio_process_iq_request_list(struct octeon_device *oct,
case REQTYPE_SOFT_COMMAND: case REQTYPE_SOFT_COMMAND:
sc = buf; sc = buf;
if (OCTEON_CN23XX_PF(oct)) if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct))
irh = (struct octeon_instr_irh *) irh = (struct octeon_instr_irh *)
&sc->cmd.cmd3.irh; &sc->cmd.cmd3.irh;
else else
@ -607,7 +607,7 @@ octeon_prepare_soft_command(struct octeon_device *oct,
oct_cfg = octeon_get_conf(oct); oct_cfg = octeon_get_conf(oct);
if (OCTEON_CN23XX_PF(oct)) { if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3; ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3;
ih3->pkind = oct->instr_queue[sc->iq_no]->txpciq.s.pkind; ih3->pkind = oct->instr_queue[sc->iq_no]->txpciq.s.pkind;
@ -700,7 +700,7 @@ int octeon_send_soft_command(struct octeon_device *oct,
struct octeon_instr_irh *irh; struct octeon_instr_irh *irh;
u32 len; u32 len;
if (OCTEON_CN23XX_PF(oct)) { if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) {
ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3; ih3 = (struct octeon_instr_ih3 *)&sc->cmd.cmd3.ih3;
if (ih3->dlengsz) { if (ih3->dlengsz) {
WARN_ON(!sc->dmadptr); WARN_ON(!sc->dmadptr);