/************************************************************************* * myri10ge.c: Myricom Myri-10G Ethernet driver. * * Copyright (C) 2005 - 2011 Myricom, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Myricom, Inc. nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * If the eeprom on your board is not recent enough, you will need to get a * newer firmware image at: * http://www.myri.com/scs/download-Myri10GE.html * * Contact Information: * <help@myri.com> * Myricom, Inc., 325N Santa Anita Avenue, Arcadia, CA 91006
*************************************************************************/
struct myri10ge_rx_buf { struct mcp_kreq_ether_recv __iomem *lanai; /* lanai ptr for recv ring */ struct mcp_kreq_ether_recv *shadow; /* host shadow of recv ring */ struct myri10ge_rx_buffer_state *info; struct page *page;
dma_addr_t bus; int page_offset; int cnt; int fill_cnt; int alloc_fail; int mask; /* number of rx slots -1 */ int watchdog_needed;
};
struct myri10ge_tx_buf { struct mcp_kreq_ether_send __iomem *lanai; /* lanai ptr for sendq */
__be32 __iomem *send_go; /* "go" doorbell ptr */
__be32 __iomem *send_stop; /* "stop" doorbell ptr */ struct mcp_kreq_ether_send *req_list; /* host shadow of sendq */ char *req_bytes; struct myri10ge_tx_buffer_state *info; int mask; /* number of transmit slots -1 */ int req ____cacheline_aligned; /* transmit slots submitted */ int pkt_start; /* packets started */ int stop_queue; int linearized; int done ____cacheline_aligned; /* transmit slots completed */ int pkt_done; /* packets completed */ int wake_queue; int queue_active;
};
struct myri10ge_rx_done { struct mcp_slot *entry;
dma_addr_t bus; int cnt; int idx;
};
/* wait up to 15ms. Longest command is the DMA benchmark, * which is capped at 5ms, but runs from a timeout handler * that runs every 7.8ms. So a 15ms timeout leaves us with * a 2.2ms margin
*/ if (atomic) { /* if atomic is set, do not sleep, * and try to get the completion quickly
* (1ms will be enough for those commands) */ for (sleep_total = 0;
sleep_total < 1000 &&
response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
sleep_total += 10) {
udelay(10);
mb();
}
} else { /* use msleep for most command */ for (sleep_total = 0;
sleep_total < 15 &&
response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
sleep_total++)
msleep(1);
}
/* * The eeprom strings on the lanaiX have the format * SN=x\0 * MAC=x:x:x:x:x:x\0 * PT:ddd mmm xx xx:xx:xx xx\0 * PV:ddd mmm xx xx:xx:xx xx\0
*/ staticint myri10ge_read_mac_addr(struct myri10ge_priv *mgp)
{ char *ptr, *limit; int i;
/* send a rdma command to the PCIe engine, and wait for the * response in the confirmation address. The firmware should * write a -1 there to indicate it is alive and well
*/
dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);
if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > mgp->sram_size) {
dev_err(dev, "Running firmware has bad header offset (%d)\n",
(int)hdr_offset); return -EIO;
}
/* copy header of running firmware from SRAM to host memory to
* validate firmware */
hdr = kmalloc(bytes, GFP_KERNEL); if (hdr == NULL) return -ENOMEM;
memcpy_fromio(hdr, mgp->sram + hdr_offset, bytes);
status = myri10ge_validate_firmware(mgp, hdr);
kfree(hdr);
/* check to see if adopted firmware has bug where adopting * it will cause broadcasts to be filtered unless the NIC
* is kept in ALLMULTI mode */ if (mgp->fw_ver_major == 1 && mgp->fw_ver_minor == 4 &&
mgp->fw_ver_tiny >= 4 && mgp->fw_ver_tiny <= 11) {
mgp->adopted_rx_filter_bug = 1;
dev_warn(dev, "Adopting fw %d.%d.%d: " "working around rx filter bug\n",
mgp->fw_ver_major, mgp->fw_ver_minor,
mgp->fw_ver_tiny);
} return status;
}
staticint myri10ge_get_firmware_capabilities(struct myri10ge_priv *mgp)
{ struct myri10ge_cmd cmd; int status;
/* probe for IPv6 TSO support */
mgp->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO;
status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE,
&cmd, 0); if (status == 0) {
mgp->max_tso6 = cmd.data0;
mgp->features |= NETIF_F_TSO6;
}
status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd, 0); if (status != 0) {
dev_err(&mgp->pdev->dev, "failed MXGEFW_CMD_GET_RX_RING_SIZE\n"); return -ENXIO;
}
/* send a reload command to the bootstrap MCP, and wait for the * response in the confirmation address. The firmware should * write a -1 there to indicate it is alive and well
*/
dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);
/* FIX: All newest firmware should un-protect the bottom of * the sram before handoff. However, the very first interfaces * do not. Therefore the handoff copy must skip the first 8 bytes
*/
buf[3] = htonl(MYRI10GE_FW_OFFSET + 8); /* where the code starts */
buf[4] = htonl(size - 8); /* length of code */
buf[5] = htonl(8); /* where to copy to */
buf[6] = htonl(0); /* where to jump to */
submit = mgp->sram + MXGEFW_BOOT_HANDOFF;
myri10ge_pio_copy(submit, &buf, sizeof(buf));
mb();
msleep(1);
mb();
i = 0; while (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 9) {
msleep(1 << i);
i++;
} if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA) {
dev_err(&mgp->pdev->dev, "handoff failed\n"); return -ENXIO;
}
myri10ge_dummy_rdma(mgp, 1);
status = myri10ge_get_firmware_capabilities(mgp);
/* Run a small DMA test. * The magic multipliers to the length tell the firmware * to do DMA read, write, or read+write tests. The * results are returned in cmd.data0. The upper 16 * bits or the return is the number of transfers completed. * The lower 16 bits is the time in 0.5us ticks that the * transfers took to complete.
*/
len = mgp->tx_boundary;
cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus);
cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus);
cmd.data2 = len * 0x10000;
status = myri10ge_send_cmd(mgp, test_type, &cmd, 0); if (status != 0) {
test = "read"; goto abort;
}
mgp->read_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff);
cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus);
cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus);
cmd.data2 = len * 0x1;
status = myri10ge_send_cmd(mgp, test_type, &cmd, 0); if (status != 0) {
test = "write"; goto abort;
}
mgp->write_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff);
cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus);
cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus);
cmd.data2 = len * 0x10001;
status = myri10ge_send_cmd(mgp, test_type, &cmd, 0); if (status != 0) {
test = "read/write"; goto abort;
}
mgp->read_write_dma = ((cmd.data0 >> 16) * len * 2 * 2) /
(cmd.data0 & 0xffff);
/* try to send a reset command to the card to see if it
* is alive */
memset(&cmd, 0, sizeof(cmd));
status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd, 0); if (status != 0) {
dev_err(&mgp->pdev->dev, "failed reset\n"); return -ENXIO;
}
(void)myri10ge_dma_test(mgp, MXGEFW_DMA_TEST); /* * Use non-ndis mcp_slot (eg, 4 bytes total, * no toeplitz hash value returned. Older firmware will * not understand this command, but will use the correct * sized mcp_slot, so we ignore error returns
*/
cmd.data0 = MXGEFW_RSS_MCP_SLOT_TYPE_MIN;
(void)myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_MCP_SLOT_TYPE, &cmd, 0);
/* * Even though we already know how many slices are supported * via myri10ge_probe_slices() MXGEFW_CMD_GET_MAX_RSS_QUEUES * has magic side effects, and must be called after a reset. * It must be called prior to calling any RSS related cmds, * including assigning an interrupt queue for anything but * slice 0. It must also be called *after* * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by * the firmware to compute offsets.
*/
if (mgp->num_slices > 1) {
/* ask the maximum number of slices it supports */
status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
&cmd, 0); if (status != 0) {
dev_err(&mgp->pdev->dev, "failed to get number of slices\n");
}
/* * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior * to setting up the interrupt queue DMA
*/
cmd.data0 = mgp->num_slices;
cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE; if (mgp->dev->real_num_tx_queues > 1)
cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ENABLE_RSS_QUEUES,
&cmd, 0);
/* Firmware older than 1.4.32 only supports multiple * RX queues, so if we get an error, first retry using a
* single TX queue before giving up */ if (status != 0 && mgp->dev->real_num_tx_queues > 1) {
netif_set_real_num_tx_queues(mgp->dev, 1);
cmd.data0 = mgp->num_slices;
cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
status = myri10ge_send_cmd(mgp,
MXGEFW_CMD_ENABLE_RSS_QUEUES,
&cmd, 0);
}
if (status != 0) {
dev_err(&mgp->pdev->dev, "failed to set number of slices\n");
return status;
}
} for (i = 0; i < mgp->num_slices; i++) {
ss = &mgp->ss[i];
cmd.data0 = MYRI10GE_LOWPART_TO_U32(ss->rx_done.bus);
cmd.data1 = MYRI10GE_HIGHPART_TO_U32(ss->rx_done.bus);
cmd.data2 = i;
status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_DMA,
&cmd, 0);
}
status |=
myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd, 0); for (i = 0; i < mgp->num_slices; i++) {
ss = &mgp->ss[i];
ss->irq_claim =
(__iomem __be32 *) (mgp->sram + cmd.data0 + 8 * i);
}
status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
&cmd, 0);
mgp->irq_deassert = (__iomem __be32 *) (mgp->sram + cmd.data0);
status |= myri10ge_send_cmd
(mgp, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd, 0);
mgp->intr_coal_delay_ptr = (__iomem __be32 *) (mgp->sram + cmd.data0); if (status != 0) {
dev_err(&mgp->pdev->dev, "failed set interrupt parameters\n"); return status;
}
put_be32(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr);
#ifdef CONFIG_MYRI10GE_DCA
status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_DCA_OFFSET, &cmd, 0);
dca_tag_off = cmd.data0; for (i = 0; i < mgp->num_slices; i++) {
ss = &mgp->ss[i]; if (status == 0) {
ss->dca_tag = (__iomem __be32 *)
(mgp->sram + dca_tag_off + 4 * i);
} else {
ss->dca_tag = NULL;
}
} #endif/* CONFIG_MYRI10GE_DCA */
/* reset mcp/driver shared state back to 0 */
mgp->link_changes = 0; for (i = 0; i < mgp->num_slices; i++) {
ss = &mgp->ss[i];
staticvoid
myri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx, int bytes, int watchdog)
{ struct page *page;
dma_addr_t bus; int idx; #if MYRI10GE_ALLOC_SIZE > 4096 int end_offset; #endif
if (unlikely(rx->watchdog_needed && !watchdog)) return;
/* try to refill entire ring */ while (rx->fill_cnt != (rx->cnt + rx->mask + 1)) {
idx = rx->fill_cnt & rx->mask; if (rx->page_offset + bytes <= MYRI10GE_ALLOC_SIZE) { /* we can use part of previous page */
get_page(rx->page);
} else { /* we need a new page */
page =
alloc_pages(GFP_ATOMIC | __GFP_COMP,
MYRI10GE_ALLOC_ORDER); if (unlikely(page == NULL)) { if (rx->fill_cnt - rx->cnt < 16)
rx->watchdog_needed = 1; return;
}
bus = dma_map_page(&mgp->pdev->dev, page, 0,
MYRI10GE_ALLOC_SIZE,
DMA_FROM_DEVICE); if (unlikely(dma_mapping_error(&mgp->pdev->dev, bus))) {
__free_pages(page, MYRI10GE_ALLOC_ORDER); if (rx->fill_cnt - rx->cnt < 16)
rx->watchdog_needed = 1; return;
}
}
rx->info[idx].page = rx->page;
rx->info[idx].page_offset = rx->page_offset; /* note that this is the address of the start of the
* page */
dma_unmap_addr_set(&rx->info[idx], bus, rx->bus);
rx->shadow[idx].addr_low =
htonl(MYRI10GE_LOWPART_TO_U32(rx->bus) + rx->page_offset);
rx->shadow[idx].addr_high =
htonl(MYRI10GE_HIGHPART_TO_U32(rx->bus));
/* start next packet on a cacheline boundary */
rx->page_offset += SKB_DATA_ALIGN(bytes);
/* copy 8 descriptors to the firmware at a time */ if ((idx & 7) == 7) {
myri10ge_submit_8rx(&rx->lanai[idx - 7],
&rx->shadow[idx - 7]);
}
}
}
staticinlinevoid
myri10ge_unmap_rx_page(struct pci_dev *pdev, struct myri10ge_rx_buffer_state *info, int bytes)
{ /* unmap the recvd page if we're the only or last user of it */ if (bytes >= MYRI10GE_ALLOC_SIZE / 2 ||
(info->page_offset + 2 * bytes) > MYRI10GE_ALLOC_SIZE) {
dma_unmap_page(&pdev->dev, (dma_unmap_addr(info, bus)
& ~(MYRI10GE_ALLOC_SIZE - 1)),
MYRI10GE_ALLOC_SIZE, DMA_FROM_DEVICE);
}
}
/* * GRO does not support acceleration of tagged vlan frames, and * this NIC does not support vlan tag offload, so we must pop * the tag ourselves to be able to achieve GRO performance that * is comparable to LRO.
*/
/* Mark as free */
tx->info[idx].skb = NULL; if (tx->info[idx].last) {
tx->pkt_done++;
tx->info[idx].last = 0;
}
tx->done++;
len = dma_unmap_len(&tx->info[idx], len);
dma_unmap_len_set(&tx->info[idx], len, 0); if (skb) {
ss->stats.tx_bytes += skb->len;
ss->stats.tx_packets++;
dev_consume_skb_irq(skb); if (len)
dma_unmap_single(&pdev->dev,
dma_unmap_addr(&tx->info[idx],
bus), len,
DMA_TO_DEVICE);
} else { if (len)
dma_unmap_page(&pdev->dev,
dma_unmap_addr(&tx->info[idx],
bus), len,
DMA_TO_DEVICE);
}
}
dev_queue = netdev_get_tx_queue(ss->dev, ss - ss->mgp->ss); /* * Make a minimal effort to prevent the NIC from polling an * idle tx queue. If we can't get the lock we leave the queue * active. In this case, either a thread was about to start * using the queue anyway, or we lost a race and the NIC will * waste some of its resources polling an inactive queue for a * while.
*/
staticint myri10ge_poll(struct napi_struct *napi, int budget)
{ struct myri10ge_slice_state *ss =
container_of(napi, struct myri10ge_slice_state, napi); int work_done;
#ifdef CONFIG_MYRI10GE_DCA if (ss->mgp->dca_enabled)
myri10ge_update_dca(ss); #endif /* process as many rx events as NAPI will allow */
work_done = myri10ge_clean_rx_done(ss, budget);
/* an interrupt on a non-zero receive-only slice is implicitly
* valid since MSI-X irqs are not shared */ if ((mgp->dev->real_num_tx_queues == 1) && (ss != mgp->ss)) {
napi_schedule(&ss->napi); return IRQ_HANDLED;
}
/* make sure it is our IRQ, and that the DMA has finished */ if (unlikely(!stats->valid)) return IRQ_NONE;
/* low bit indicates receives are present, so schedule
* napi poll handler */ if (stats->valid & 1)
napi_schedule(&ss->napi);
if (!mgp->msi_enabled && !mgp->msix_enabled) {
put_be32(0, mgp->irq_deassert); if (!myri10ge_deassert_wait)
stats->valid = 0;
mb();
} else
stats->valid = 0;
/* Wait for IRQ line to go low, if using INTx */
i = 0; while (1) {
i++; /* check for transmit completes and receives */
send_done_count = ntohl(stats->send_done_count); if (send_done_count != tx->pkt_done)
myri10ge_tx_done(ss, (int)send_done_count); if (unlikely(i > myri10ge_max_irq_loops)) {
netdev_warn(mgp->dev, "irq stuck?\n");
stats->valid = 0;
schedule_work(&mgp->watchdog_work);
} if (likely(stats->valid == 0)) break;
cpu_relax();
barrier();
}
/* Only slice 0 updates stats */ if (ss == mgp->ss)
myri10ge_check_statblock(mgp);
switch (stringset) { case ETH_SS_STATS:
memcpy(data, *myri10ge_gstrings_main_stats, sizeof(myri10ge_gstrings_main_stats));
data += sizeof(myri10ge_gstrings_main_stats); for (i = 0; i < mgp->num_slices; i++) {
memcpy(data, *myri10ge_gstrings_slice_stats, sizeof(myri10ge_gstrings_slice_stats));
data += sizeof(myri10ge_gstrings_slice_stats);
} break;
}
}
/* * Use a low-level command to change the LED behavior. Rather than * blinking (which is the normal case), when identify is used, the * yellow LED turns solid.
*/ staticint myri10ge_led(struct myri10ge_priv *mgp, int on)
{ struct mcp_gen_header *hdr; struct device *dev = &mgp->pdev->dev;
size_t hdr_off, pattern_off, hdr_len;
u32 pattern = 0xfffffffe;
/* find running firmware header */
hdr_off = swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET)); if ((hdr_off & 3) || hdr_off + sizeof(*hdr) > mgp->sram_size) {
dev_err(dev, "Running firmware has bad header offset (%d)\n",
(int)hdr_off); return -EIO;
}
hdr_len = swab32(readl(mgp->sram + hdr_off +
offsetof(struct mcp_gen_header, header_length)));
pattern_off = hdr_off + offsetof(struct mcp_gen_header, led_pattern); if (pattern_off >= (hdr_len + hdr_off)) {
dev_info(dev, "Firmware does not support LED identification\n"); return -EINVAL;
} if (!on)
pattern = swab32(readl(mgp->sram + pattern_off + 4));
writel(swab32(pattern), mgp->sram + pattern_off); return 0;
}
if (ss->rx_small.fill_cnt < ss->rx_small.mask + 1) {
netdev_err(dev, "slice-%d: alloced only %d small bufs\n",
slice, ss->rx_small.fill_cnt); goto abort_with_rx_small_ring;
}
myri10ge_alloc_rx_pages(mgp, &ss->rx_big, mgp->big_bytes, 0); if (ss->rx_big.fill_cnt < ss->rx_big.mask + 1) {
netdev_err(dev, "slice-%d: alloced only %d big bufs\n",
slice, ss->rx_big.fill_cnt); goto abort_with_rx_big_ring;
}
return 0;
abort_with_rx_big_ring: for (i = ss->rx_big.cnt; i < ss->rx_big.fill_cnt; i++) { int idx = i & ss->rx_big.mask;
myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_big.info[idx],
mgp->big_bytes);
put_page(ss->rx_big.info[idx].page);
}
abort_with_rx_small_ring: if (mgp->small_bytes == 0)
ss->rx_small.fill_cnt = ss->rx_small.cnt; for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) { int idx = i & ss->rx_small.mask;
myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx],
mgp->small_bytes + MXGEFW_PAD);
put_page(ss->rx_small.info[idx].page);
}
staticvoid myri10ge_free_rings(struct myri10ge_slice_state *ss)
{ struct myri10ge_priv *mgp = ss->mgp; struct sk_buff *skb; struct myri10ge_tx_buf *tx; int i, len, idx;
/* If not allocated, skip it */ if (ss->tx.req_list == NULL) return;
for (i = ss->rx_big.cnt; i < ss->rx_big.fill_cnt; i++) {
idx = i & ss->rx_big.mask; if (i == ss->rx_big.fill_cnt - 1)
ss->rx_big.info[idx].page_offset = MYRI10GE_ALLOC_SIZE;
myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_big.info[idx],
mgp->big_bytes);
put_page(ss->rx_big.info[idx].page);
}
if (mgp->small_bytes == 0)
ss->rx_small.fill_cnt = ss->rx_small.cnt; for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) {
idx = i & ss->rx_small.mask; if (i == ss->rx_small.fill_cnt - 1)
ss->rx_small.info[idx].page_offset =
MYRI10GE_ALLOC_SIZE;
myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx],
mgp->small_bytes + MXGEFW_PAD);
put_page(ss->rx_small.info[idx].page);
}
tx = &ss->tx; while (tx->done != tx->req) {
idx = tx->done & tx->mask;
skb = tx->info[idx].skb;
/* Mark as free */
tx->info[idx].skb = NULL;
tx->done++;
len = dma_unmap_len(&tx->info[idx], len);
dma_unmap_len_set(&tx->info[idx], len, 0); if (skb) {
ss->stats.tx_dropped++;
dev_kfree_skb_any(skb); if (len)
dma_unmap_single(&mgp->pdev->dev,
dma_unmap_addr(&tx->info[idx],
bus), len,
DMA_TO_DEVICE);
} else { if (len)
dma_unmap_page(&mgp->pdev->dev,
dma_unmap_addr(&tx->info[idx],
bus), len,
DMA_TO_DEVICE);
}
}
kfree(ss->rx_big.info);
if (mgp->msix_enabled) { for (i = 0; i < mgp->num_slices; i++)
free_irq(mgp->msix_vectors[i].vector, &mgp->ss[i]);
} else {
free_irq(pdev->irq, &mgp->ss[0]);
} if (mgp->msi_enabled)
pci_disable_msi(pdev); if (mgp->msix_enabled)
pci_disable_msix(pdev);
}
staticint myri10ge_get_txrx(struct myri10ge_priv *mgp, int slice)
{ struct myri10ge_cmd cmd; struct myri10ge_slice_state *ss; int status;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.