/* * Copyright (C) 2005 - 2008 ServerEngines * 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 version 2 * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * * Contact Information: * linux-drivers@serverengines.com * * ServerEngines * 209 N. Fair Oaks Ave * Sunnyvale, CA 94085 */ #include #include #include "benet.h" /* number of bytes of RX frame that are copied to skb->data */ #define BE_HDR_LEN 64 #define NETIF_RX(skb) netif_receive_skb(skb) #define VLAN_ACCEL_RX(skb, pnob, vt) \ vlan_hwaccel_rx(skb, pnob->vlan_grp, vt) /* This function notifies BladeEngine of the number of completion entries processed from the specified completion queue by writing the number of popped entries to the door bell. pnob - Pointer to the NetObject structure n - Number of completion entries processed cq_id - Queue ID of the completion queue for which notification is being done. re_arm - 1 - rearm the completion ring to generate an event. - 0 - dont rearm the completion ring to generate an event */ void be_notify_cmpl(struct be_net_object *pnob, int n, int cq_id, int re_arm) { struct CQ_DB_AMAP cqdb; cqdb.dw[0] = 0; AMAP_SET_BITS_PTR(CQ_DB, qid, &cqdb, cq_id); AMAP_SET_BITS_PTR(CQ_DB, rearm, &cqdb, re_arm); AMAP_SET_BITS_PTR(CQ_DB, num_popped, &cqdb, n); PD_WRITE(&pnob->fn_obj, cq_db, cqdb.dw[0]); } /* * adds additional receive frags indicated by BE starting from given * frag index (fi) to specified skb's frag list */ static void add_skb_frags(struct be_net_object *pnob, struct sk_buff *skb, u32 nresid, u32 fi) { struct be_adapter *adapter = pnob->adapter; u32 sk_frag_idx, n; struct be_rx_page_info *rx_page_info; u32 frag_sz = pnob->rx_buf_size; sk_frag_idx = skb_shinfo(skb)->nr_frags; while (nresid) { index_inc(&fi, pnob->rx_q_len); rx_page_info = (struct be_rx_page_info *)pnob->rx_ctxt[fi]; pnob->rx_ctxt[fi] = NULL; if ((rx_page_info->page_offset) || (pnob->rx_pg_shared == false)) { pci_unmap_page(adapter->pdev, pci_unmap_addr(rx_page_info, bus), frag_sz, PCI_DMA_FROMDEVICE); } n = min(nresid, frag_sz); skb_shinfo(skb)->frags[sk_frag_idx].page = rx_page_info->page; skb_shinfo(skb)->frags[sk_frag_idx].page_offset = rx_page_info->page_offset; skb_shinfo(skb)->frags[sk_frag_idx].size = n; sk_frag_idx++; skb->len += n; skb->data_len += n; skb_shinfo(skb)->nr_frags++; nresid -= n; memset(rx_page_info, 0, sizeof(struct be_rx_page_info)); atomic_dec(&pnob->rx_q_posted); } } /* * This function processes incoming nic packets over various Rx queues. * This function takes the adapter, the current Rx status descriptor * entry and the Rx completion queue ID as argument. */ static inline int process_nic_rx_completion(struct be_net_object *pnob, struct ETH_RX_COMPL_AMAP *rxcp) { struct be_adapter *adapter = pnob->adapter; struct sk_buff *skb; int udpcksm, tcpcksm; int n; u32 nresid, fi; u32 frag_sz = pnob->rx_buf_size; u8 *va; struct be_rx_page_info *rx_page_info; u32 numfrags, vtp, vtm, vlan_tag, pktsize; fi = AMAP_GET_BITS_PTR(ETH_RX_COMPL, fragndx, rxcp); BUG_ON(fi >= (int)pnob->rx_q_len); BUG_ON(fi < 0); rx_page_info = (struct be_rx_page_info *)pnob->rx_ctxt[fi]; BUG_ON(!rx_page_info->page); pnob->rx_ctxt[fi] = NULL; /* * If one page is used per fragment or if this is the second half of * of the page, unmap the page here */ if ((rx_page_info->page_offset) || (pnob->rx_pg_shared == false)) { pci_unmap_page(adapter->pdev, pci_unmap_addr(rx_page_info, bus), frag_sz, PCI_DMA_FROMDEVICE); } atomic_dec(&pnob->rx_q_posted); udpcksm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, udpcksm, rxcp); tcpcksm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, tcpcksm, rxcp); pktsize = AMAP_GET_BITS_PTR(ETH_RX_COMPL, pktsize, rxcp); /* * get rid of RX flush completions first. */ if ((tcpcksm) && (udpcksm) && (pktsize == 32)) { put_page(rx_page_info->page); memset(rx_page_info, 0, sizeof(struct be_rx_page_info)); return 0; } skb = netdev_alloc_skb(pnob->netdev, BE_HDR_LEN + NET_IP_ALIGN); if (skb == NULL) { dev_info(&pnob->netdev->dev, "alloc_skb() failed\n"); put_page(rx_page_info->page); memset(rx_page_info, 0, sizeof(struct be_rx_page_info)); goto free_frags; } skb_reserve(skb, NET_IP_ALIGN); skb->dev = pnob->netdev; n = min(pktsize, frag_sz); va = page_address(rx_page_info->page) + rx_page_info->page_offset; prefetch(va); skb->len = n; skb->data_len = n; if (n <= BE_HDR_LEN) { memcpy(skb->data, va, n); put_page(rx_page_info->page); skb->data_len -= n; skb->tail += n; } else { /* Setup the SKB with page buffer information */ skb_shinfo(skb)->frags[0].page = rx_page_info->page; skb_shinfo(skb)->nr_frags++; /* Copy the header into the skb_data */ memcpy(skb->data, va, BE_HDR_LEN); skb_shinfo(skb)->frags[0].page_offset = rx_page_info->page_offset + BE_HDR_LEN; skb_shinfo(skb)->frags[0].size = n - BE_HDR_LEN; skb->data_len -= BE_HDR_LEN; skb->tail += BE_HDR_LEN; } memset(rx_page_info, 0, sizeof(struct be_rx_page_info)); nresid = pktsize - n; skb->protocol = eth_type_trans(skb, pnob->netdev); if ((tcpcksm || udpcksm) && adapter->rx_csum) skb->ip_summed = CHECKSUM_UNNECESSARY; else skb->ip_summed = CHECKSUM_NONE; /* * if we have more bytes left, the frame has been * given to us in multiple fragments. This happens * with Jumbo frames. Add the remaining fragments to * skb->frags[] array. */ if (nresid) add_skb_frags(pnob, skb, nresid, fi); /* update the the true size of the skb. */ skb->truesize = skb->len + sizeof(struct sk_buff); /* * If a 802.3 frame or 802.2 LLC frame * (i.e) contains length field in MAC Hdr * and frame len is greater than 64 bytes */ if (((skb->protocol == ntohs(ETH_P_802_2)) || (skb->protocol == ntohs(ETH_P_802_3))) && (pktsize > BE_HDR_LEN)) { /* * If the length given in Mac Hdr is less than frame size * Erraneous frame, Drop it */ if ((ntohs(*(u16 *) (va + 12)) + ETH_HLEN) < pktsize) { /* Increment Non Ether type II frames dropped */ adapter->be_stat.bes_802_3_dropped_frames++; kfree_skb(skb); return 0; } /* * else if the length given in Mac Hdr is greater than * frame size, should not be seeing this sort of frames * dump the pkt and pass to stack */ else if ((ntohs(*(u16 *) (va + 12)) + ETH_HLEN) > pktsize) { /* Increment Non Ether type II frames malformed */ adapter->be_stat.bes_802_3_malformed_frames++; } } vtp = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vtp, rxcp); vtm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vtm, rxcp); if (vtp && vtm) { /* Vlan tag present in pkt and BE found * that the tag matched an entry in VLAN table */ if (!pnob->vlan_grp || pnob->num_vlans == 0) { /* But we have no VLANs configured. * This should never happen. Drop the packet. */ dev_info(&pnob->netdev->dev, "BladeEngine: Unexpected vlan tagged packet\n"); kfree_skb(skb); return 0; } /* pass the VLAN packet to stack */ vlan_tag = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vlan_tag, rxcp); VLAN_ACCEL_RX(skb, pnob, be16_to_cpu(vlan_tag)); } else { NETIF_RX(skb); } return 0; free_frags: /* free all frags associated with the current rxcp */ numfrags = AMAP_GET_BITS_PTR(ETH_RX_COMPL, numfrags, rxcp); while (numfrags-- > 1) { index_inc(&fi, pnob->rx_q_len); rx_page_info = (struct be_rx_page_info *) pnob->rx_ctxt[fi]; pnob->rx_ctxt[fi] = (void *)NULL; if (rx_page_info->page_offset || !pnob->rx_pg_shared) { pci_unmap_page(adapter->pdev, pci_unmap_addr(rx_page_info, bus), frag_sz, PCI_DMA_FROMDEVICE); } put_page(rx_page_info->page); memset(rx_page_info, 0, sizeof(struct be_rx_page_info)); atomic_dec(&pnob->rx_q_posted); } return -ENOMEM; } static void process_nic_rx_completion_lro(struct be_net_object *pnob, struct ETH_RX_COMPL_AMAP *rxcp) { struct be_adapter *adapter = pnob->adapter; struct skb_frag_struct rx_frags[BE_MAX_FRAGS_PER_FRAME]; unsigned int udpcksm, tcpcksm; u32 numfrags, vlanf, vtm, vlan_tag, nresid; u16 vlant; unsigned int fi, idx, n; struct be_rx_page_info *rx_page_info; u32 frag_sz = pnob->rx_buf_size, pktsize; bool rx_coal = (adapter->max_rx_coal <= 1) ? 0 : 1; u8 err, *va; __wsum csum = 0; if (AMAP_GET_BITS_PTR(ETH_RX_COMPL, ipsec, rxcp)) { /* Drop the pkt and move to the next completion. */ adapter->be_stat.bes_rx_misc_pkts++; return; } err = AMAP_GET_BITS_PTR(ETH_RX_COMPL, err, rxcp); if (err || !rx_coal) { /* We won't coalesce Rx pkts if the err bit set. * take the path of normal completion processing */ process_nic_rx_completion(pnob, rxcp); return; } fi = AMAP_GET_BITS_PTR(ETH_RX_COMPL, fragndx, rxcp); BUG_ON(fi >= (int)pnob->rx_q_len); BUG_ON(fi < 0); rx_page_info = (struct be_rx_page_info *)pnob->rx_ctxt[fi]; BUG_ON(!rx_page_info->page); pnob->rx_ctxt[fi] = (void *)NULL; /* If one page is used per fragment or if this is the * second half of the page, unmap the page here */ if (rx_page_info->page_offset || !pnob->rx_pg_shared) { pci_unmap_page(adapter->pdev, pci_unmap_addr(rx_page_info, bus), frag_sz, PCI_DMA_FROMDEVICE); } numfrags = AMAP_GET_BITS_PTR(ETH_RX_COMPL, numfrags, rxcp); udpcksm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, udpcksm, rxcp); tcpcksm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, tcpcksm, rxcp); vlan_tag = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vlan_tag, rxcp); vlant = be16_to_cpu(vlan_tag); vlanf = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vtp, rxcp); vtm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vtm, rxcp); pktsize = AMAP_GET_BITS_PTR(ETH_RX_COMPL, pktsize, rxcp); atomic_dec(&pnob->rx_q_posted); if (tcpcksm && udpcksm && pktsize == 32) { /* flush completion entries */ put_page(rx_page_info->page); memset(rx_page_info, 0, sizeof(struct be_rx_page_info)); return; } /* Only one of udpcksum and tcpcksum can be set */ BUG_ON(udpcksm && tcpcksm); /* jumbo frames could come in multiple fragments */ BUG_ON(numfrags != ((pktsize + (frag_sz - 1)) / frag_sz)); n = min(pktsize, frag_sz); nresid = pktsize - n; /* will be useful for jumbo pkts */ idx = 0; va = page_address(rx_page_info->page) + rx_page_info->page_offset; prefetch(va); rx_frags[idx].page = rx_page_info->page; rx_frags[idx].page_offset = (rx_page_info->page_offset); rx_frags[idx].size = n; memset(rx_page_info, 0, sizeof(struct be_rx_page_info)); /* If we got multiple fragments, we have more data. */ while (nresid) { idx++; index_inc(&fi, pnob->rx_q_len); rx_page_info = (struct be_rx_page_info *)pnob->rx_ctxt[fi]; pnob->rx_ctxt[fi] = (void *)NULL; if (rx_page_info->page_offset || !pnob->rx_pg_shared) { pci_unmap_page(adapter->pdev, pci_unmap_addr(rx_page_info, bus), frag_sz, PCI_DMA_FROMDEVICE); } n = min(nresid, frag_sz); rx_frags[idx].page = rx_page_info->page; rx_frags[idx].page_offset = (rx_page_info->page_offset); rx_frags[idx].size = n; nresid -= n; memset(rx_page_info, 0, sizeof(struct be_rx_page_info)); atomic_dec(&pnob->rx_q_posted); } if (likely(!(vlanf && vtm))) { lro_receive_frags(&pnob->lro_mgr, rx_frags, pktsize, pktsize, (void *)(unsigned long)csum, csum); } else { /* Vlan tag present in pkt and BE found * that the tag matched an entry in VLAN table */ if (unlikely(!pnob->vlan_grp || pnob->num_vlans == 0)) { /* But we have no VLANs configured. * This should never happen. Drop the packet. */ dev_info(&pnob->netdev->dev, "BladeEngine: Unexpected vlan tagged packet\n"); return; } /* pass the VLAN packet to stack */ lro_vlan_hwaccel_receive_frags(&pnob->lro_mgr, rx_frags, pktsize, pktsize, pnob->vlan_grp, vlant, (void *)(unsigned long)csum, csum); } adapter->be_stat.bes_rx_coal++; } struct ETH_RX_COMPL_AMAP *be_get_rx_cmpl(struct be_net_object *pnob) { struct ETH_RX_COMPL_AMAP *rxcp = &pnob->rx_cq[pnob->rx_cq_tl]; u32 valid, ct; valid = AMAP_GET_BITS_PTR(ETH_RX_COMPL, valid, rxcp); if (valid == 0) return NULL; ct = AMAP_GET_BITS_PTR(ETH_RX_COMPL, ct, rxcp); if (ct != 0) { /* Invalid chute #. treat as error */ AMAP_SET_BITS_PTR(ETH_RX_COMPL, err, rxcp, 1); } be_adv_rxcq_tl(pnob); AMAP_SET_BITS_PTR(ETH_RX_COMPL, valid, rxcp, 0); return rxcp; } static void update_rx_rate(struct be_adapter *adapter) { /* update the rate once in two seconds */ if ((jiffies - adapter->eth_rx_jiffies) > 2 * (HZ)) { u32 r; r = adapter->eth_rx_bytes / ((jiffies - adapter->eth_rx_jiffies) / (HZ)); r = (r / 1000000); /* MB/Sec */ /* Mega Bits/Sec */ adapter->be_stat.bes_eth_rx_rate = (r * 8); adapter->eth_rx_jiffies = jiffies; adapter->eth_rx_bytes = 0; } } static int process_rx_completions(struct be_net_object *pnob, int max_work) { struct be_adapter *adapter = pnob->adapter; struct ETH_RX_COMPL_AMAP *rxcp; u32 nc = 0; unsigned int pktsize; while (max_work && (rxcp = be_get_rx_cmpl(pnob))) { prefetch(rxcp); pktsize = AMAP_GET_BITS_PTR(ETH_RX_COMPL, pktsize, rxcp); process_nic_rx_completion_lro(pnob, rxcp); adapter->eth_rx_bytes += pktsize; update_rx_rate(adapter); nc++; max_work--; adapter->be_stat.bes_rx_compl++; } if (likely(adapter->max_rx_coal > 1)) { adapter->be_stat.bes_rx_flush++; lro_flush_all(&pnob->lro_mgr); } /* Refill the queue */ if (atomic_read(&pnob->rx_q_posted) < 900) be_post_eth_rx_buffs(pnob); return nc; } static struct ETH_TX_COMPL_AMAP *be_get_tx_cmpl(struct be_net_object *pnob) { struct ETH_TX_COMPL_AMAP *txcp = &pnob->tx_cq[pnob->tx_cq_tl]; u32 valid; valid = AMAP_GET_BITS_PTR(ETH_TX_COMPL, valid, txcp); if (valid == 0) return NULL; AMAP_SET_BITS_PTR(ETH_TX_COMPL, valid, txcp, 0); be_adv_txcq_tl(pnob); return txcp; } void process_one_tx_compl(struct be_net_object *pnob, u32 end_idx) { struct be_adapter *adapter = pnob->adapter; int cur_index, tx_wrbs_completed = 0; struct sk_buff *skb; u64 busaddr, pa, pa_lo, pa_hi; struct ETH_WRB_AMAP *wrb; u32 frag_len, last_index, j; last_index = tx_compl_lastwrb_idx_get(pnob); BUG_ON(last_index != end_idx); pnob->tx_ctxt[pnob->tx_q_tl] = NULL; do { cur_index = pnob->tx_q_tl; wrb = &pnob->tx_q[cur_index]; pa_hi = AMAP_GET_BITS_PTR(ETH_WRB, frag_pa_hi, wrb); pa_lo = AMAP_GET_BITS_PTR(ETH_WRB, frag_pa_lo, wrb); frag_len = AMAP_GET_BITS_PTR(ETH_WRB, frag_len, wrb); busaddr = (pa_hi << 32) | pa_lo; if (busaddr != 0) { pa = le64_to_cpu(busaddr); pci_unmap_single(adapter->pdev, pa, frag_len, PCI_DMA_TODEVICE); } if (cur_index == last_index) { skb = (struct sk_buff *)pnob->tx_ctxt[cur_index]; BUG_ON(!skb); for (j = 0; j < skb_shinfo(skb)->nr_frags; j++) { struct skb_frag_struct *frag; frag = &skb_shinfo(skb)->frags[j]; pci_unmap_page(adapter->pdev, (ulong) frag->page, frag->size, PCI_DMA_TODEVICE); } kfree_skb(skb); pnob->tx_ctxt[cur_index] = NULL; } else { BUG_ON(pnob->tx_ctxt[cur_index]); } tx_wrbs_completed++; be_adv_txq_tl(pnob); } while (cur_index != last_index); atomic_sub(tx_wrbs_completed, &pnob->tx_q_used); } /* there is no need to take an SMP lock here since currently * we have only one instance of the tasklet that does completion * processing. */ static void process_nic_tx_completions(struct be_net_object *pnob) { struct be_adapter *adapter = pnob->adapter; struct ETH_TX_COMPL_AMAP *txcp; struct net_device *netdev = pnob->netdev; u32 end_idx, num_processed = 0; adapter->be_stat.bes_tx_events++; while ((txcp = be_get_tx_cmpl(pnob))) { end_idx = AMAP_GET_BITS_PTR(ETH_TX_COMPL, wrb_index, txcp); process_one_tx_compl(pnob, end_idx); num_processed++; adapter->be_stat.bes_tx_compl++; } be_notify_cmpl(pnob, num_processed, pnob->tx_cq_id, 1); /* * We got Tx completions and have usable WRBs. * If the netdev's queue has been stopped * because we had run out of WRBs, wake it now. */ spin_lock(&adapter->txq_lock); if (netif_queue_stopped(netdev) && atomic_read(&pnob->tx_q_used) < pnob->tx_q_len / 2) { netif_wake_queue(netdev); } spin_unlock(&adapter->txq_lock); } static u32 post_rx_buffs(struct be_net_object *pnob, struct list_head *rxbl) { u32 nposted = 0; struct ETH_RX_D_AMAP *rxd = NULL; struct be_recv_buffer *rxbp; void **rx_ctxp; struct RQ_DB_AMAP rqdb; rx_ctxp = pnob->rx_ctxt; while (!list_empty(rxbl) && (rx_ctxp[pnob->rx_q_hd] == NULL) && nposted < 255) { rxbp = list_first_entry(rxbl, struct be_recv_buffer, rxb_list); list_del(&rxbp->rxb_list); rxd = pnob->rx_q + pnob->rx_q_hd; AMAP_SET_BITS_PTR(ETH_RX_D, fragpa_lo, rxd, rxbp->rxb_pa_lo); AMAP_SET_BITS_PTR(ETH_RX_D, fragpa_hi, rxd, rxbp->rxb_pa_hi); rx_ctxp[pnob->rx_q_hd] = rxbp->rxb_ctxt; be_adv_rxq_hd(pnob); nposted++; } if (nposted) { /* Now press the door bell to notify BladeEngine. */ rqdb.dw[0] = 0; AMAP_SET_BITS_PTR(RQ_DB, numPosted, &rqdb, nposted); AMAP_SET_BITS_PTR(RQ_DB, rq, &rqdb, pnob->rx_q_id); PD_WRITE(&pnob->fn_obj, erx_rq_db, rqdb.dw[0]); } atomic_add(nposted, &pnob->rx_q_posted); return nposted; } void be_post_eth_rx_buffs(struct be_net_object *pnob) { struct be_adapter *adapter = pnob->adapter; u32 num_bufs, r; u64 busaddr = 0, tmp_pa; u32 max_bufs, pg_hd; u32 frag_size; struct be_recv_buffer *rxbp; struct list_head rxbl; struct be_rx_page_info *rx_page_info; struct page *page = NULL; u32 page_order = 0; gfp_t alloc_flags = GFP_ATOMIC; BUG_ON(!adapter); max_bufs = 64; /* should be even # <= 255. */ frag_size = pnob->rx_buf_size; page_order = get_order(frag_size); if (frag_size == 8192) alloc_flags |= (gfp_t) __GFP_COMP; /* * Form a linked list of RECV_BUFFFER structure to be be posted. * We will post even number of buffer so that pages can be * shared. */ INIT_LIST_HEAD(&rxbl); for (num_bufs = 0; num_bufs < max_bufs && !pnob->rx_page_info[pnob->rx_pg_info_hd].page; ++num_bufs) { rxbp = &pnob->eth_rx_bufs[num_bufs]; pg_hd = pnob->rx_pg_info_hd; rx_page_info = &pnob->rx_page_info[pg_hd]; if (!page) { page = alloc_pages(alloc_flags, page_order); if (unlikely(page == NULL)) { adapter->be_stat.bes_ethrx_post_fail++; pnob->rxbuf_post_fail++; break; } pnob->rxbuf_post_fail = 0; busaddr = pci_map_page(adapter->pdev, page, 0, frag_size, PCI_DMA_FROMDEVICE); rx_page_info->page_offset = 0; rx_page_info->page = page; /* * If we are sharing a page among two skbs, * alloc a new one on the next iteration */ if (pnob->rx_pg_shared == false) page = NULL; } else { get_page(page); rx_page_info->page_offset += frag_size; rx_page_info->page = page; /* * We are finished with the alloced page, * Alloc a new one on the next iteration */ page = NULL; } rxbp->rxb_ctxt = (void *)rx_page_info; index_inc(&pnob->rx_pg_info_hd, pnob->rx_q_len); pci_unmap_addr_set(rx_page_info, bus, busaddr); tmp_pa = busaddr + rx_page_info->page_offset; rxbp->rxb_pa_lo = (tmp_pa & 0xFFFFFFFF); rxbp->rxb_pa_hi = (tmp_pa >> 32); rxbp->rxb_len = frag_size; list_add_tail(&rxbp->rxb_list, &rxbl); } /* End of for */ r = post_rx_buffs(pnob, &rxbl); BUG_ON(r != num_bufs); return; } /* * Interrupt service for network function. We just schedule the * tasklet which does all completion processing. */ irqreturn_t be_int(int irq, void *dev) { struct net_device *netdev = dev; struct be_net_object *pnob = netdev_priv(netdev); struct be_adapter *adapter = pnob->adapter; u32 isr; isr = CSR_READ(&pnob->fn_obj, cev.isr1); if (unlikely(!isr)) return IRQ_NONE; spin_lock(&adapter->int_lock); adapter->isr |= isr; spin_unlock(&adapter->int_lock); adapter->be_stat.bes_ints++; tasklet_schedule(&adapter->sts_handler); return IRQ_HANDLED; } /* * Poll function called by NAPI with a work budget. * We process as many UC. BC and MC receive completions * as the budget allows and return the actual number of * RX ststutses processed. */ int be_poll(struct napi_struct *napi, int budget) { struct be_net_object *pnob = container_of(napi, struct be_net_object, napi); u32 work_done; pnob->adapter->be_stat.bes_polls++; work_done = process_rx_completions(pnob, budget); BUG_ON(work_done > budget); /* All consumed */ if (work_done < budget) { netif_rx_complete(napi); /* enable intr */ be_notify_cmpl(pnob, work_done, pnob->rx_cq_id, 1); } else { /* More to be consumed; continue with interrupts disabled */ be_notify_cmpl(pnob, work_done, pnob->rx_cq_id, 0); } return work_done; } static struct EQ_ENTRY_AMAP *get_event(struct be_net_object *pnob) { struct EQ_ENTRY_AMAP *eqp = &(pnob->event_q[pnob->event_q_tl]); if (!AMAP_GET_BITS_PTR(EQ_ENTRY, Valid, eqp)) return NULL; be_adv_eq_tl(pnob); return eqp; } /* * Processes all valid events in the event ring associated with given * NetObject. Also, notifies BE the number of events processed. */ static inline u32 process_events(struct be_net_object *pnob) { struct be_adapter *adapter = pnob->adapter; struct EQ_ENTRY_AMAP *eqp; u32 rid, num_events = 0; struct net_device *netdev = pnob->netdev; while ((eqp = get_event(pnob)) != NULL) { adapter->be_stat.bes_events++; rid = AMAP_GET_BITS_PTR(EQ_ENTRY, ResourceID, eqp); if (rid == pnob->rx_cq_id) { adapter->be_stat.bes_rx_events++; netif_rx_schedule(&pnob->napi); } else if (rid == pnob->tx_cq_id) { process_nic_tx_completions(pnob); } else if (rid == pnob->mcc_cq_id) { be_mcc_process_cq(&pnob->mcc_q_obj, 1); } else { dev_info(&netdev->dev, "Invalid EQ ResourceID %d\n", rid); } AMAP_SET_BITS_PTR(EQ_ENTRY, Valid, eqp, 0); AMAP_SET_BITS_PTR(EQ_ENTRY, ResourceID, eqp, 0); num_events++; } return num_events; } static void update_eqd(struct be_adapter *adapter, struct be_net_object *pnob) { int status; struct be_eq_object *eq_objectp; /* update once a second */ if ((jiffies - adapter->ips_jiffies) > 1 * (HZ)) { /* One second elapsed since last update */ u32 r, new_eqd = -1; r = adapter->be_stat.bes_ints - adapter->be_stat.bes_prev_ints; r = r / ((jiffies - adapter->ips_jiffies) / (HZ)); adapter->be_stat.bes_ips = r; adapter->ips_jiffies = jiffies; adapter->be_stat.bes_prev_ints = adapter->be_stat.bes_ints; if (r > IPS_HI_WM && adapter->cur_eqd < adapter->max_eqd) new_eqd = (adapter->cur_eqd + 8); if (r < IPS_LO_WM && adapter->cur_eqd > adapter->min_eqd) new_eqd = (adapter->cur_eqd - 8); if (adapter->enable_aic && new_eqd != -1) { eq_objectp = &pnob->event_q_obj; status = be_eq_modify_delay(&pnob->fn_obj, 1, &eq_objectp, &new_eqd, NULL, NULL, NULL); if (status == BE_SUCCESS) adapter->cur_eqd = new_eqd; } } } /* This function notifies BladeEngine of how many events were processed from the event queue by ringing the corresponding door bell and optionally re-arms the event queue. n - number of events processed re_arm - 1 - re-arm the EQ, 0 - do not re-arm the EQ */ static void be_notify_event(struct be_net_object *pnob, int n, int re_arm) { struct CQ_DB_AMAP eqdb; eqdb.dw[0] = 0; AMAP_SET_BITS_PTR(CQ_DB, qid, &eqdb, pnob->event_q_id); AMAP_SET_BITS_PTR(CQ_DB, rearm, &eqdb, re_arm); AMAP_SET_BITS_PTR(CQ_DB, event, &eqdb, 1); AMAP_SET_BITS_PTR(CQ_DB, num_popped, &eqdb, n); /* * Under some situations we see an interrupt and no valid * EQ entry. To keep going, we need to ring the DB even if * numPOsted is 0. */ PD_WRITE(&pnob->fn_obj, cq_db, eqdb.dw[0]); return; } /* * Called from the tasklet scheduled by ISR. All real interrupt processing * is done here. */ void be_process_intr(unsigned long context) { struct be_adapter *adapter = (struct be_adapter *)context; struct be_net_object *pnob = adapter->net_obj; u32 isr, n; ulong flags = 0; isr = adapter->isr; /* * we create only one NIC event queue in Linux. Event is * expected only in the first event queue */ BUG_ON(isr & 0xfffffffe); if ((isr & 1) == 0) return; /* not our interrupt */ n = process_events(pnob); /* * Clear the event bit. adapter->isr is set by * hard interrupt. Prevent race with lock. */ spin_lock_irqsave(&adapter->int_lock, flags); adapter->isr &= ~1; spin_unlock_irqrestore(&adapter->int_lock, flags); be_notify_event(pnob, n, 1); /* * If previous allocation attempts had failed and * BE has used up all posted buffers, post RX buffers here */ if (pnob->rxbuf_post_fail && atomic_read(&pnob->rx_q_posted) == 0) be_post_eth_rx_buffs(pnob); update_eqd(adapter, pnob); return; }