aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/cavium/thunder/nic.h4
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c3
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.c121
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.h11
4 files changed, 119 insertions, 20 deletions
diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h
index 6fb44218bf55..dca6aed49094 100644
--- a/drivers/net/ethernet/cavium/thunder/nic.h
+++ b/drivers/net/ethernet/cavium/thunder/nic.h
@@ -252,12 +252,14 @@ struct nicvf_drv_stats {
252 u64 tx_csum_overflow; 252 u64 tx_csum_overflow;
253 253
254 /* driver debug stats */ 254 /* driver debug stats */
255 u64 rcv_buffer_alloc_failures;
256 u64 tx_tso; 255 u64 tx_tso;
257 u64 tx_timeout; 256 u64 tx_timeout;
258 u64 txq_stop; 257 u64 txq_stop;
259 u64 txq_wake; 258 u64 txq_wake;
260 259
260 u64 rcv_buffer_alloc_failures;
261 u64 page_alloc;
262
261 struct u64_stats_sync syncp; 263 struct u64_stats_sync syncp;
262}; 264};
263 265
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
index 02a986cdbb39..a89db5f3e26e 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
@@ -100,11 +100,12 @@ static const struct nicvf_stat nicvf_drv_stats[] = {
100 NICVF_DRV_STAT(tx_csum_overlap), 100 NICVF_DRV_STAT(tx_csum_overlap),
101 NICVF_DRV_STAT(tx_csum_overflow), 101 NICVF_DRV_STAT(tx_csum_overflow),
102 102
103 NICVF_DRV_STAT(rcv_buffer_alloc_failures),
104 NICVF_DRV_STAT(tx_tso), 103 NICVF_DRV_STAT(tx_tso),
105 NICVF_DRV_STAT(tx_timeout), 104 NICVF_DRV_STAT(tx_timeout),
106 NICVF_DRV_STAT(txq_stop), 105 NICVF_DRV_STAT(txq_stop),
107 NICVF_DRV_STAT(txq_wake), 106 NICVF_DRV_STAT(txq_wake),
107 NICVF_DRV_STAT(rcv_buffer_alloc_failures),
108 NICVF_DRV_STAT(page_alloc),
108}; 109};
109 110
110static const struct nicvf_stat nicvf_queue_stats[] = { 111static const struct nicvf_stat nicvf_queue_stats[] = {
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
index 7b0fd8d871cc..12f9709bb180 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
@@ -19,8 +19,6 @@
19#include "q_struct.h" 19#include "q_struct.h"
20#include "nicvf_queues.h" 20#include "nicvf_queues.h"
21 21
22#define NICVF_PAGE_ORDER ((PAGE_SIZE <= 4096) ? PAGE_ALLOC_COSTLY_ORDER : 0)
23
24static inline u64 nicvf_iova_to_phys(struct nicvf *nic, dma_addr_t dma_addr) 22static inline u64 nicvf_iova_to_phys(struct nicvf *nic, dma_addr_t dma_addr)
25{ 23{
26 /* Translation is installed only when IOMMU is present */ 24 /* Translation is installed only when IOMMU is present */
@@ -90,33 +88,88 @@ static void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem)
90 dmem->base = NULL; 88 dmem->base = NULL;
91} 89}
92 90
93/* Allocate buffer for packet reception 91/* Allocate a new page or recycle one if possible
94 * HW returns memory address where packet is DMA'ed but not a pointer 92 *
95 * into RBDR ring, so save buffer address at the start of fragment and 93 * We cannot optimize dma mapping here, since
96 * align the start address to a cache aligned address 94 * 1. It's only one RBDR ring for 8 Rx queues.
95 * 2. CQE_RX gives address of the buffer where pkt has been DMA'ed
96 * and not idx into RBDR ring, so can't refer to saved info.
97 * 3. There are multiple receive buffers per page
97 */ 98 */
98static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, gfp_t gfp, 99static struct pgcache *nicvf_alloc_page(struct nicvf *nic,
99 u32 buf_len, u64 **rbuf) 100 struct rbdr *rbdr, gfp_t gfp)
100{ 101{
101 int order = NICVF_PAGE_ORDER; 102 struct page *page = NULL;
103 struct pgcache *pgcache, *next;
104
105 /* Check if page is already allocated */
106 pgcache = &rbdr->pgcache[rbdr->pgidx];
107 page = pgcache->page;
108 /* Check if page can be recycled */
109 if (page && (page_ref_count(page) != 1))
110 page = NULL;
111
112 if (!page) {
113 page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN, 0);
114 if (!page)
115 return NULL;
116
117 this_cpu_inc(nic->pnicvf->drv_stats->page_alloc);
118
119 /* Check for space */
120 if (rbdr->pgalloc >= rbdr->pgcnt) {
121 /* Page can still be used */
122 nic->rb_page = page;
123 return NULL;
124 }
125
126 /* Save the page in page cache */
127 pgcache->page = page;
128 rbdr->pgalloc++;
129 }
130
131 /* Take extra page reference for recycling */
132 page_ref_add(page, 1);
133
134 rbdr->pgidx++;
135 rbdr->pgidx &= (rbdr->pgcnt - 1);
136
137 /* Prefetch refcount of next page in page cache */
138 next = &rbdr->pgcache[rbdr->pgidx];
139 page = next->page;
140 if (page)
141 prefetch(&page->_refcount);
142
143 return pgcache;
144}
145
146/* Allocate buffer for packet reception */
147static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr,
148 gfp_t gfp, u32 buf_len, u64 **rbuf)
149{
150 struct pgcache *pgcache = NULL;
102 151
103 /* Check if request can be accomodated in previous allocated page */ 152 /* Check if request can be accomodated in previous allocated page */
104 if (nic->rb_page && 153 if (nic->rb_page &&
105 ((nic->rb_page_offset + buf_len) < (PAGE_SIZE << order))) { 154 ((nic->rb_page_offset + buf_len) <= PAGE_SIZE)) {
106 nic->rb_pageref++; 155 nic->rb_pageref++;
107 goto ret; 156 goto ret;
108 } 157 }
109 158
110 nicvf_get_page(nic); 159 nicvf_get_page(nic);
160 nic->rb_page = NULL;
111 161
112 /* Allocate a new page */ 162 /* Get new page, either recycled or new one */
113 nic->rb_page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN, 163 pgcache = nicvf_alloc_page(nic, rbdr, gfp);
114 order); 164 if (!pgcache && !nic->rb_page) {
115 if (!nic->rb_page) {
116 this_cpu_inc(nic->pnicvf->drv_stats->rcv_buffer_alloc_failures); 165 this_cpu_inc(nic->pnicvf->drv_stats->rcv_buffer_alloc_failures);
117 return -ENOMEM; 166 return -ENOMEM;
118 } 167 }
168
119 nic->rb_page_offset = 0; 169 nic->rb_page_offset = 0;
170 /* Check if it's recycled */
171 if (pgcache)
172 nic->rb_page = pgcache->page;
120ret: 173ret:
121 /* HW will ensure data coherency, CPU sync not required */ 174 /* HW will ensure data coherency, CPU sync not required */
122 *rbuf = (u64 *)((u64)dma_map_page_attrs(&nic->pdev->dev, nic->rb_page, 175 *rbuf = (u64 *)((u64)dma_map_page_attrs(&nic->pdev->dev, nic->rb_page,
@@ -125,7 +178,7 @@ ret:
125 DMA_ATTR_SKIP_CPU_SYNC)); 178 DMA_ATTR_SKIP_CPU_SYNC));
126 if (dma_mapping_error(&nic->pdev->dev, (dma_addr_t)*rbuf)) { 179 if (dma_mapping_error(&nic->pdev->dev, (dma_addr_t)*rbuf)) {
127 if (!nic->rb_page_offset) 180 if (!nic->rb_page_offset)
128 __free_pages(nic->rb_page, order); 181 __free_pages(nic->rb_page, 0);
129 nic->rb_page = NULL; 182 nic->rb_page = NULL;
130 return -ENOMEM; 183 return -ENOMEM;
131 } 184 }
@@ -177,10 +230,26 @@ static int nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr,
177 rbdr->head = 0; 230 rbdr->head = 0;
178 rbdr->tail = 0; 231 rbdr->tail = 0;
179 232
233 /* Initialize page recycling stuff.
234 *
235 * Can't use single buffer per page especially with 64K pages.
236 * On embedded platforms i.e 81xx/83xx available memory itself
237 * is low and minimum ring size of RBDR is 8K, that takes away
238 * lots of memory.
239 */
240 rbdr->pgcnt = ring_len / (PAGE_SIZE / buf_size);
241 rbdr->pgcnt = roundup_pow_of_two(rbdr->pgcnt);
242 rbdr->pgcache = kzalloc(sizeof(*rbdr->pgcache) *
243 rbdr->pgcnt, GFP_KERNEL);
244 if (!rbdr->pgcache)
245 return -ENOMEM;
246 rbdr->pgidx = 0;
247 rbdr->pgalloc = 0;
248
180 nic->rb_page = NULL; 249 nic->rb_page = NULL;
181 for (idx = 0; idx < ring_len; idx++) { 250 for (idx = 0; idx < ring_len; idx++) {
182 err = nicvf_alloc_rcv_buffer(nic, GFP_KERNEL, RCV_FRAG_LEN, 251 err = nicvf_alloc_rcv_buffer(nic, rbdr, GFP_KERNEL,
183 &rbuf); 252 RCV_FRAG_LEN, &rbuf);
184 if (err) { 253 if (err) {
185 /* To free already allocated and mapped ones */ 254 /* To free already allocated and mapped ones */
186 rbdr->tail = idx - 1; 255 rbdr->tail = idx - 1;
@@ -201,6 +270,7 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr)
201{ 270{
202 int head, tail; 271 int head, tail;
203 u64 buf_addr, phys_addr; 272 u64 buf_addr, phys_addr;
273 struct pgcache *pgcache;
204 struct rbdr_entry_t *desc; 274 struct rbdr_entry_t *desc;
205 275
206 if (!rbdr) 276 if (!rbdr)
@@ -234,6 +304,18 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr)
234 if (phys_addr) 304 if (phys_addr)
235 put_page(virt_to_page(phys_to_virt(phys_addr))); 305 put_page(virt_to_page(phys_to_virt(phys_addr)));
236 306
307 /* Sync page cache info */
308 smp_rmb();
309
310 /* Release additional page references held for recycling */
311 head = 0;
312 while (head < rbdr->pgcnt) {
313 pgcache = &rbdr->pgcache[head];
314 if (pgcache->page && page_ref_count(pgcache->page) != 0)
315 put_page(pgcache->page);
316 head++;
317 }
318
237 /* Free RBDR ring */ 319 /* Free RBDR ring */
238 nicvf_free_q_desc_mem(nic, &rbdr->dmem); 320 nicvf_free_q_desc_mem(nic, &rbdr->dmem);
239} 321}
@@ -269,13 +351,16 @@ refill:
269 else 351 else
270 refill_rb_cnt = qs->rbdr_len - qcount - 1; 352 refill_rb_cnt = qs->rbdr_len - qcount - 1;
271 353
354 /* Sync page cache info */
355 smp_rmb();
356
272 /* Start filling descs from tail */ 357 /* Start filling descs from tail */
273 tail = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_TAIL, rbdr_idx) >> 3; 358 tail = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_TAIL, rbdr_idx) >> 3;
274 while (refill_rb_cnt) { 359 while (refill_rb_cnt) {
275 tail++; 360 tail++;
276 tail &= (rbdr->dmem.q_len - 1); 361 tail &= (rbdr->dmem.q_len - 1);
277 362
278 if (nicvf_alloc_rcv_buffer(nic, gfp, RCV_FRAG_LEN, &rbuf)) 363 if (nicvf_alloc_rcv_buffer(nic, rbdr, gfp, RCV_FRAG_LEN, &rbuf))
279 break; 364 break;
280 365
281 desc = GET_RBDR_DESC(rbdr, tail); 366 desc = GET_RBDR_DESC(rbdr, tail);
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h
index 10cb4b84625b..da4836601d8c 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h
@@ -213,6 +213,11 @@ struct q_desc_mem {
213 void *unalign_base; 213 void *unalign_base;
214}; 214};
215 215
216struct pgcache {
217 struct page *page;
218 u64 dma_addr;
219};
220
216struct rbdr { 221struct rbdr {
217 bool enable; 222 bool enable;
218 u32 dma_size; 223 u32 dma_size;
@@ -222,6 +227,12 @@ struct rbdr {
222 u32 head; 227 u32 head;
223 u32 tail; 228 u32 tail;
224 struct q_desc_mem dmem; 229 struct q_desc_mem dmem;
230
231 /* For page recycling */
232 int pgidx;
233 int pgcnt;
234 int pgalloc;
235 struct pgcache *pgcache;
225} ____cacheline_aligned_in_smp; 236} ____cacheline_aligned_in_smp;
226 237
227struct rcv_queue { 238struct rcv_queue {