diff options
-rw-r--r-- | drivers/net/ethernet/cavium/thunder/nic.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 121 | ||||
-rw-r--r-- | drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 11 |
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 | ||
110 | static const struct nicvf_stat nicvf_queue_stats[] = { | 111 | static 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 | |||
24 | static inline u64 nicvf_iova_to_phys(struct nicvf *nic, dma_addr_t dma_addr) | 22 | static 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 | */ |
98 | static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, gfp_t gfp, | 99 | static 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 */ | ||
147 | static 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; | ||
120 | ret: | 173 | ret: |
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 | ||
216 | struct pgcache { | ||
217 | struct page *page; | ||
218 | u64 dma_addr; | ||
219 | }; | ||
220 | |||
216 | struct rbdr { | 221 | struct 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 | ||
227 | struct rcv_queue { | 238 | struct rcv_queue { |