| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (c) 2007 Mellanox Technologies. All rights reserved. | 
|  | 3 | * | 
|  | 4 | * This software is available to you under a choice of one of two | 
|  | 5 | * licenses.  You may choose to be licensed under the terms of the GNU | 
|  | 6 | * General Public License (GPL) Version 2, available from the file | 
|  | 7 | * COPYING in the main directory of this source tree, or the | 
|  | 8 | * OpenIB.org BSD license below: | 
|  | 9 | * | 
|  | 10 | *     Redistribution and use in source and binary forms, with or | 
|  | 11 | *     without modification, are permitted provided that the following | 
|  | 12 | *     conditions are met: | 
|  | 13 | * | 
|  | 14 | *      - Redistributions of source code must retain the above | 
|  | 15 | *        copyright notice, this list of conditions and the following | 
|  | 16 | *        disclaimer. | 
|  | 17 | * | 
|  | 18 | *      - Redistributions in binary form must reproduce the above | 
|  | 19 | *        copyright notice, this list of conditions and the following | 
|  | 20 | *        disclaimer in the documentation and/or other materials | 
|  | 21 | *        provided with the distribution. | 
|  | 22 | * | 
|  | 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
|  | 24 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
|  | 25 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
|  | 26 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | 
|  | 27 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | 
|  | 28 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
|  | 29 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
|  | 30 | * SOFTWARE. | 
|  | 31 | * | 
|  | 32 | */ | 
|  | 33 |  | 
|  | 34 | #include <linux/mlx4/cq.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 35 | #include <linux/slab.h> | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 36 | #include <linux/mlx4/qp.h> | 
|  | 37 | #include <linux/skbuff.h> | 
|  | 38 | #include <linux/if_ether.h> | 
|  | 39 | #include <linux/if_vlan.h> | 
|  | 40 | #include <linux/vmalloc.h> | 
|  | 41 |  | 
|  | 42 | #include "mlx4_en.h" | 
|  | 43 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 44 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 45 | static int mlx4_en_alloc_frag(struct mlx4_en_priv *priv, | 
|  | 46 | struct mlx4_en_rx_desc *rx_desc, | 
|  | 47 | struct skb_frag_struct *skb_frags, | 
|  | 48 | struct mlx4_en_rx_alloc *ring_alloc, | 
|  | 49 | int i) | 
|  | 50 | { | 
|  | 51 | struct mlx4_en_dev *mdev = priv->mdev; | 
|  | 52 | struct mlx4_en_frag_info *frag_info = &priv->frag_info[i]; | 
|  | 53 | struct mlx4_en_rx_alloc *page_alloc = &ring_alloc[i]; | 
|  | 54 | struct page *page; | 
|  | 55 | dma_addr_t dma; | 
|  | 56 |  | 
|  | 57 | if (page_alloc->offset == frag_info->last_offset) { | 
|  | 58 | /* Allocate new page */ | 
|  | 59 | page = alloc_pages(GFP_ATOMIC | __GFP_COMP, MLX4_EN_ALLOC_ORDER); | 
|  | 60 | if (!page) | 
|  | 61 | return -ENOMEM; | 
|  | 62 |  | 
|  | 63 | skb_frags[i].page = page_alloc->page; | 
|  | 64 | skb_frags[i].page_offset = page_alloc->offset; | 
|  | 65 | page_alloc->page = page; | 
|  | 66 | page_alloc->offset = frag_info->frag_align; | 
|  | 67 | } else { | 
|  | 68 | page = page_alloc->page; | 
|  | 69 | get_page(page); | 
|  | 70 |  | 
|  | 71 | skb_frags[i].page = page; | 
|  | 72 | skb_frags[i].page_offset = page_alloc->offset; | 
|  | 73 | page_alloc->offset += frag_info->frag_stride; | 
|  | 74 | } | 
|  | 75 | dma = pci_map_single(mdev->pdev, page_address(skb_frags[i].page) + | 
|  | 76 | skb_frags[i].page_offset, frag_info->frag_size, | 
|  | 77 | PCI_DMA_FROMDEVICE); | 
|  | 78 | rx_desc->data[i].addr = cpu_to_be64(dma); | 
|  | 79 | return 0; | 
|  | 80 | } | 
|  | 81 |  | 
|  | 82 | static int mlx4_en_init_allocator(struct mlx4_en_priv *priv, | 
|  | 83 | struct mlx4_en_rx_ring *ring) | 
|  | 84 | { | 
|  | 85 | struct mlx4_en_rx_alloc *page_alloc; | 
|  | 86 | int i; | 
|  | 87 |  | 
|  | 88 | for (i = 0; i < priv->num_frags; i++) { | 
|  | 89 | page_alloc = &ring->page_alloc[i]; | 
|  | 90 | page_alloc->page = alloc_pages(GFP_ATOMIC | __GFP_COMP, | 
|  | 91 | MLX4_EN_ALLOC_ORDER); | 
|  | 92 | if (!page_alloc->page) | 
|  | 93 | goto out; | 
|  | 94 |  | 
|  | 95 | page_alloc->offset = priv->frag_info[i].frag_align; | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 96 | en_dbg(DRV, priv, "Initialized allocator:%d with page:%p\n", | 
|  | 97 | i, page_alloc->page); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 98 | } | 
|  | 99 | return 0; | 
|  | 100 |  | 
|  | 101 | out: | 
|  | 102 | while (i--) { | 
|  | 103 | page_alloc = &ring->page_alloc[i]; | 
|  | 104 | put_page(page_alloc->page); | 
|  | 105 | page_alloc->page = NULL; | 
|  | 106 | } | 
|  | 107 | return -ENOMEM; | 
|  | 108 | } | 
|  | 109 |  | 
|  | 110 | static void mlx4_en_destroy_allocator(struct mlx4_en_priv *priv, | 
|  | 111 | struct mlx4_en_rx_ring *ring) | 
|  | 112 | { | 
|  | 113 | struct mlx4_en_rx_alloc *page_alloc; | 
|  | 114 | int i; | 
|  | 115 |  | 
|  | 116 | for (i = 0; i < priv->num_frags; i++) { | 
|  | 117 | page_alloc = &ring->page_alloc[i]; | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 118 | en_dbg(DRV, priv, "Freeing allocator:%d count:%d\n", | 
|  | 119 | i, page_count(page_alloc->page)); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 120 |  | 
|  | 121 | put_page(page_alloc->page); | 
|  | 122 | page_alloc->page = NULL; | 
|  | 123 | } | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 |  | 
|  | 127 | static void mlx4_en_init_rx_desc(struct mlx4_en_priv *priv, | 
|  | 128 | struct mlx4_en_rx_ring *ring, int index) | 
|  | 129 | { | 
|  | 130 | struct mlx4_en_rx_desc *rx_desc = ring->buf + ring->stride * index; | 
|  | 131 | struct skb_frag_struct *skb_frags = ring->rx_info + | 
|  | 132 | (index << priv->log_rx_info); | 
|  | 133 | int possible_frags; | 
|  | 134 | int i; | 
|  | 135 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 136 | /* Set size and memtype fields */ | 
|  | 137 | for (i = 0; i < priv->num_frags; i++) { | 
|  | 138 | skb_frags[i].size = priv->frag_info[i].frag_size; | 
|  | 139 | rx_desc->data[i].byte_count = | 
|  | 140 | cpu_to_be32(priv->frag_info[i].frag_size); | 
|  | 141 | rx_desc->data[i].lkey = cpu_to_be32(priv->mdev->mr.key); | 
|  | 142 | } | 
|  | 143 |  | 
|  | 144 | /* If the number of used fragments does not fill up the ring stride, | 
|  | 145 | * remaining (unused) fragments must be padded with null address/size | 
|  | 146 | * and a special memory key */ | 
|  | 147 | possible_frags = (ring->stride - sizeof(struct mlx4_en_rx_desc)) / DS_SIZE; | 
|  | 148 | for (i = priv->num_frags; i < possible_frags; i++) { | 
|  | 149 | rx_desc->data[i].byte_count = 0; | 
|  | 150 | rx_desc->data[i].lkey = cpu_to_be32(MLX4_EN_MEMTYPE_PAD); | 
|  | 151 | rx_desc->data[i].addr = 0; | 
|  | 152 | } | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 |  | 
|  | 156 | static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv, | 
|  | 157 | struct mlx4_en_rx_ring *ring, int index) | 
|  | 158 | { | 
|  | 159 | struct mlx4_en_rx_desc *rx_desc = ring->buf + (index * ring->stride); | 
|  | 160 | struct skb_frag_struct *skb_frags = ring->rx_info + | 
|  | 161 | (index << priv->log_rx_info); | 
|  | 162 | int i; | 
|  | 163 |  | 
|  | 164 | for (i = 0; i < priv->num_frags; i++) | 
|  | 165 | if (mlx4_en_alloc_frag(priv, rx_desc, skb_frags, ring->page_alloc, i)) | 
|  | 166 | goto err; | 
|  | 167 |  | 
|  | 168 | return 0; | 
|  | 169 |  | 
|  | 170 | err: | 
|  | 171 | while (i--) | 
|  | 172 | put_page(skb_frags[i].page); | 
|  | 173 | return -ENOMEM; | 
|  | 174 | } | 
|  | 175 |  | 
|  | 176 | static inline void mlx4_en_update_rx_prod_db(struct mlx4_en_rx_ring *ring) | 
|  | 177 | { | 
|  | 178 | *ring->wqres.db.db = cpu_to_be32(ring->prod & 0xffff); | 
|  | 179 | } | 
|  | 180 |  | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 181 | static void mlx4_en_free_rx_desc(struct mlx4_en_priv *priv, | 
|  | 182 | struct mlx4_en_rx_ring *ring, | 
|  | 183 | int index) | 
|  | 184 | { | 
|  | 185 | struct mlx4_en_dev *mdev = priv->mdev; | 
|  | 186 | struct skb_frag_struct *skb_frags; | 
|  | 187 | struct mlx4_en_rx_desc *rx_desc = ring->buf + (index << ring->log_stride); | 
|  | 188 | dma_addr_t dma; | 
|  | 189 | int nr; | 
|  | 190 |  | 
|  | 191 | skb_frags = ring->rx_info + (index << priv->log_rx_info); | 
|  | 192 | for (nr = 0; nr < priv->num_frags; nr++) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 193 | en_dbg(DRV, priv, "Freeing fragment:%d\n", nr); | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 194 | dma = be64_to_cpu(rx_desc->data[nr].addr); | 
|  | 195 |  | 
| André Goddard Rosa | af901ca | 2009-11-14 13:09:05 -0200 | [diff] [blame] | 196 | en_dbg(DRV, priv, "Unmapping buffer at dma:0x%llx\n", (u64) dma); | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 197 | pci_unmap_single(mdev->pdev, dma, skb_frags[nr].size, | 
|  | 198 | PCI_DMA_FROMDEVICE); | 
|  | 199 | put_page(skb_frags[nr].page); | 
|  | 200 | } | 
|  | 201 | } | 
|  | 202 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 203 | static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv) | 
|  | 204 | { | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 205 | struct mlx4_en_rx_ring *ring; | 
|  | 206 | int ring_ind; | 
|  | 207 | int buf_ind; | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 208 | int new_size; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 209 |  | 
|  | 210 | for (buf_ind = 0; buf_ind < priv->prof->rx_ring_size; buf_ind++) { | 
|  | 211 | for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) { | 
|  | 212 | ring = &priv->rx_ring[ring_ind]; | 
|  | 213 |  | 
|  | 214 | if (mlx4_en_prepare_rx_desc(priv, ring, | 
|  | 215 | ring->actual_size)) { | 
|  | 216 | if (ring->actual_size < MLX4_EN_MIN_RX_SIZE) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 217 | en_err(priv, "Failed to allocate " | 
|  | 218 | "enough rx buffers\n"); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 219 | return -ENOMEM; | 
|  | 220 | } else { | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 221 | new_size = rounddown_pow_of_two(ring->actual_size); | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 222 | en_warn(priv, "Only %d buffers allocated " | 
|  | 223 | "reducing ring size to %d", | 
|  | 224 | ring->actual_size, new_size); | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 225 | goto reduce_rings; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 226 | } | 
|  | 227 | } | 
|  | 228 | ring->actual_size++; | 
|  | 229 | ring->prod++; | 
|  | 230 | } | 
|  | 231 | } | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 232 | return 0; | 
|  | 233 |  | 
|  | 234 | reduce_rings: | 
|  | 235 | for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) { | 
|  | 236 | ring = &priv->rx_ring[ring_ind]; | 
|  | 237 | while (ring->actual_size > new_size) { | 
|  | 238 | ring->actual_size--; | 
|  | 239 | ring->prod--; | 
|  | 240 | mlx4_en_free_rx_desc(priv, ring, ring->actual_size); | 
|  | 241 | } | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 242 | } | 
|  | 243 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 244 | return 0; | 
|  | 245 | } | 
|  | 246 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 247 | static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv, | 
|  | 248 | struct mlx4_en_rx_ring *ring) | 
|  | 249 | { | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 250 | int index; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 251 |  | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 252 | en_dbg(DRV, priv, "Freeing Rx buf - cons:%d prod:%d\n", | 
|  | 253 | ring->cons, ring->prod); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 254 |  | 
|  | 255 | /* Unmap and free Rx buffers */ | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 256 | BUG_ON((u32) (ring->prod - ring->cons) > ring->actual_size); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 257 | while (ring->cons != ring->prod) { | 
|  | 258 | index = ring->cons & ring->size_mask; | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 259 | en_dbg(DRV, priv, "Processing descriptor:%d\n", index); | 
| Yevgeny Petrilin | 38aab07 | 2009-05-24 03:17:11 +0000 | [diff] [blame] | 260 | mlx4_en_free_rx_desc(priv, ring, index); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 261 | ++ring->cons; | 
|  | 262 | } | 
|  | 263 | } | 
|  | 264 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 265 | int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, | 
|  | 266 | struct mlx4_en_rx_ring *ring, u32 size, u16 stride) | 
|  | 267 | { | 
|  | 268 | struct mlx4_en_dev *mdev = priv->mdev; | 
|  | 269 | int err; | 
|  | 270 | int tmp; | 
|  | 271 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 272 |  | 
|  | 273 | ring->prod = 0; | 
|  | 274 | ring->cons = 0; | 
|  | 275 | ring->size = size; | 
|  | 276 | ring->size_mask = size - 1; | 
|  | 277 | ring->stride = stride; | 
|  | 278 | ring->log_stride = ffs(ring->stride) - 1; | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 279 | ring->buf_size = ring->size * ring->stride + TXBB_SIZE; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 280 |  | 
|  | 281 | tmp = size * roundup_pow_of_two(MLX4_EN_MAX_RX_FRAGS * | 
|  | 282 | sizeof(struct skb_frag_struct)); | 
|  | 283 | ring->rx_info = vmalloc(tmp); | 
|  | 284 | if (!ring->rx_info) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 285 | en_err(priv, "Failed allocating rx_info ring\n"); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 286 | return -ENOMEM; | 
|  | 287 | } | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 288 | en_dbg(DRV, priv, "Allocated rx_info ring at addr:%p size:%d\n", | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 289 | ring->rx_info, tmp); | 
|  | 290 |  | 
|  | 291 | err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, | 
|  | 292 | ring->buf_size, 2 * PAGE_SIZE); | 
|  | 293 | if (err) | 
|  | 294 | goto err_ring; | 
|  | 295 |  | 
|  | 296 | err = mlx4_en_map_buffer(&ring->wqres.buf); | 
|  | 297 | if (err) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 298 | en_err(priv, "Failed to map RX buffer\n"); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 299 | goto err_hwq; | 
|  | 300 | } | 
|  | 301 | ring->buf = ring->wqres.buf.direct.buf; | 
|  | 302 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 303 | return 0; | 
|  | 304 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 305 | err_hwq: | 
|  | 306 | mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); | 
|  | 307 | err_ring: | 
|  | 308 | vfree(ring->rx_info); | 
|  | 309 | ring->rx_info = NULL; | 
|  | 310 | return err; | 
|  | 311 | } | 
|  | 312 |  | 
|  | 313 | int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv) | 
|  | 314 | { | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 315 | struct mlx4_en_rx_ring *ring; | 
|  | 316 | int i; | 
|  | 317 | int ring_ind; | 
|  | 318 | int err; | 
|  | 319 | int stride = roundup_pow_of_two(sizeof(struct mlx4_en_rx_desc) + | 
|  | 320 | DS_SIZE * priv->num_frags); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 321 |  | 
|  | 322 | for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) { | 
|  | 323 | ring = &priv->rx_ring[ring_ind]; | 
|  | 324 |  | 
|  | 325 | ring->prod = 0; | 
|  | 326 | ring->cons = 0; | 
|  | 327 | ring->actual_size = 0; | 
|  | 328 | ring->cqn = priv->rx_cq[ring_ind].mcq.cqn; | 
|  | 329 |  | 
|  | 330 | ring->stride = stride; | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 331 | if (ring->stride <= TXBB_SIZE) | 
|  | 332 | ring->buf += TXBB_SIZE; | 
|  | 333 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 334 | ring->log_stride = ffs(ring->stride) - 1; | 
|  | 335 | ring->buf_size = ring->size * ring->stride; | 
|  | 336 |  | 
|  | 337 | memset(ring->buf, 0, ring->buf_size); | 
|  | 338 | mlx4_en_update_rx_prod_db(ring); | 
|  | 339 |  | 
|  | 340 | /* Initailize all descriptors */ | 
|  | 341 | for (i = 0; i < ring->size; i++) | 
|  | 342 | mlx4_en_init_rx_desc(priv, ring, i); | 
|  | 343 |  | 
|  | 344 | /* Initialize page allocators */ | 
|  | 345 | err = mlx4_en_init_allocator(priv, ring); | 
|  | 346 | if (err) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 347 | en_err(priv, "Failed initializing ring allocator\n"); | 
| Yevgeny Petrilin | 60b1809 | 2011-04-06 23:25:45 +0000 | [diff] [blame] | 348 | if (ring->stride <= TXBB_SIZE) | 
|  | 349 | ring->buf -= TXBB_SIZE; | 
| Yevgeny Petrilin | 9a4f92a | 2009-04-20 04:24:28 +0000 | [diff] [blame] | 350 | ring_ind--; | 
|  | 351 | goto err_allocator; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 352 | } | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 353 | } | 
| Ingo Molnar | b58515b | 2008-11-25 16:53:32 -0800 | [diff] [blame] | 354 | err = mlx4_en_fill_rx_buffers(priv); | 
|  | 355 | if (err) | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 356 | goto err_buffers; | 
|  | 357 |  | 
|  | 358 | for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) { | 
|  | 359 | ring = &priv->rx_ring[ring_ind]; | 
|  | 360 |  | 
| Yevgeny Petrilin | 00d7d7b | 2010-08-24 03:45:20 +0000 | [diff] [blame] | 361 | ring->size_mask = ring->actual_size - 1; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 362 | mlx4_en_update_rx_prod_db(ring); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 363 | } | 
|  | 364 |  | 
|  | 365 | return 0; | 
|  | 366 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 367 | err_buffers: | 
|  | 368 | for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) | 
|  | 369 | mlx4_en_free_rx_buf(priv, &priv->rx_ring[ring_ind]); | 
|  | 370 |  | 
|  | 371 | ring_ind = priv->rx_ring_num - 1; | 
|  | 372 | err_allocator: | 
|  | 373 | while (ring_ind >= 0) { | 
| Yevgeny Petrilin | 60b1809 | 2011-04-06 23:25:45 +0000 | [diff] [blame] | 374 | if (priv->rx_ring[ring_ind].stride <= TXBB_SIZE) | 
|  | 375 | priv->rx_ring[ring_ind].buf -= TXBB_SIZE; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 376 | mlx4_en_destroy_allocator(priv, &priv->rx_ring[ring_ind]); | 
|  | 377 | ring_ind--; | 
|  | 378 | } | 
|  | 379 | return err; | 
|  | 380 | } | 
|  | 381 |  | 
|  | 382 | void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv, | 
|  | 383 | struct mlx4_en_rx_ring *ring) | 
|  | 384 | { | 
|  | 385 | struct mlx4_en_dev *mdev = priv->mdev; | 
|  | 386 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 387 | mlx4_en_unmap_buffer(&ring->wqres.buf); | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 388 | mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size + TXBB_SIZE); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 389 | vfree(ring->rx_info); | 
|  | 390 | ring->rx_info = NULL; | 
|  | 391 | } | 
|  | 392 |  | 
|  | 393 | void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv, | 
|  | 394 | struct mlx4_en_rx_ring *ring) | 
|  | 395 | { | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 396 | mlx4_en_free_rx_buf(priv, ring); | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 397 | if (ring->stride <= TXBB_SIZE) | 
|  | 398 | ring->buf -= TXBB_SIZE; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 399 | mlx4_en_destroy_allocator(priv, ring); | 
|  | 400 | } | 
|  | 401 |  | 
|  | 402 |  | 
|  | 403 | /* Unmap a completed descriptor and free unused pages */ | 
|  | 404 | static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv, | 
|  | 405 | struct mlx4_en_rx_desc *rx_desc, | 
|  | 406 | struct skb_frag_struct *skb_frags, | 
|  | 407 | struct skb_frag_struct *skb_frags_rx, | 
|  | 408 | struct mlx4_en_rx_alloc *page_alloc, | 
|  | 409 | int length) | 
|  | 410 | { | 
|  | 411 | struct mlx4_en_dev *mdev = priv->mdev; | 
|  | 412 | struct mlx4_en_frag_info *frag_info; | 
|  | 413 | int nr; | 
|  | 414 | dma_addr_t dma; | 
|  | 415 |  | 
|  | 416 | /* Collect used fragments while replacing them in the HW descirptors */ | 
|  | 417 | for (nr = 0; nr < priv->num_frags; nr++) { | 
|  | 418 | frag_info = &priv->frag_info[nr]; | 
|  | 419 | if (length <= frag_info->frag_prefix_size) | 
|  | 420 | break; | 
|  | 421 |  | 
|  | 422 | /* Save page reference in skb */ | 
|  | 423 | skb_frags_rx[nr].page = skb_frags[nr].page; | 
|  | 424 | skb_frags_rx[nr].size = skb_frags[nr].size; | 
|  | 425 | skb_frags_rx[nr].page_offset = skb_frags[nr].page_offset; | 
|  | 426 | dma = be64_to_cpu(rx_desc->data[nr].addr); | 
|  | 427 |  | 
|  | 428 | /* Allocate a replacement page */ | 
|  | 429 | if (mlx4_en_alloc_frag(priv, rx_desc, skb_frags, page_alloc, nr)) | 
|  | 430 | goto fail; | 
|  | 431 |  | 
|  | 432 | /* Unmap buffer */ | 
| Yevgeny Petrilin | 69351a2 | 2010-08-24 03:45:05 +0000 | [diff] [blame] | 433 | pci_unmap_single(mdev->pdev, dma, skb_frags_rx[nr].size, | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 434 | PCI_DMA_FROMDEVICE); | 
|  | 435 | } | 
|  | 436 | /* Adjust size of last fragment to match actual length */ | 
| roel kluin | 973507c | 2009-08-08 23:54:21 +0000 | [diff] [blame] | 437 | if (nr > 0) | 
|  | 438 | skb_frags_rx[nr - 1].size = length - | 
|  | 439 | priv->frag_info[nr - 1].frag_prefix_size; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 440 | return nr; | 
|  | 441 |  | 
|  | 442 | fail: | 
|  | 443 | /* Drop all accumulated fragments (which have already been replaced in | 
|  | 444 | * the descriptor) of this packet; remaining fragments are reused... */ | 
|  | 445 | while (nr > 0) { | 
|  | 446 | nr--; | 
|  | 447 | put_page(skb_frags_rx[nr].page); | 
|  | 448 | } | 
|  | 449 | return 0; | 
|  | 450 | } | 
|  | 451 |  | 
|  | 452 |  | 
|  | 453 | static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, | 
|  | 454 | struct mlx4_en_rx_desc *rx_desc, | 
|  | 455 | struct skb_frag_struct *skb_frags, | 
|  | 456 | struct mlx4_en_rx_alloc *page_alloc, | 
|  | 457 | unsigned int length) | 
|  | 458 | { | 
|  | 459 | struct mlx4_en_dev *mdev = priv->mdev; | 
|  | 460 | struct sk_buff *skb; | 
|  | 461 | void *va; | 
|  | 462 | int used_frags; | 
|  | 463 | dma_addr_t dma; | 
|  | 464 |  | 
|  | 465 | skb = dev_alloc_skb(SMALL_PACKET_SIZE + NET_IP_ALIGN); | 
|  | 466 | if (!skb) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 467 | en_dbg(RX_ERR, priv, "Failed allocating skb\n"); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 468 | return NULL; | 
|  | 469 | } | 
|  | 470 | skb->dev = priv->dev; | 
|  | 471 | skb_reserve(skb, NET_IP_ALIGN); | 
|  | 472 | skb->len = length; | 
|  | 473 | skb->truesize = length + sizeof(struct sk_buff); | 
|  | 474 |  | 
|  | 475 | /* Get pointer to first fragment so we could copy the headers into the | 
|  | 476 | * (linear part of the) skb */ | 
|  | 477 | va = page_address(skb_frags[0].page) + skb_frags[0].page_offset; | 
|  | 478 |  | 
|  | 479 | if (length <= SMALL_PACKET_SIZE) { | 
|  | 480 | /* We are copying all relevant data to the skb - temporarily | 
|  | 481 | * synch buffers for the copy */ | 
|  | 482 | dma = be64_to_cpu(rx_desc->data[0].addr); | 
| FUJITA Tomonori | e4fc856 | 2010-02-04 18:57:42 +0000 | [diff] [blame] | 483 | dma_sync_single_for_cpu(&mdev->pdev->dev, dma, length, | 
|  | 484 | DMA_FROM_DEVICE); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 485 | skb_copy_to_linear_data(skb, va, length); | 
| FUJITA Tomonori | e4fc856 | 2010-02-04 18:57:42 +0000 | [diff] [blame] | 486 | dma_sync_single_for_device(&mdev->pdev->dev, dma, length, | 
|  | 487 | DMA_FROM_DEVICE); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 488 | skb->tail += length; | 
|  | 489 | } else { | 
|  | 490 |  | 
|  | 491 | /* Move relevant fragments to skb */ | 
|  | 492 | used_frags = mlx4_en_complete_rx_desc(priv, rx_desc, skb_frags, | 
|  | 493 | skb_shinfo(skb)->frags, | 
|  | 494 | page_alloc, length); | 
| Yevgeny Petrilin | 785a0982 | 2009-04-26 20:42:57 +0000 | [diff] [blame] | 495 | if (unlikely(!used_frags)) { | 
|  | 496 | kfree_skb(skb); | 
|  | 497 | return NULL; | 
|  | 498 | } | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 499 | skb_shinfo(skb)->nr_frags = used_frags; | 
|  | 500 |  | 
|  | 501 | /* Copy headers into the skb linear buffer */ | 
|  | 502 | memcpy(skb->data, va, HEADER_COPY_SIZE); | 
|  | 503 | skb->tail += HEADER_COPY_SIZE; | 
|  | 504 |  | 
|  | 505 | /* Skip headers in first fragment */ | 
|  | 506 | skb_shinfo(skb)->frags[0].page_offset += HEADER_COPY_SIZE; | 
|  | 507 |  | 
|  | 508 | /* Adjust size of first fragment */ | 
|  | 509 | skb_shinfo(skb)->frags[0].size -= HEADER_COPY_SIZE; | 
|  | 510 | skb->data_len = length - HEADER_COPY_SIZE; | 
|  | 511 | } | 
|  | 512 | return skb; | 
|  | 513 | } | 
|  | 514 |  | 
| Yevgeny Petrilin | e7c1c2c4 | 2010-08-24 03:46:18 +0000 | [diff] [blame] | 515 | static void validate_loopback(struct mlx4_en_priv *priv, struct sk_buff *skb) | 
|  | 516 | { | 
|  | 517 | int i; | 
|  | 518 | int offset = ETH_HLEN; | 
|  | 519 |  | 
|  | 520 | for (i = 0; i < MLX4_LOOPBACK_TEST_PAYLOAD; i++, offset++) { | 
|  | 521 | if (*(skb->data + offset) != (unsigned char) (i & 0xff)) | 
|  | 522 | goto out_loopback; | 
|  | 523 | } | 
|  | 524 | /* Loopback found */ | 
|  | 525 | priv->loopback_ok = 1; | 
|  | 526 |  | 
|  | 527 | out_loopback: | 
|  | 528 | dev_kfree_skb_any(skb); | 
|  | 529 | } | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 530 |  | 
|  | 531 | int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget) | 
|  | 532 | { | 
|  | 533 | struct mlx4_en_priv *priv = netdev_priv(dev); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 534 | struct mlx4_cqe *cqe; | 
|  | 535 | struct mlx4_en_rx_ring *ring = &priv->rx_ring[cq->ring]; | 
|  | 536 | struct skb_frag_struct *skb_frags; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 537 | struct mlx4_en_rx_desc *rx_desc; | 
|  | 538 | struct sk_buff *skb; | 
|  | 539 | int index; | 
|  | 540 | int nr; | 
|  | 541 | unsigned int length; | 
|  | 542 | int polled = 0; | 
|  | 543 | int ip_summed; | 
|  | 544 |  | 
|  | 545 | if (!priv->port_up) | 
|  | 546 | return 0; | 
|  | 547 |  | 
|  | 548 | /* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx | 
|  | 549 | * descriptor offset can be deduced from the CQE index instead of | 
|  | 550 | * reading 'cqe->index' */ | 
|  | 551 | index = cq->mcq.cons_index & ring->size_mask; | 
|  | 552 | cqe = &cq->buf[index]; | 
|  | 553 |  | 
|  | 554 | /* Process all completed CQEs */ | 
|  | 555 | while (XNOR(cqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK, | 
|  | 556 | cq->mcq.cons_index & cq->size)) { | 
|  | 557 |  | 
|  | 558 | skb_frags = ring->rx_info + (index << priv->log_rx_info); | 
|  | 559 | rx_desc = ring->buf + (index << ring->log_stride); | 
|  | 560 |  | 
|  | 561 | /* | 
|  | 562 | * make sure we read the CQE after we read the ownership bit | 
|  | 563 | */ | 
|  | 564 | rmb(); | 
|  | 565 |  | 
|  | 566 | /* Drop packet on bad receive or bad checksum */ | 
|  | 567 | if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) == | 
|  | 568 | MLX4_CQE_OPCODE_ERROR)) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 569 | en_err(priv, "CQE completed in error - vendor " | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 570 | "syndrom:%d syndrom:%d\n", | 
|  | 571 | ((struct mlx4_err_cqe *) cqe)->vendor_err_syndrome, | 
|  | 572 | ((struct mlx4_err_cqe *) cqe)->syndrome); | 
|  | 573 | goto next; | 
|  | 574 | } | 
|  | 575 | if (unlikely(cqe->badfcs_enc & MLX4_CQE_BAD_FCS)) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 576 | en_dbg(RX_ERR, priv, "Accepted frame with bad FCS\n"); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 577 | goto next; | 
|  | 578 | } | 
|  | 579 |  | 
|  | 580 | /* | 
|  | 581 | * Packet is OK - process it. | 
|  | 582 | */ | 
|  | 583 | length = be32_to_cpu(cqe->byte_cnt); | 
|  | 584 | ring->bytes += length; | 
|  | 585 | ring->packets++; | 
|  | 586 |  | 
| Michał Mirosław | c8c64cf | 2011-04-15 04:50:49 +0000 | [diff] [blame] | 587 | if (likely(dev->features & NETIF_F_RXCSUM)) { | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 588 | if ((cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPOK)) && | 
|  | 589 | (cqe->checksum == cpu_to_be16(0xffff))) { | 
|  | 590 | priv->port_stats.rx_chksum_good++; | 
|  | 591 | /* This packet is eligible for LRO if it is: | 
|  | 592 | * - DIX Ethernet (type interpretation) | 
|  | 593 | * - TCP/IP (v4) | 
|  | 594 | * - without IP options | 
|  | 595 | * - not an IP fragment */ | 
| Yevgeny Petrilin | fa37a95 | 2010-08-24 03:46:46 +0000 | [diff] [blame] | 596 | if (dev->features & NETIF_F_GRO) { | 
|  | 597 | struct sk_buff *gro_skb = napi_get_frags(&cq->napi); | 
| Yevgeny Petrilin | ebc872c | 2010-09-05 22:20:11 +0000 | [diff] [blame] | 598 | if (!gro_skb) | 
|  | 599 | goto next; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 600 |  | 
|  | 601 | nr = mlx4_en_complete_rx_desc( | 
|  | 602 | priv, rx_desc, | 
| Yevgeny Petrilin | fa37a95 | 2010-08-24 03:46:46 +0000 | [diff] [blame] | 603 | skb_frags, skb_shinfo(gro_skb)->frags, | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 604 | ring->page_alloc, length); | 
|  | 605 | if (!nr) | 
|  | 606 | goto next; | 
|  | 607 |  | 
| Yevgeny Petrilin | fa37a95 | 2010-08-24 03:46:46 +0000 | [diff] [blame] | 608 | skb_shinfo(gro_skb)->nr_frags = nr; | 
|  | 609 | gro_skb->len = length; | 
|  | 610 | gro_skb->data_len = length; | 
|  | 611 | gro_skb->truesize += length; | 
|  | 612 | gro_skb->ip_summed = CHECKSUM_UNNECESSARY; | 
|  | 613 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 614 | if (priv->vlgrp && (cqe->vlan_my_qpn & | 
| Yevgeny Petrilin | fa37a95 | 2010-08-24 03:46:46 +0000 | [diff] [blame] | 615 | cpu_to_be32(MLX4_CQE_VLAN_PRESENT_MASK))) | 
|  | 616 | vlan_gro_frags(&cq->napi, priv->vlgrp, be16_to_cpu(cqe->sl_vid)); | 
|  | 617 | else | 
|  | 618 | napi_gro_frags(&cq->napi); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 619 |  | 
|  | 620 | goto next; | 
|  | 621 | } | 
|  | 622 |  | 
|  | 623 | /* LRO not possible, complete processing here */ | 
|  | 624 | ip_summed = CHECKSUM_UNNECESSARY; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 625 | } else { | 
|  | 626 | ip_summed = CHECKSUM_NONE; | 
|  | 627 | priv->port_stats.rx_chksum_none++; | 
|  | 628 | } | 
|  | 629 | } else { | 
|  | 630 | ip_summed = CHECKSUM_NONE; | 
|  | 631 | priv->port_stats.rx_chksum_none++; | 
|  | 632 | } | 
|  | 633 |  | 
|  | 634 | skb = mlx4_en_rx_skb(priv, rx_desc, skb_frags, | 
|  | 635 | ring->page_alloc, length); | 
|  | 636 | if (!skb) { | 
|  | 637 | priv->stats.rx_dropped++; | 
|  | 638 | goto next; | 
|  | 639 | } | 
|  | 640 |  | 
| Yevgeny Petrilin | e7c1c2c4 | 2010-08-24 03:46:18 +0000 | [diff] [blame] | 641 | if (unlikely(priv->validate_loopback)) { | 
|  | 642 | validate_loopback(priv, skb); | 
|  | 643 | goto next; | 
|  | 644 | } | 
|  | 645 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 646 | skb->ip_summed = ip_summed; | 
|  | 647 | skb->protocol = eth_type_trans(skb, dev); | 
| David S. Miller | 0c8dfc8 | 2009-01-27 16:22:32 -0800 | [diff] [blame] | 648 | skb_record_rx_queue(skb, cq->ring); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 649 |  | 
|  | 650 | /* Push it up the stack */ | 
|  | 651 | if (priv->vlgrp && (be32_to_cpu(cqe->vlan_my_qpn) & | 
|  | 652 | MLX4_CQE_VLAN_PRESENT_MASK)) { | 
|  | 653 | vlan_hwaccel_receive_skb(skb, priv->vlgrp, | 
|  | 654 | be16_to_cpu(cqe->sl_vid)); | 
|  | 655 | } else | 
|  | 656 | netif_receive_skb(skb); | 
|  | 657 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 658 | next: | 
|  | 659 | ++cq->mcq.cons_index; | 
|  | 660 | index = (cq->mcq.cons_index) & ring->size_mask; | 
|  | 661 | cqe = &cq->buf[index]; | 
|  | 662 | if (++polled == budget) { | 
|  | 663 | /* We are here because we reached the NAPI budget - | 
|  | 664 | * flush only pending LRO sessions */ | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 665 | goto out; | 
|  | 666 | } | 
|  | 667 | } | 
|  | 668 |  | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 669 | out: | 
|  | 670 | AVG_PERF_COUNTER(priv->pstats.rx_coal_avg, polled); | 
|  | 671 | mlx4_cq_set_ci(&cq->mcq); | 
|  | 672 | wmb(); /* ensure HW sees CQ consumer before we post new buffers */ | 
|  | 673 | ring->cons = cq->mcq.cons_index; | 
|  | 674 | ring->prod += polled; /* Polled descriptors were realocated in place */ | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 675 | mlx4_en_update_rx_prod_db(ring); | 
|  | 676 | return polled; | 
|  | 677 | } | 
|  | 678 |  | 
|  | 679 |  | 
|  | 680 | void mlx4_en_rx_irq(struct mlx4_cq *mcq) | 
|  | 681 | { | 
|  | 682 | struct mlx4_en_cq *cq = container_of(mcq, struct mlx4_en_cq, mcq); | 
|  | 683 | struct mlx4_en_priv *priv = netdev_priv(cq->dev); | 
|  | 684 |  | 
|  | 685 | if (priv->port_up) | 
| Ben Hutchings | 288379f | 2009-01-19 16:43:59 -0800 | [diff] [blame] | 686 | napi_schedule(&cq->napi); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 687 | else | 
|  | 688 | mlx4_en_arm_cq(priv, cq); | 
|  | 689 | } | 
|  | 690 |  | 
|  | 691 | /* Rx CQ polling - called by NAPI */ | 
|  | 692 | int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) | 
|  | 693 | { | 
|  | 694 | struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi); | 
|  | 695 | struct net_device *dev = cq->dev; | 
|  | 696 | struct mlx4_en_priv *priv = netdev_priv(dev); | 
|  | 697 | int done; | 
|  | 698 |  | 
|  | 699 | done = mlx4_en_process_rx_cq(dev, cq, budget); | 
|  | 700 |  | 
|  | 701 | /* If we used up all the quota - we're probably not done yet... */ | 
|  | 702 | if (done == budget) | 
|  | 703 | INC_PERF_COUNTER(priv->pstats.napi_quota); | 
|  | 704 | else { | 
|  | 705 | /* Done for now */ | 
| Ben Hutchings | 288379f | 2009-01-19 16:43:59 -0800 | [diff] [blame] | 706 | napi_complete(napi); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 707 | mlx4_en_arm_cq(priv, cq); | 
|  | 708 | } | 
|  | 709 | return done; | 
|  | 710 | } | 
|  | 711 |  | 
|  | 712 |  | 
| Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 713 | /* Calculate the last offset position that accommodates a full fragment | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 714 | * (assuming fagment size = stride-align) */ | 
|  | 715 | static int mlx4_en_last_alloc_offset(struct mlx4_en_priv *priv, u16 stride, u16 align) | 
|  | 716 | { | 
|  | 717 | u16 res = MLX4_EN_ALLOC_SIZE % stride; | 
|  | 718 | u16 offset = MLX4_EN_ALLOC_SIZE - stride - res + align; | 
|  | 719 |  | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 720 | en_dbg(DRV, priv, "Calculated last offset for stride:%d align:%d " | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 721 | "res:%d offset:%d\n", stride, align, res, offset); | 
|  | 722 | return offset; | 
|  | 723 | } | 
|  | 724 |  | 
|  | 725 |  | 
|  | 726 | static int frag_sizes[] = { | 
|  | 727 | FRAG_SZ0, | 
|  | 728 | FRAG_SZ1, | 
|  | 729 | FRAG_SZ2, | 
|  | 730 | FRAG_SZ3 | 
|  | 731 | }; | 
|  | 732 |  | 
|  | 733 | void mlx4_en_calc_rx_buf(struct net_device *dev) | 
|  | 734 | { | 
|  | 735 | struct mlx4_en_priv *priv = netdev_priv(dev); | 
|  | 736 | int eff_mtu = dev->mtu + ETH_HLEN + VLAN_HLEN + ETH_LLC_SNAP_SIZE; | 
|  | 737 | int buf_size = 0; | 
|  | 738 | int i = 0; | 
|  | 739 |  | 
|  | 740 | while (buf_size < eff_mtu) { | 
|  | 741 | priv->frag_info[i].frag_size = | 
|  | 742 | (eff_mtu > buf_size + frag_sizes[i]) ? | 
|  | 743 | frag_sizes[i] : eff_mtu - buf_size; | 
|  | 744 | priv->frag_info[i].frag_prefix_size = buf_size; | 
|  | 745 | if (!i)	{ | 
|  | 746 | priv->frag_info[i].frag_align = NET_IP_ALIGN; | 
|  | 747 | priv->frag_info[i].frag_stride = | 
|  | 748 | ALIGN(frag_sizes[i] + NET_IP_ALIGN, SMP_CACHE_BYTES); | 
|  | 749 | } else { | 
|  | 750 | priv->frag_info[i].frag_align = 0; | 
|  | 751 | priv->frag_info[i].frag_stride = | 
|  | 752 | ALIGN(frag_sizes[i], SMP_CACHE_BYTES); | 
|  | 753 | } | 
|  | 754 | priv->frag_info[i].last_offset = mlx4_en_last_alloc_offset( | 
|  | 755 | priv, priv->frag_info[i].frag_stride, | 
|  | 756 | priv->frag_info[i].frag_align); | 
|  | 757 | buf_size += priv->frag_info[i].frag_size; | 
|  | 758 | i++; | 
|  | 759 | } | 
|  | 760 |  | 
|  | 761 | priv->num_frags = i; | 
|  | 762 | priv->rx_skb_size = eff_mtu; | 
|  | 763 | priv->log_rx_info = ROUNDUP_LOG2(i * sizeof(struct skb_frag_struct)); | 
|  | 764 |  | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 765 | en_dbg(DRV, priv, "Rx buffer scatter-list (effective-mtu:%d " | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 766 | "num_frags:%d):\n", eff_mtu, priv->num_frags); | 
|  | 767 | for (i = 0; i < priv->num_frags; i++) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 768 | en_dbg(DRV, priv, "  frag:%d - size:%d prefix:%d align:%d " | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 769 | "stride:%d last_offset:%d\n", i, | 
|  | 770 | priv->frag_info[i].frag_size, | 
|  | 771 | priv->frag_info[i].frag_prefix_size, | 
|  | 772 | priv->frag_info[i].frag_align, | 
|  | 773 | priv->frag_info[i].frag_stride, | 
|  | 774 | priv->frag_info[i].last_offset); | 
|  | 775 | } | 
|  | 776 | } | 
|  | 777 |  | 
|  | 778 | /* RSS related functions */ | 
|  | 779 |  | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 780 | static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn, | 
|  | 781 | struct mlx4_en_rx_ring *ring, | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 782 | enum mlx4_qp_state *state, | 
|  | 783 | struct mlx4_qp *qp) | 
|  | 784 | { | 
|  | 785 | struct mlx4_en_dev *mdev = priv->mdev; | 
|  | 786 | struct mlx4_qp_context *context; | 
|  | 787 | int err = 0; | 
|  | 788 |  | 
|  | 789 | context = kmalloc(sizeof *context , GFP_KERNEL); | 
|  | 790 | if (!context) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 791 | en_err(priv, "Failed to allocate qp context\n"); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 792 | return -ENOMEM; | 
|  | 793 | } | 
|  | 794 |  | 
|  | 795 | err = mlx4_qp_alloc(mdev->dev, qpn, qp); | 
|  | 796 | if (err) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 797 | en_err(priv, "Failed to allocate qp #%x\n", qpn); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 798 | goto out; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 799 | } | 
|  | 800 | qp->event = mlx4_en_sqp_event; | 
|  | 801 |  | 
|  | 802 | memset(context, 0, sizeof *context); | 
| Yevgeny Petrilin | 00d7d7b | 2010-08-24 03:45:20 +0000 | [diff] [blame] | 803 | mlx4_en_fill_qp_context(priv, ring->actual_size, ring->stride, 0, 0, | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 804 | qpn, ring->cqn, context); | 
|  | 805 | context->db_rec_addr = cpu_to_be64(ring->wqres.db.dma); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 806 |  | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 807 | err = mlx4_qp_to_ready(mdev->dev, &ring->wqres.mtt, context, qp, state); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 808 | if (err) { | 
|  | 809 | mlx4_qp_remove(mdev->dev, qp); | 
|  | 810 | mlx4_qp_free(mdev->dev, qp); | 
|  | 811 | } | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 812 | mlx4_en_update_rx_prod_db(ring); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 813 | out: | 
|  | 814 | kfree(context); | 
|  | 815 | return err; | 
|  | 816 | } | 
|  | 817 |  | 
|  | 818 | /* Allocate rx qp's and configure them according to rss map */ | 
|  | 819 | int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) | 
|  | 820 | { | 
|  | 821 | struct mlx4_en_dev *mdev = priv->mdev; | 
|  | 822 | struct mlx4_en_rss_map *rss_map = &priv->rss_map; | 
|  | 823 | struct mlx4_qp_context context; | 
|  | 824 | struct mlx4_en_rss_context *rss_context; | 
|  | 825 | void *ptr; | 
| Yevgeny Petrilin | 0533943 | 2010-08-24 03:46:42 +0000 | [diff] [blame] | 826 | u8 rss_mask = 0x3f; | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 827 | int i, qpn; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 828 | int err = 0; | 
|  | 829 | int good_qps = 0; | 
|  | 830 |  | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 831 | en_dbg(DRV, priv, "Configuring rss steering\n"); | 
| Yevgeny Petrilin | b6b912e | 2009-08-06 19:27:51 -0700 | [diff] [blame] | 832 | err = mlx4_qp_reserve_range(mdev->dev, priv->rx_ring_num, | 
|  | 833 | priv->rx_ring_num, | 
|  | 834 | &rss_map->base_qpn); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 835 | if (err) { | 
| Yevgeny Petrilin | b6b912e | 2009-08-06 19:27:51 -0700 | [diff] [blame] | 836 | en_err(priv, "Failed reserving %d qps\n", priv->rx_ring_num); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 837 | return err; | 
|  | 838 | } | 
|  | 839 |  | 
| Yevgeny Petrilin | b6b912e | 2009-08-06 19:27:51 -0700 | [diff] [blame] | 840 | for (i = 0; i < priv->rx_ring_num; i++) { | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 841 | qpn = rss_map->base_qpn + i; | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 842 | err = mlx4_en_config_rss_qp(priv, qpn, &priv->rx_ring[i], | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 843 | &rss_map->state[i], | 
|  | 844 | &rss_map->qps[i]); | 
|  | 845 | if (err) | 
|  | 846 | goto rss_err; | 
|  | 847 |  | 
|  | 848 | ++good_qps; | 
|  | 849 | } | 
|  | 850 |  | 
|  | 851 | /* Configure RSS indirection qp */ | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 852 | err = mlx4_qp_alloc(mdev->dev, priv->base_qpn, &rss_map->indir_qp); | 
|  | 853 | if (err) { | 
| Yevgeny Petrilin | 453a608 | 2009-06-01 20:27:13 +0000 | [diff] [blame] | 854 | en_err(priv, "Failed to allocate RSS indirection QP\n"); | 
| Yevgeny Petrilin | 1679200 | 2011-03-22 22:38:31 +0000 | [diff] [blame] | 855 | goto rss_err; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 856 | } | 
|  | 857 | rss_map->indir_qp.event = mlx4_en_sqp_event; | 
|  | 858 | mlx4_en_fill_qp_context(priv, 0, 0, 0, 1, priv->base_qpn, | 
| Yevgeny Petrilin | 9f519f6 | 2009-08-06 19:28:18 -0700 | [diff] [blame] | 859 | priv->rx_ring[0].cqn, &context); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 860 |  | 
|  | 861 | ptr = ((void *) &context) + 0x3c; | 
|  | 862 | rss_context = (struct mlx4_en_rss_context *) ptr; | 
| Yevgeny Petrilin | b6b912e | 2009-08-06 19:27:51 -0700 | [diff] [blame] | 863 | rss_context->base_qpn = cpu_to_be32(ilog2(priv->rx_ring_num) << 24 | | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 864 | (rss_map->base_qpn)); | 
|  | 865 | rss_context->default_qpn = cpu_to_be32(rss_map->base_qpn); | 
| Yevgeny Petrilin | 0533943 | 2010-08-24 03:46:42 +0000 | [diff] [blame] | 866 | rss_context->flags = rss_mask; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 867 |  | 
| Yevgeny Petrilin | 0533943 | 2010-08-24 03:46:42 +0000 | [diff] [blame] | 868 | if (priv->mdev->profile.udp_rss) | 
|  | 869 | rss_context->base_qpn_udp = rss_context->default_qpn; | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 870 | err = mlx4_qp_to_ready(mdev->dev, &priv->res.mtt, &context, | 
|  | 871 | &rss_map->indir_qp, &rss_map->indir_state); | 
|  | 872 | if (err) | 
|  | 873 | goto indir_err; | 
|  | 874 |  | 
|  | 875 | return 0; | 
|  | 876 |  | 
|  | 877 | indir_err: | 
|  | 878 | mlx4_qp_modify(mdev->dev, NULL, rss_map->indir_state, | 
|  | 879 | MLX4_QP_STATE_RST, NULL, 0, 0, &rss_map->indir_qp); | 
|  | 880 | mlx4_qp_remove(mdev->dev, &rss_map->indir_qp); | 
|  | 881 | mlx4_qp_free(mdev->dev, &rss_map->indir_qp); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 882 | rss_err: | 
|  | 883 | for (i = 0; i < good_qps; i++) { | 
|  | 884 | mlx4_qp_modify(mdev->dev, NULL, rss_map->state[i], | 
|  | 885 | MLX4_QP_STATE_RST, NULL, 0, 0, &rss_map->qps[i]); | 
|  | 886 | mlx4_qp_remove(mdev->dev, &rss_map->qps[i]); | 
|  | 887 | mlx4_qp_free(mdev->dev, &rss_map->qps[i]); | 
|  | 888 | } | 
| Yevgeny Petrilin | b6b912e | 2009-08-06 19:27:51 -0700 | [diff] [blame] | 889 | mlx4_qp_release_range(mdev->dev, rss_map->base_qpn, priv->rx_ring_num); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 890 | return err; | 
|  | 891 | } | 
|  | 892 |  | 
|  | 893 | void mlx4_en_release_rss_steer(struct mlx4_en_priv *priv) | 
|  | 894 | { | 
|  | 895 | struct mlx4_en_dev *mdev = priv->mdev; | 
|  | 896 | struct mlx4_en_rss_map *rss_map = &priv->rss_map; | 
|  | 897 | int i; | 
|  | 898 |  | 
|  | 899 | mlx4_qp_modify(mdev->dev, NULL, rss_map->indir_state, | 
|  | 900 | MLX4_QP_STATE_RST, NULL, 0, 0, &rss_map->indir_qp); | 
|  | 901 | mlx4_qp_remove(mdev->dev, &rss_map->indir_qp); | 
|  | 902 | mlx4_qp_free(mdev->dev, &rss_map->indir_qp); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 903 |  | 
| Yevgeny Petrilin | b6b912e | 2009-08-06 19:27:51 -0700 | [diff] [blame] | 904 | for (i = 0; i < priv->rx_ring_num; i++) { | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 905 | mlx4_qp_modify(mdev->dev, NULL, rss_map->state[i], | 
|  | 906 | MLX4_QP_STATE_RST, NULL, 0, 0, &rss_map->qps[i]); | 
|  | 907 | mlx4_qp_remove(mdev->dev, &rss_map->qps[i]); | 
|  | 908 | mlx4_qp_free(mdev->dev, &rss_map->qps[i]); | 
|  | 909 | } | 
| Yevgeny Petrilin | b6b912e | 2009-08-06 19:27:51 -0700 | [diff] [blame] | 910 | mlx4_qp_release_range(mdev->dev, rss_map->base_qpn, priv->rx_ring_num); | 
| Yevgeny Petrilin | c27a02c | 2008-10-22 15:47:49 -0700 | [diff] [blame] | 911 | } | 
|  | 912 |  | 
|  | 913 |  | 
|  | 914 |  | 
|  | 915 |  | 
|  | 916 |  |