From: Misha Tomushev <misha@fabric7.com> (by way of Misha Tomushev <misha@fabric7.com>)
To: NETDEV Linux <netdev@vger.kernel.org>
Subject: [PATCH 9/10] VIOC: New Network Device Driver
Date: Thu, 5 Oct 2006 11:55:56 -0700 [thread overview]
Message-ID: <200610051155.56262.misha@fabric7.com> (raw)
Adding VIOC device driver. Packet receive code.
Signed-off-by: Misha Tomushev <misha@fabric7.com>
diff -uprN linux-2.6.17/drivers/net/vioc/vioc_receive.c
linux-2.6.17.vioc/drivers/net/vioc/vioc_receive.c
--- linux-2.6.17/drivers/net/vioc/vioc_receive.c 1969-12-31
16:00:00.000000000 -0800
+++ linux-2.6.17.vioc/drivers/net/vioc/vioc_receive.c 2006-10-04
10:39:10.000000000 -0700
@@ -0,0 +1,365 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ * driver-support@fabric7.com
+ *
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#include "f7/vnic_hw_registers.h"
+#include "f7/vnic_defs.h"
+
+#include "vioc_vnic.h"
+#include "vioc_api.h"
+
+/*
+ * Receive one packet. The VIOC is read-locked. Since RxDQs are
+ * partitioned into independent RxSets and VNICs assigned to exactly
+ * one RxSet, no locking is needed on RxDQs or RxCQs.
+ * Return true if we got a packet, false if the queue is empty.
+ */
+int vioc_rx_pkt(struct vioc_device *viocdev, struct rxc *rxc, u32 sw_idx)
+{
+ u32 rx_status;
+ u32 vnic_id;
+ u32 rxdq_id;
+ u32 rxd_id;
+ u32 pkt_len;
+ u32 dmap_idx;
+ struct sk_buff *in_skb, *out_skb;
+ struct vnic_device *vnicdev;
+ struct rxdq *rxdq;
+ struct rxc_pktDesc_Phys_w *rxcd;
+ struct rx_pktBufDesc_Phys_w *rxd;
+
+ rxcd = &rxc->desc[sw_idx];
+ if (GET_VNIC_RXC_FLAGGED(rxcd) != VNIC_RXC_FLAGGED_HW_W)
+ return 0; /* ring empty */
+
+ vnic_id = GET_VNIC_RXC_VNIC_ID_SHIFTED(rxcd);
+ rxdq_id = GET_VNIC_RXC_RXQ_ID_SHIFTED(rxcd);
+ rxd_id = GET_VNIC_RXC_IDX_SHIFTED(rxcd);
+ rxdq = viocdev->rxd_p[rxdq_id];
+ rxd = &rxdq->desc[rxd_id];
+
+ in_skb = (struct sk_buff *)rxdq->vbuf[rxd_id].skb;
+ BUG_ON(in_skb == NULL);
+ out_skb = in_skb; /* default it here */
+
+ /*
+ * Reset HW FLAG in this RxC Descriptor, marking it as "SW
+ * acknowledged HW completion".
+ */
+ CLR_VNIC_RXC_FLAGGED(rxcd);
+
+ if (!(viocdev->vnics_map & (1 << vnic_id)))
+ /* VNIC is not enabled - silently drop packet */
+ goto out;
+
+ in_skb->dev = viocdev->vnic_netdev[vnic_id];
+ vnicdev = in_skb->dev->priv;
+ BUG_ON(vnicdev == NULL);
+
+ rx_status = GET_VNIC_RXC_STATUS(rxcd);
+
+ if (likely(rx_status == VNIC_RXC_STATUS_OK_W)) {
+
+ pkt_len = GET_NTOH_VIOC_F7PF_PKTLEN_SHIFTED(in_skb->data);
+
+ /* Copy out mice packets in ALL rings, even small */
+ if (pkt_len <= VIOC_COPYOUT_THRESHOLD) {
+ out_skb = dev_alloc_skb(pkt_len);
+ if (!out_skb)
+ goto drop;
+ out_skb->dev = in_skb->dev;
+ memcpy(out_skb->data, in_skb->data, pkt_len);
+ }
+
+ vnicdev->net_stats.rx_bytes += pkt_len;
+ vnicdev->net_stats.rx_packets++;
+ /* Set ->len and ->tail to reflect packet size */
+ skb_put(out_skb, pkt_len);
+
+ skb_pull(out_skb, F7PF_HLEN_STD);
+ out_skb->protocol = eth_type_trans(out_skb, out_skb->dev);
+
+ /* Checksum offload */
+ if (GET_VNIC_RXC_ENTAG_SHIFTED(rxcd) ==
+ VIOC_F7PF_ET_ETH_IPV4_CKS)
+ out_skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else {
+ out_skb->ip_summed = CHECKSUM_HW;
+ out_skb->csum =
+ ntohs(~GET_VNIC_RXC_CKSUM_SHIFTED(rxcd) & 0xffff);
+ }
+
+ netif_receive_skb(out_skb);
+ } else {
+ vnicdev->net_stats.rx_errors++;
+ if (rx_status & VNIC_RXC_ISBADLENGTH_W)
+ vnicdev->net_stats.rx_length_errors++;
+ if (rx_status & VNIC_RXC_ISBADCRC_W)
+ vnicdev->net_stats.rx_crc_errors++;
+ vnicdev->net_stats.rx_missed_errors++;
+ drop:
+ vnicdev->net_stats.rx_dropped++;
+ }
+
+ out:
+ CLR_VNIC_RX_OWNED(rxd);
+
+ /* Deallocate only if we did not copy skb above */
+ if (in_skb == out_skb) {
+ pci_unmap_single(viocdev->pdev,
+ rxdq->vbuf[rxd_id].dma,
+ rxdq->rx_buf_size, PCI_DMA_FROMDEVICE);
+ rxdq->vbuf[rxd_id].skb = NULL;
+ rxdq->vbuf[rxd_id].dma = (dma_addr_t) NULL;
+ }
+
+ dmap_idx = rxd_id / VIOC_RXD_BATCH_BITS;
+ rxdq->dmap[dmap_idx] &= ~(1 << (rxd_id % VIOC_RXD_BATCH_BITS));
+
+ if (!rxdq->skip_fence_run)
+ vioc_next_fence_run(rxdq);
+ else
+ rxdq->skip_fence_run--;
+
+ return 1; /* got a packet */
+}
+
+/*
+ * Refill one batch of VIOC_RXD_BATCH_BITS descriptors with skb's as
+ * needed. Transfer this batch to HW. Return -1 on success, the
+ * batch id otherwise (which means this batch must be retried.)
+ */
+static u32 _vioc_fill_n_xfer(struct rxdq *rxdq, unsigned batch_idx)
+{
+ unsigned first_idx, idx;
+ int i;
+ struct rx_pktBufDesc_Phys_w *rxd;
+ struct sk_buff *skb;
+ u64 x;
+ unsigned size;
+
+ first_idx = batch_idx * VIOC_RXD_BATCH_BITS;
+ size = rxdq->rx_buf_size;
+ i = VIOC_RXD_BATCH_BITS;
+
+ while (--i >= 0) {
+ idx = first_idx + i;
+ /* Check if that slot is unallocated */
+ if (rxdq->vbuf[idx].skb == NULL) {
+ skb = dev_alloc_skb(size + 64);
+ if (skb == NULL) {
+ goto undo_refill;
+ }
+ /* Cache align */
+ x = (u64) skb->data;
+ skb->data = (unsigned char *)ALIGN(x, 64);
+ rxdq->vbuf[idx].skb = skb;
+ rxdq->vbuf[idx].dma =
+ pci_map_single(rxdq->viocdev->pdev,
+ skb->data, size, PCI_DMA_FROMDEVICE);
+ }
+
+ rxd = RXD_PTR(rxdq, idx);
+
+ if (idx == first_idx)
+ /*
+ * Making sure that all writes to the Rx
+ * Descriptor Queue where completed before
+ * turning over up the last (i.e the first)
+ * descriptor to HW.
+ */
+ wmb();
+
+ /* Set the address of the Rx buffer and HW FLAG */
+ SET_VNIC_RX_BUFADDR_HW(rxd, rxdq->vbuf[idx].dma);
+ }
+
+ rxdq->dmap[batch_idx] = ALL_BATCH_HW_OWNED;
+ return VIOC_NONE_TO_HW; /* XXX success */
+
+ undo_refill:
+ /*
+ * Just turn descriptors over to SW. Leave skb's allocated,
+ * they will be used when we retry. Uses idx!
+ */
+ for (; idx < (first_idx + VIOC_RXD_BATCH_BITS); idx++) {
+ rxd = RXD_PTR(rxdq, idx);
+ CLR_VNIC_RX_OWNED(rxd);
+ }
+
+ rxdq->starvations++;
+ return batch_idx; /* failure - retry this batch */
+}
+
+#define IDX_PLUS_DELTA(idx, count, delta) \
+ (((idx) + (delta)) % (count))
+#define IDX_MINUS_DELTA(idx, count, delta) \
+ (((idx) == 0) ? ((count) - (delta)): ((idx) - (delta)))
+
+/*
+ * Returns 0 on success, or a negative errno on failure
+ */
+int vioc_next_fence_run(struct rxdq *rxdq)
+{
+ unsigned dmap_idx, dmap_start, dmap_end, dmap_count;
+ unsigned to_hw = rxdq->to_hw;
+
+ if (to_hw != VIOC_NONE_TO_HW)
+ to_hw = _vioc_fill_n_xfer(rxdq, to_hw);
+ if (to_hw != VIOC_NONE_TO_HW) {
+ /* Problem! Can't refill the Rx queue. */
+ rxdq->to_hw = to_hw;
+ return -ENOMEM;
+ }
+
+ dmap_count = rxdq->dmap_count;
+ dmap_start = rxdq->fence;
+ dmap_end = IDX_MINUS_DELTA(dmap_start, dmap_count, 1);
+ dmap_idx = dmap_start;
+
+ while (rxdq->dmap[dmap_idx] == ALL_BATCH_SW_OWNED) {
+ dmap_idx = IDX_PLUS_DELTA(dmap_idx, dmap_count, 1);
+ if (dmap_idx == dmap_end) {
+ rxdq->run_to_end++;
+ break;
+ }
+ }
+
+ dmap_idx = IDX_MINUS_DELTA(dmap_idx, dmap_count, 1);
+
+ /* If are back at the fence - nothing to do */
+ if (dmap_idx == dmap_start)
+ return 0;
+ /*
+ * Now dmap_idx points to the "last" batch, that is
+ * 100% owned by SW, this will become a new fence.
+ */
+ rxdq->fence = dmap_idx;
+ rxdq->skip_fence_run = dmap_count / 4;
+
+ /*
+ * Go back up tp dmap_start (old fence) and refill and
+ * turn over to HW Rx Descriptors
+ */
+ while (dmap_idx != dmap_start) {
+ dmap_idx = IDX_MINUS_DELTA(dmap_idx, dmap_count, 1);
+ to_hw = _vioc_fill_n_xfer(rxdq, dmap_idx);
+ if (to_hw != VIOC_NONE_TO_HW) {
+ rxdq->to_hw = to_hw;
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * NAPI poll method. RxC interrupt for this queue is disabled.
+ * The only lock we need is a read-lock on the VIOC.
+ */
+int vioc_rx_poll(struct net_device *dev, int *budget)
+{
+ struct napi_poll *napi = dev->priv;
+ struct rxc *rxc = napi->rxc;
+ struct vioc_device *viocdev = rxc->viocdev; /* safe */
+ u32 sw_idx, count;
+ int rxflag = 0;
+ int quota, work_done = 0;
+
+ if (!napi->enabled) {
+ /* If NOT enabled - do nothing.
+ * ACK by setting stopped.
+ */
+ napi->stopped = 1;
+ netif_rx_complete(dev);
+ return 0;
+ }
+
+ quota = min(*budget, dev->quota);
+
+ sw_idx = rxc->sw_idx;
+ count = rxc->count;
+ while (work_done <= quota) {
+ rxflag = vioc_rx_pkt(viocdev, rxc, sw_idx);
+ if (unlikely(!rxflag))
+ break; /* no work left */
+ sw_idx++;
+ if (unlikely(sw_idx == count))
+ sw_idx = 0;
+ work_done++;
+ }
+ rxc->sw_idx = sw_idx;
+ dev->quota -= work_done;
+ *budget -= work_done;
+ if (rxflag)
+ return 1; /* keep going */
+
+ netif_rx_complete(dev);
+ dev->weight = POLL_WEIGHT;
+
+ vioc_rxc_interrupt_enable(rxc);
+
+ return 0; /* poll done */
+}
+
+void vioc_rxc_interrupt(void *input_param)
+{
+ unsigned vioc_idx = VIOC_IRQ_PARAM_VIOC_ID(input_param);
+ struct vioc_device *viocdev = vioc_viocdev(vioc_idx);
+ unsigned rxc_id = VIOC_IRQ_PARAM_PARAM_ID(input_param);
+ struct rxc *rxc = viocdev->rxc_p[rxc_id];
+ struct napi_poll *napi = &rxc->napi;
+
+ napi->rx_interrupts++;
+ /* Schedule NAPI poll */
+ if (netif_rx_schedule_prep(&napi->poll_dev)) {
+ vioc_rxc_interrupt_disable(rxc);
+ __netif_rx_schedule(&napi->poll_dev);
+ } else {
+ vioc_rxc_interrupt_clear_pend(rxc);
+ }
+}
--
Misha Tomushev
misha@fabric7.com
next reply other threads:[~2006-10-05 19:05 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-10-05 18:55 Misha Tomushev [this message]
-- strict thread matches above, loose matches on Subject: below --
2006-10-05 18:12 [PATCH 9/10] VIOC: New Network Device Driver Misha Tomushev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200610051155.56262.misha@fabric7.com \
--to=misha@fabric7.com \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.