blob: a3db0924b306ca0a9eb84e37b64a7d35464a9454 [file] [log] [blame]
Ghanim Fodi5fd0c952018-01-31 14:49:37 +02001/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
Amir Levy9659e592016-10-27 18:08:27 +03002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/bitops.h>
14#include "ipa_i.h"
15
16#define IPA_RT_TABLE_INDEX_NOT_FOUND (-1)
17#define IPA_RT_TABLE_WORD_SIZE (4)
18#define IPA_RT_INDEX_BITMAP_SIZE (32)
19#define IPA_RT_TABLE_MEMORY_ALLIGNMENT (127)
20#define IPA_RT_ENTRY_MEMORY_ALLIGNMENT (3)
21#define IPA_RT_BIT_MASK (0x1)
22#define IPA_RT_STATUS_OF_ADD_FAILED (-1)
23#define IPA_RT_STATUS_OF_DEL_FAILED (-1)
24#define IPA_RT_STATUS_OF_MDFY_FAILED (-1)
25
26/**
27 * __ipa_generate_rt_hw_rule_v2() - generates the routing hardware rule
28 * @ip: the ip address family type
29 * @entry: routing entry
30 * @buf: output buffer, buf == NULL means
31 * caller wants to know the size of the rule as seen
32 * by HW so they did not pass a valid buffer, we will use a
33 * scratch buffer instead.
34 * With this scheme we are going to
35 * generate the rule twice, once to know size using scratch
36 * buffer and second to write the rule to the actual caller
37 * supplied buffer which is of required size
38 *
39 * Returns: 0 on success, negative on failure
40 *
41 * caller needs to hold any needed locks to ensure integrity
42 *
43 */
44int __ipa_generate_rt_hw_rule_v2(enum ipa_ip_type ip,
45 struct ipa_rt_entry *entry, u8 *buf)
46{
47 struct ipa_rt_rule_hw_hdr *rule_hdr;
48 const struct ipa_rt_rule *rule =
49 (const struct ipa_rt_rule *)&entry->rule;
50 u16 en_rule = 0;
51 u32 tmp[IPA_RT_FLT_HW_RULE_BUF_SIZE/4];
52 u8 *start;
53 int pipe_idx;
54
55 if (buf == NULL) {
Mohammed Javid025d7bb2017-08-01 19:05:06 +053056 memset(tmp, 0, (IPA_RT_FLT_HW_RULE_BUF_SIZE/4));
Amir Levy9659e592016-10-27 18:08:27 +030057 buf = (u8 *)tmp;
58 }
59
60 start = buf;
61 rule_hdr = (struct ipa_rt_rule_hw_hdr *)buf;
62 pipe_idx = ipa2_get_ep_mapping(entry->rule.dst);
63 if (pipe_idx == -1) {
64 IPAERR("Wrong destination pipe specified in RT rule\n");
65 WARN_ON(1);
66 return -EPERM;
67 }
68 if (!IPA_CLIENT_IS_CONS(entry->rule.dst)) {
69 IPAERR("No RT rule on IPA_client_producer pipe.\n");
70 IPAERR("pipe_idx: %d dst_pipe: %d\n",
71 pipe_idx, entry->rule.dst);
72 WARN_ON(1);
73 return -EPERM;
74 }
75 rule_hdr->u.hdr.pipe_dest_idx = pipe_idx;
76 rule_hdr->u.hdr.system = !ipa_ctx->hdr_tbl_lcl;
77 if (entry->hdr) {
Mohammed Javid025d7bb2017-08-01 19:05:06 +053078 if (entry->hdr->cookie == IPA_HDR_COOKIE) {
79 rule_hdr->u.hdr.hdr_offset =
80 entry->hdr->offset_entry->offset >> 2;
81 } else {
82 IPAERR("Entry hdr deleted by user = %d cookie = %u\n",
83 entry->hdr->user_deleted, entry->hdr->cookie);
84 WARN_ON(1);
85 rule_hdr->u.hdr.hdr_offset = 0;
86 }
Amir Levy9659e592016-10-27 18:08:27 +030087 } else {
88 rule_hdr->u.hdr.hdr_offset = 0;
89 }
90 buf += sizeof(struct ipa_rt_rule_hw_hdr);
91
92 if (ipa_generate_hw_rule(ip, &rule->attrib, &buf, &en_rule)) {
93 IPAERR("fail to generate hw rule\n");
94 return -EPERM;
95 }
96
Utkarsh Saxena41d57c52016-11-16 12:04:28 +053097 IPADBG_LOW("en_rule 0x%x\n", en_rule);
Amir Levy9659e592016-10-27 18:08:27 +030098
99 rule_hdr->u.hdr.en_rule = en_rule;
100 ipa_write_32(rule_hdr->u.word, (u8 *)rule_hdr);
101
102 if (entry->hw_len == 0) {
103 entry->hw_len = buf - start;
104 } else if (entry->hw_len != (buf - start)) {
105 IPAERR(
Teng Fei Fan7d751942018-02-02 16:51:00 +0800106 "hw_len differs b/w passes passed=0x%x calc=0x%zxtd\n",
Amir Levy9659e592016-10-27 18:08:27 +0300107 entry->hw_len,
108 (buf - start));
109 return -EPERM;
110 }
111
112 return 0;
113}
114
115/**
116 * __ipa_generate_rt_hw_rule_v2_5() - generates the routing hardware rule
117 * @ip: the ip address family type
118 * @entry: routing entry
119 * @buf: output buffer, buf == NULL means
120 * caller wants to know the size of the rule as seen
121 * by HW so they did not pass a valid buffer, we will use a
122 * scratch buffer instead.
123 * With this scheme we are going to
124 * generate the rule twice, once to know size using scratch
125 * buffer and second to write the rule to the actual caller
126 * supplied buffer which is of required size
127 *
128 * Returns: 0 on success, negative on failure
129 *
130 * caller needs to hold any needed locks to ensure integrity
131 *
132 */
133int __ipa_generate_rt_hw_rule_v2_5(enum ipa_ip_type ip,
134 struct ipa_rt_entry *entry, u8 *buf)
135{
136 struct ipa_rt_rule_hw_hdr *rule_hdr;
137 const struct ipa_rt_rule *rule =
138 (const struct ipa_rt_rule *)&entry->rule;
139 u16 en_rule = 0;
140 u32 tmp[IPA_RT_FLT_HW_RULE_BUF_SIZE/4];
141 u8 *start;
142 int pipe_idx;
143
144 if (buf == NULL) {
145 memset(tmp, 0, IPA_RT_FLT_HW_RULE_BUF_SIZE);
146 buf = (u8 *)tmp;
147 }
148
149 start = buf;
150 rule_hdr = (struct ipa_rt_rule_hw_hdr *)buf;
151 pipe_idx = ipa2_get_ep_mapping(entry->rule.dst);
152 if (pipe_idx == -1) {
153 IPAERR("Wrong destination pipe specified in RT rule\n");
154 WARN_ON(1);
155 return -EPERM;
156 }
157 if (!IPA_CLIENT_IS_CONS(entry->rule.dst)) {
158 IPAERR("No RT rule on IPA_client_producer pipe.\n");
159 IPAERR("pipe_idx: %d dst_pipe: %d\n",
160 pipe_idx, entry->rule.dst);
161 WARN_ON(1);
162 return -EPERM;
163 }
164 rule_hdr->u.hdr_v2_5.pipe_dest_idx = pipe_idx;
165 if (entry->proc_ctx || (entry->hdr && entry->hdr->is_hdr_proc_ctx)) {
166 struct ipa_hdr_proc_ctx_entry *proc_ctx;
167
168 proc_ctx = (entry->proc_ctx) ? : entry->hdr->proc_ctx;
169 rule_hdr->u.hdr_v2_5.system = !ipa_ctx->hdr_proc_ctx_tbl_lcl;
170 BUG_ON(proc_ctx->offset_entry->offset & 31);
171 rule_hdr->u.hdr_v2_5.proc_ctx = 1;
172 rule_hdr->u.hdr_v2_5.hdr_offset =
173 (proc_ctx->offset_entry->offset +
174 ipa_ctx->hdr_proc_ctx_tbl.start_offset) >> 5;
175 } else if (entry->hdr) {
176 rule_hdr->u.hdr_v2_5.system = !ipa_ctx->hdr_tbl_lcl;
177 BUG_ON(entry->hdr->offset_entry->offset & 3);
178 rule_hdr->u.hdr_v2_5.proc_ctx = 0;
179 rule_hdr->u.hdr_v2_5.hdr_offset =
180 entry->hdr->offset_entry->offset >> 2;
181 } else {
182 rule_hdr->u.hdr_v2_5.proc_ctx = 0;
183 rule_hdr->u.hdr_v2_5.hdr_offset = 0;
184 }
185 buf += sizeof(struct ipa_rt_rule_hw_hdr);
186
187 if (ipa_generate_hw_rule(ip, &rule->attrib, &buf, &en_rule)) {
188 IPAERR("fail to generate hw rule\n");
189 return -EPERM;
190 }
191
192 IPADBG("en_rule 0x%x\n", en_rule);
193
194 rule_hdr->u.hdr_v2_5.en_rule = en_rule;
195 ipa_write_32(rule_hdr->u.word, (u8 *)rule_hdr);
196
197 if (entry->hw_len == 0) {
198 entry->hw_len = buf - start;
199 } else if (entry->hw_len != (buf - start)) {
Teng Fei Fan7d751942018-02-02 16:51:00 +0800200 IPAERR("hw_len differs b/w passes passed=0x%x calc=0x%zxtd\n",
Amir Levy9659e592016-10-27 18:08:27 +0300201 entry->hw_len, (buf - start));
202 return -EPERM;
203 }
204
205 return 0;
206}
207
208/**
209 * __ipa_generate_rt_hw_rule_v2_6L() - generates the routing hardware rule
210 * @ip: the ip address family type
211 * @entry: routing entry
212 * @buf: output buffer, buf == NULL means that the caller wants to know the size
213 * of the rule as seen by HW so they did not pass a valid buffer, we will
214 * use a scratch buffer instead.
215 * With this scheme we are going to generate the rule twice, once to know
216 * size using scratch buffer and second to write the rule to the actual
217 * caller supplied buffer which is of required size.
218 *
219 * Returns: 0 on success, negative on failure
220 *
221 * caller needs to hold any needed locks to ensure integrity
222 *
223 */
224int __ipa_generate_rt_hw_rule_v2_6L(enum ipa_ip_type ip,
225 struct ipa_rt_entry *entry, u8 *buf)
226{
227 /* Same implementation as IPAv2 */
228 return __ipa_generate_rt_hw_rule_v2(ip, entry, buf);
229}
230
231/**
232 * ipa_get_rt_hw_tbl_size() - returns the size of HW routing table
233 * @ip: the ip address family type
234 * @hdr_sz: header size
235 * @max_rt_idx: maximal index
236 *
Skylar Chang88610112016-10-19 13:30:44 -0700237 * Returns: size on success, negative on failure
Amir Levy9659e592016-10-27 18:08:27 +0300238 *
239 * caller needs to hold any needed locks to ensure integrity
240 *
241 * the MSB set in rt_idx_bitmap indicates the size of hdr of routing tbl
242 */
243static int ipa_get_rt_hw_tbl_size(enum ipa_ip_type ip, u32 *hdr_sz,
244 int *max_rt_idx)
245{
246 struct ipa_rt_tbl_set *set;
247 struct ipa_rt_tbl *tbl;
248 struct ipa_rt_entry *entry;
249 u32 total_sz = 0;
250 u32 tbl_sz;
251 u32 bitmap = ipa_ctx->rt_idx_bitmap[ip];
252 int highest_bit_set = IPA_RT_TABLE_INDEX_NOT_FOUND;
253 int i;
254 int res;
255
256 *hdr_sz = 0;
257 set = &ipa_ctx->rt_tbl_set[ip];
258
259 for (i = 0; i < IPA_RT_INDEX_BITMAP_SIZE; i++) {
260 if (bitmap & IPA_RT_BIT_MASK)
261 highest_bit_set = i;
262 bitmap >>= 1;
263 }
264
265 *max_rt_idx = highest_bit_set;
266 if (highest_bit_set == IPA_RT_TABLE_INDEX_NOT_FOUND) {
267 IPAERR("no rt tbls present\n");
268 total_sz = IPA_RT_TABLE_WORD_SIZE;
269 *hdr_sz = IPA_RT_TABLE_WORD_SIZE;
270 return total_sz;
271 }
272
273 *hdr_sz = (highest_bit_set + 1) * IPA_RT_TABLE_WORD_SIZE;
274 total_sz += *hdr_sz;
275 list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
276 tbl_sz = 0;
277 list_for_each_entry(entry, &tbl->head_rt_rule_list, link) {
278 res = ipa_ctx->ctrl->ipa_generate_rt_hw_rule(
279 ip,
280 entry,
281 NULL);
282 if (res) {
283 IPAERR("failed to find HW RT rule size\n");
284 return -EPERM;
285 }
286 tbl_sz += entry->hw_len;
287 }
288
289 if (tbl_sz)
290 tbl->sz = tbl_sz + IPA_RT_TABLE_WORD_SIZE;
291
292 if (tbl->in_sys)
293 continue;
294
295 if (tbl_sz) {
296 /* add the terminator */
297 total_sz += (tbl_sz + IPA_RT_TABLE_WORD_SIZE);
298 /* every rule-set should start at word boundary */
299 total_sz = (total_sz + IPA_RT_ENTRY_MEMORY_ALLIGNMENT) &
300 ~IPA_RT_ENTRY_MEMORY_ALLIGNMENT;
301 }
302 }
303
304 IPADBG("RT HW TBL SZ %d HDR SZ %d IP %d\n", total_sz, *hdr_sz, ip);
305
306 return total_sz;
307}
308
309static int ipa_generate_rt_hw_tbl_common(enum ipa_ip_type ip, u8 *base, u8 *hdr,
310 u32 body_ofst, u32 apps_start_idx)
311{
312 struct ipa_rt_tbl *tbl;
313 struct ipa_rt_entry *entry;
314 struct ipa_rt_tbl_set *set;
315 u32 offset;
316 u8 *body;
317 struct ipa_mem_buffer rt_tbl_mem;
318 u8 *rt_tbl_mem_body;
319 int res;
320
321 /* build the rt tbl in the DMA buffer to submit to IPA HW */
322 body = base;
323
324 set = &ipa_ctx->rt_tbl_set[ip];
325 list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
326 if (!tbl->in_sys) {
327 offset = body - base + body_ofst;
328 if (offset & IPA_RT_ENTRY_MEMORY_ALLIGNMENT) {
329 IPAERR("offset is not word multiple %d\n",
330 offset);
331 goto proc_err;
332 }
333
334 /* convert offset to words from bytes */
335 offset &= ~IPA_RT_ENTRY_MEMORY_ALLIGNMENT;
336 /* rule is at an offset from base */
337 offset |= IPA_RT_BIT_MASK;
338
339 /* update the hdr at the right index */
340 ipa_write_32(offset, hdr +
341 ((tbl->idx - apps_start_idx) *
342 IPA_RT_TABLE_WORD_SIZE));
343
344 /* generate the rule-set */
345 list_for_each_entry(entry, &tbl->head_rt_rule_list,
346 link) {
347 res = ipa_ctx->ctrl->ipa_generate_rt_hw_rule(
348 ip,
349 entry,
350 body);
351 if (res) {
352 IPAERR("failed to gen HW RT rule\n");
353 goto proc_err;
354 }
355 body += entry->hw_len;
356 }
357
358 /* write the rule-set terminator */
359 body = ipa_write_32(0, body);
360 if ((long)body & IPA_RT_ENTRY_MEMORY_ALLIGNMENT)
361 /* advance body to next word boundary */
362 body = body + (IPA_RT_TABLE_WORD_SIZE -
363 ((long)body &
364 IPA_RT_ENTRY_MEMORY_ALLIGNMENT));
365 } else {
Skylar Chang88610112016-10-19 13:30:44 -0700366 if (tbl->sz == 0) {
367 IPAERR("cannot generate 0 size table\n");
368 goto proc_err;
369 }
370
Amir Levy9659e592016-10-27 18:08:27 +0300371 /* allocate memory for the RT tbl */
372 rt_tbl_mem.size = tbl->sz;
373 rt_tbl_mem.base =
374 dma_alloc_coherent(ipa_ctx->pdev, rt_tbl_mem.size,
375 &rt_tbl_mem.phys_base, GFP_KERNEL);
376 if (!rt_tbl_mem.base) {
377 IPAERR("fail to alloc DMA buff of size %d\n",
378 rt_tbl_mem.size);
379 WARN_ON(1);
380 goto proc_err;
381 }
382
383 WARN_ON(rt_tbl_mem.phys_base &
384 IPA_RT_ENTRY_MEMORY_ALLIGNMENT);
385 rt_tbl_mem_body = rt_tbl_mem.base;
386 memset(rt_tbl_mem.base, 0, rt_tbl_mem.size);
387 /* update the hdr at the right index */
388 ipa_write_32(rt_tbl_mem.phys_base,
389 hdr + ((tbl->idx - apps_start_idx) *
390 IPA_RT_TABLE_WORD_SIZE));
391 /* generate the rule-set */
392 list_for_each_entry(entry, &tbl->head_rt_rule_list,
393 link) {
394 res = ipa_ctx->ctrl->ipa_generate_rt_hw_rule(
395 ip,
396 entry,
397 rt_tbl_mem_body);
398 if (res) {
399 IPAERR("failed to gen HW RT rule\n");
400 WARN_ON(1);
401 goto rt_table_mem_alloc_failed;
402 }
403 rt_tbl_mem_body += entry->hw_len;
404 }
405
406 /* write the rule-set terminator */
407 rt_tbl_mem_body = ipa_write_32(0, rt_tbl_mem_body);
408
409 if (tbl->curr_mem.phys_base) {
410 WARN_ON(tbl->prev_mem.phys_base);
411 tbl->prev_mem = tbl->curr_mem;
412 }
413 tbl->curr_mem = rt_tbl_mem;
414 }
415 }
416
417 return 0;
418
419rt_table_mem_alloc_failed:
420 dma_free_coherent(ipa_ctx->pdev, rt_tbl_mem.size,
421 rt_tbl_mem.base, rt_tbl_mem.phys_base);
422proc_err:
423 return -EPERM;
424}
425
426
427/**
428 * ipa_generate_rt_hw_tbl() - generates the routing hardware table
429 * @ip: [in] the ip address family type
430 * @mem: [out] buffer to put the filtering table
431 *
432 * Returns: 0 on success, negative on failure
433 */
434static int ipa_generate_rt_hw_tbl_v1_1(enum ipa_ip_type ip,
435 struct ipa_mem_buffer *mem)
436{
437 u32 hdr_sz;
438 u8 *hdr;
439 u8 *body;
440 u8 *base;
441 int max_rt_idx;
442 int i;
Skylar Chang88610112016-10-19 13:30:44 -0700443 int res;
Amir Levy9659e592016-10-27 18:08:27 +0300444
Skylar Chang88610112016-10-19 13:30:44 -0700445 res = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx);
446 if (res < 0) {
447 IPAERR("ipa_get_rt_hw_tbl_size failed %d\n", res);
448 goto error;
449 }
450
451 mem->size = res;
Amir Levy9659e592016-10-27 18:08:27 +0300452 mem->size = (mem->size + IPA_RT_TABLE_MEMORY_ALLIGNMENT) &
453 ~IPA_RT_TABLE_MEMORY_ALLIGNMENT;
454
455 if (mem->size == 0) {
456 IPAERR("rt tbl empty ip=%d\n", ip);
457 goto error;
458 }
459 mem->base = dma_alloc_coherent(ipa_ctx->pdev, mem->size,
460 &mem->phys_base, GFP_KERNEL);
461 if (!mem->base) {
462 IPAERR("fail to alloc DMA buff of size %d\n", mem->size);
463 goto error;
464 }
465
466 memset(mem->base, 0, mem->size);
467
468 /* build the rt tbl in the DMA buffer to submit to IPA HW */
469 base = hdr = (u8 *)mem->base;
470 body = base + hdr_sz;
471
472 /* setup all indices to point to the empty sys rt tbl */
473 for (i = 0; i <= max_rt_idx; i++)
474 ipa_write_32(ipa_ctx->empty_rt_tbl_mem.phys_base,
475 hdr + (i * IPA_RT_TABLE_WORD_SIZE));
476
477 if (ipa_generate_rt_hw_tbl_common(ip, base, hdr, 0, 0)) {
478 IPAERR("fail to generate RT tbl\n");
479 goto proc_err;
480 }
481
482 return 0;
483
484proc_err:
485 dma_free_coherent(ipa_ctx->pdev, mem->size, mem->base, mem->phys_base);
486 mem->base = NULL;
487error:
488 return -EPERM;
489}
490
491static void __ipa_reap_sys_rt_tbls(enum ipa_ip_type ip)
492{
493 struct ipa_rt_tbl *tbl;
494 struct ipa_rt_tbl *next;
495 struct ipa_rt_tbl_set *set;
496
497 set = &ipa_ctx->rt_tbl_set[ip];
498 list_for_each_entry(tbl, &set->head_rt_tbl_list, link) {
499 if (tbl->prev_mem.phys_base) {
Utkarsh Saxena41d57c52016-11-16 12:04:28 +0530500 IPADBG_LOW("reaping rt");
501 IPADBG_LOW("tbl name=%s ip=%d\n",
502 tbl->name, ip);
Amir Levy9659e592016-10-27 18:08:27 +0300503 dma_free_coherent(ipa_ctx->pdev, tbl->prev_mem.size,
504 tbl->prev_mem.base,
505 tbl->prev_mem.phys_base);
506 memset(&tbl->prev_mem, 0, sizeof(tbl->prev_mem));
507 }
508 }
509
510 set = &ipa_ctx->reap_rt_tbl_set[ip];
511 list_for_each_entry_safe(tbl, next, &set->head_rt_tbl_list, link) {
512 list_del(&tbl->link);
513 WARN_ON(tbl->prev_mem.phys_base != 0);
514 if (tbl->curr_mem.phys_base) {
Utkarsh Saxena41d57c52016-11-16 12:04:28 +0530515 IPADBG_LOW("reaping sys");
516 IPADBG_LOW("rt tbl name=%s ip=%d\n",
517 tbl->name, ip);
Amir Levy9659e592016-10-27 18:08:27 +0300518 dma_free_coherent(ipa_ctx->pdev, tbl->curr_mem.size,
519 tbl->curr_mem.base,
520 tbl->curr_mem.phys_base);
521 kmem_cache_free(ipa_ctx->rt_tbl_cache, tbl);
522 }
523 }
524}
525
526int __ipa_commit_rt_v1_1(enum ipa_ip_type ip)
527{
528 struct ipa_desc desc = { 0 };
529 struct ipa_mem_buffer *mem;
530 void *cmd;
531 struct ipa_ip_v4_routing_init *v4;
532 struct ipa_ip_v6_routing_init *v6;
533 u16 avail;
534 u16 size;
Utkarsh Saxena67d59b62017-05-16 22:41:50 +0530535 gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
Amir Levy9659e592016-10-27 18:08:27 +0300536
537 mem = kmalloc(sizeof(struct ipa_mem_buffer), GFP_KERNEL);
538 if (!mem) {
539 IPAERR("failed to alloc memory object\n");
540 goto fail_alloc_mem;
541 }
542
543 if (ip == IPA_IP_v4) {
544 avail = ipa_ctx->ip4_rt_tbl_lcl ? IPA_MEM_v1_RAM_V4_RT_SIZE :
545 IPA_MEM_PART(v4_rt_size_ddr);
546 size = sizeof(struct ipa_ip_v4_routing_init);
547 } else {
548 avail = ipa_ctx->ip6_rt_tbl_lcl ? IPA_MEM_v1_RAM_V6_RT_SIZE :
549 IPA_MEM_PART(v6_rt_size_ddr);
550 size = sizeof(struct ipa_ip_v6_routing_init);
551 }
Utkarsh Saxena67d59b62017-05-16 22:41:50 +0530552 cmd = kmalloc(size, flag);
Amir Levy9659e592016-10-27 18:08:27 +0300553 if (!cmd) {
554 IPAERR("failed to alloc immediate command object\n");
555 goto fail_alloc_cmd;
556 }
557
558 if (ipa_generate_rt_hw_tbl_v1_1(ip, mem)) {
559 IPAERR("fail to generate RT HW TBL ip %d\n", ip);
560 goto fail_hw_tbl_gen;
561 }
562
563 if (mem->size > avail) {
564 IPAERR("tbl too big, needed %d avail %d\n", mem->size, avail);
565 goto fail_send_cmd;
566 }
567
568 if (ip == IPA_IP_v4) {
569 v4 = (struct ipa_ip_v4_routing_init *)cmd;
570 desc.opcode = IPA_IP_V4_ROUTING_INIT;
571 v4->ipv4_rules_addr = mem->phys_base;
572 v4->size_ipv4_rules = mem->size;
573 v4->ipv4_addr = IPA_MEM_v1_RAM_V4_RT_OFST;
574 IPADBG("putting Routing IPv4 rules to phys 0x%x",
575 v4->ipv4_addr);
576 } else {
577 v6 = (struct ipa_ip_v6_routing_init *)cmd;
578 desc.opcode = IPA_IP_V6_ROUTING_INIT;
579 v6->ipv6_rules_addr = mem->phys_base;
580 v6->size_ipv6_rules = mem->size;
581 v6->ipv6_addr = IPA_MEM_v1_RAM_V6_RT_OFST;
582 IPADBG("putting Routing IPv6 rules to phys 0x%x",
583 v6->ipv6_addr);
584 }
585
586 desc.pyld = cmd;
587 desc.len = size;
588 desc.type = IPA_IMM_CMD_DESC;
589 IPA_DUMP_BUFF(mem->base, mem->phys_base, mem->size);
590
591 if (ipa_send_cmd(1, &desc)) {
592 IPAERR("fail to send immediate command\n");
593 goto fail_send_cmd;
594 }
595
596 __ipa_reap_sys_rt_tbls(ip);
597 dma_free_coherent(ipa_ctx->pdev, mem->size, mem->base, mem->phys_base);
598 kfree(cmd);
599 kfree(mem);
600
601 return 0;
602
603fail_send_cmd:
604 if (mem->base)
605 dma_free_coherent(ipa_ctx->pdev, mem->size, mem->base,
606 mem->phys_base);
607fail_hw_tbl_gen:
608 kfree(cmd);
609fail_alloc_cmd:
610 kfree(mem);
611fail_alloc_mem:
612 return -EPERM;
613}
614
615static int ipa_generate_rt_hw_tbl_v2(enum ipa_ip_type ip,
616 struct ipa_mem_buffer *mem, struct ipa_mem_buffer *head)
617{
618 u32 hdr_sz;
619 u8 *hdr;
620 u8 *body;
621 u8 *base;
622 int max_rt_idx;
623 int i;
624 u32 *entr;
625 int num_index;
626 u32 body_start_offset;
627 u32 apps_start_idx;
Skylar Chang88610112016-10-19 13:30:44 -0700628 int res;
Amir Levy9659e592016-10-27 18:08:27 +0300629
630 if (ip == IPA_IP_v4) {
631 num_index = IPA_MEM_PART(v4_apps_rt_index_hi) -
632 IPA_MEM_PART(v4_apps_rt_index_lo) + 1;
633 body_start_offset = IPA_MEM_PART(apps_v4_rt_ofst) -
634 IPA_MEM_PART(v4_rt_ofst);
635 apps_start_idx = IPA_MEM_PART(v4_apps_rt_index_lo);
636 } else {
637 num_index = IPA_MEM_PART(v6_apps_rt_index_hi) -
638 IPA_MEM_PART(v6_apps_rt_index_lo) + 1;
639 body_start_offset = IPA_MEM_PART(apps_v6_rt_ofst) -
640 IPA_MEM_PART(v6_rt_ofst);
641 apps_start_idx = IPA_MEM_PART(v6_apps_rt_index_lo);
642 }
643
644 head->size = num_index * 4;
645 head->base = dma_alloc_coherent(ipa_ctx->pdev, head->size,
646 &head->phys_base, GFP_KERNEL);
647 if (!head->base) {
648 IPAERR("fail to alloc DMA buff of size %d\n", head->size);
649 goto err;
650 }
651 entr = (u32 *)head->base;
652 hdr = (u8 *)head->base;
653 for (i = 1; i <= num_index; i++) {
654 *entr = ipa_ctx->empty_rt_tbl_mem.phys_base;
655 entr++;
656 }
657
Skylar Chang88610112016-10-19 13:30:44 -0700658 res = ipa_get_rt_hw_tbl_size(ip, &hdr_sz, &max_rt_idx);
659 if (res < 0) {
660 IPAERR("ipa_get_rt_hw_tbl_size failed %d\n", res);
661 goto base_err;
662 }
663
664 mem->size = res;
Amir Levy9659e592016-10-27 18:08:27 +0300665 mem->size -= hdr_sz;
666 mem->size = (mem->size + IPA_RT_TABLE_MEMORY_ALLIGNMENT) &
667 ~IPA_RT_TABLE_MEMORY_ALLIGNMENT;
668
669 if (mem->size > 0) {
670 mem->base = dma_alloc_coherent(ipa_ctx->pdev, mem->size,
671 &mem->phys_base, GFP_KERNEL);
672 if (!mem->base) {
673 IPAERR("fail to alloc DMA buff of size %d\n",
674 mem->size);
675 goto base_err;
676 }
677 memset(mem->base, 0, mem->size);
678 }
679
680 /* build the rt tbl in the DMA buffer to submit to IPA HW */
681 body = base = (u8 *)mem->base;
682
683 if (ipa_generate_rt_hw_tbl_common(ip, base, hdr, body_start_offset,
684 apps_start_idx)) {
685 IPAERR("fail to generate RT tbl\n");
686 goto proc_err;
687 }
688
689 return 0;
690
691proc_err:
692 if (mem->size)
693 dma_free_coherent(ipa_ctx->pdev, mem->size, mem->base,
694 mem->phys_base);
695base_err:
696 dma_free_coherent(ipa_ctx->pdev, head->size, head->base,
697 head->phys_base);
698err:
699 return -EPERM;
700}
701
702int __ipa_commit_rt_v2(enum ipa_ip_type ip)
703{
704 struct ipa_desc desc[2];
705 struct ipa_mem_buffer body;
706 struct ipa_mem_buffer head;
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530707 struct ipa_hw_imm_cmd_dma_shared_mem *cmd1 = NULL;
708 struct ipa_hw_imm_cmd_dma_shared_mem *cmd2 = NULL;
Utkarsh Saxenae6510102017-04-14 19:31:07 +0530709 gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0);
Amir Levy9659e592016-10-27 18:08:27 +0300710 u16 avail;
711 u32 num_modem_rt_index;
712 int rc = 0;
713 u32 local_addr1;
714 u32 local_addr2;
715 bool lcl;
716
717 memset(desc, 0, 2 * sizeof(struct ipa_desc));
718
719 if (ip == IPA_IP_v4) {
720 avail = ipa_ctx->ip4_rt_tbl_lcl ?
721 IPA_MEM_PART(apps_v4_rt_size) :
722 IPA_MEM_PART(v4_rt_size_ddr);
723 num_modem_rt_index =
724 IPA_MEM_PART(v4_modem_rt_index_hi) -
725 IPA_MEM_PART(v4_modem_rt_index_lo) + 1;
726 local_addr1 = ipa_ctx->smem_restricted_bytes +
727 IPA_MEM_PART(v4_rt_ofst) +
728 num_modem_rt_index * 4;
729 local_addr2 = ipa_ctx->smem_restricted_bytes +
730 IPA_MEM_PART(apps_v4_rt_ofst);
731 lcl = ipa_ctx->ip4_rt_tbl_lcl;
732 } else {
733 avail = ipa_ctx->ip6_rt_tbl_lcl ?
734 IPA_MEM_PART(apps_v6_rt_size) :
735 IPA_MEM_PART(v6_rt_size_ddr);
736 num_modem_rt_index =
737 IPA_MEM_PART(v6_modem_rt_index_hi) -
738 IPA_MEM_PART(v6_modem_rt_index_lo) + 1;
739 local_addr1 = ipa_ctx->smem_restricted_bytes +
740 IPA_MEM_PART(v6_rt_ofst) +
741 num_modem_rt_index * 4;
742 local_addr2 = ipa_ctx->smem_restricted_bytes +
743 IPA_MEM_PART(apps_v6_rt_ofst);
744 lcl = ipa_ctx->ip6_rt_tbl_lcl;
745 }
746
747 if (ipa_generate_rt_hw_tbl_v2(ip, &body, &head)) {
748 IPAERR("fail to generate RT HW TBL ip %d\n", ip);
749 rc = -EFAULT;
750 goto fail_gen;
751 }
752
753 if (body.size > avail) {
754 IPAERR("tbl too big, needed %d avail %d\n", body.size, avail);
755 rc = -EFAULT;
756 goto fail_send_cmd;
757 }
758
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530759 cmd1 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
Utkarsh Saxenae6510102017-04-14 19:31:07 +0530760 flag);
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530761 if (cmd1 == NULL) {
762 IPAERR("Failed to alloc immediate command object\n");
763 rc = -ENOMEM;
764 goto fail_send_cmd;
765 }
766
767 cmd1->size = head.size;
768 cmd1->system_addr = head.phys_base;
769 cmd1->local_addr = local_addr1;
Amir Levy9659e592016-10-27 18:08:27 +0300770 desc[0].opcode = IPA_DMA_SHARED_MEM;
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530771 desc[0].pyld = (void *)cmd1;
Amir Levy9659e592016-10-27 18:08:27 +0300772 desc[0].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
773 desc[0].type = IPA_IMM_CMD_DESC;
774
775 if (lcl) {
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530776 cmd2 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
Utkarsh Saxenae6510102017-04-14 19:31:07 +0530777 flag);
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530778 if (cmd2 == NULL) {
779 IPAERR("Failed to alloc immediate command object\n");
780 rc = -ENOMEM;
781 goto fail_send_cmd1;
782 }
783
784 cmd2->size = body.size;
785 cmd2->system_addr = body.phys_base;
786 cmd2->local_addr = local_addr2;
Amir Levy9659e592016-10-27 18:08:27 +0300787
788 desc[1].opcode = IPA_DMA_SHARED_MEM;
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530789 desc[1].pyld = (void *)cmd2;
Amir Levy9659e592016-10-27 18:08:27 +0300790 desc[1].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
791 desc[1].type = IPA_IMM_CMD_DESC;
792
793 if (ipa_send_cmd(2, desc)) {
794 IPAERR("fail to send immediate command\n");
795 rc = -EFAULT;
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530796 goto fail_send_cmd2;
Amir Levy9659e592016-10-27 18:08:27 +0300797 }
798 } else {
799 if (ipa_send_cmd(1, desc)) {
800 IPAERR("fail to send immediate command\n");
801 rc = -EFAULT;
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530802 goto fail_send_cmd1;
Amir Levy9659e592016-10-27 18:08:27 +0300803 }
804 }
805
806 IPADBG("HEAD\n");
807 IPA_DUMP_BUFF(head.base, head.phys_base, head.size);
808 if (body.size) {
809 IPADBG("BODY\n");
810 IPA_DUMP_BUFF(body.base, body.phys_base, body.size);
811 }
812 __ipa_reap_sys_rt_tbls(ip);
Utkarsh Saxena88dd3e82017-03-01 19:22:31 +0530813
814fail_send_cmd2:
815 kfree(cmd2);
816fail_send_cmd1:
817 kfree(cmd1);
Amir Levy9659e592016-10-27 18:08:27 +0300818fail_send_cmd:
819 dma_free_coherent(ipa_ctx->pdev, head.size, head.base, head.phys_base);
820 if (body.size)
821 dma_free_coherent(ipa_ctx->pdev, body.size, body.base,
822 body.phys_base);
823fail_gen:
824 return rc;
825}
826
827/**
828 * __ipa_find_rt_tbl() - find the routing table
829 * which name is given as parameter
830 * @ip: [in] the ip address family type of the wanted routing table
831 * @name: [in] the name of the wanted routing table
832 *
833 * Returns: the routing table which name is given as parameter, or NULL if it
834 * doesn't exist
835 */
836struct ipa_rt_tbl *__ipa_find_rt_tbl(enum ipa_ip_type ip, const char *name)
837{
838 struct ipa_rt_tbl *entry;
839 struct ipa_rt_tbl_set *set;
840
841 set = &ipa_ctx->rt_tbl_set[ip];
842 list_for_each_entry(entry, &set->head_rt_tbl_list, link) {
843 if (!strcmp(name, entry->name))
844 return entry;
845 }
846
847 return NULL;
848}
849
850/**
851 * ipa2_query_rt_index() - find the routing table index
852 * which name and ip type are given as parameters
853 * @in: [out] the index of the wanted routing table
854 *
855 * Returns: the routing table which name is given as parameter, or NULL if it
856 * doesn't exist
857 */
858int ipa2_query_rt_index(struct ipa_ioc_get_rt_tbl_indx *in)
859{
860 struct ipa_rt_tbl *entry;
861
862 if (in->ip >= IPA_IP_MAX) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +0530863 IPAERR_RL("bad parm\n");
Amir Levy9659e592016-10-27 18:08:27 +0300864 return -EINVAL;
865 }
866
Mohammed Javid47193a12017-06-15 18:39:07 +0530867 mutex_lock(&ipa_ctx->lock);
Amir Levy9659e592016-10-27 18:08:27 +0300868 /* check if this table exists */
Mohammed Javidcd665892017-10-11 17:05:57 +0530869 in->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
Amir Levy9659e592016-10-27 18:08:27 +0300870 entry = __ipa_find_rt_tbl(in->ip, in->name);
Mohammed Javid47193a12017-06-15 18:39:07 +0530871 if (!entry) {
872 mutex_unlock(&ipa_ctx->lock);
Amir Levy9659e592016-10-27 18:08:27 +0300873 return -EFAULT;
Mohammed Javid47193a12017-06-15 18:39:07 +0530874 }
Amir Levy9659e592016-10-27 18:08:27 +0300875
876 in->idx = entry->idx;
Mohammed Javid47193a12017-06-15 18:39:07 +0530877 mutex_unlock(&ipa_ctx->lock);
Amir Levy9659e592016-10-27 18:08:27 +0300878 return 0;
879}
880
881static struct ipa_rt_tbl *__ipa_add_rt_tbl(enum ipa_ip_type ip,
882 const char *name)
883{
884 struct ipa_rt_tbl *entry;
885 struct ipa_rt_tbl_set *set;
886 int i;
887 int id;
888
889 if (ip >= IPA_IP_MAX || name == NULL) {
890 IPAERR("bad parm\n");
891 goto error;
892 }
893
894 set = &ipa_ctx->rt_tbl_set[ip];
895 /* check if this table exists */
896 entry = __ipa_find_rt_tbl(ip, name);
897 if (!entry) {
898 entry = kmem_cache_zalloc(ipa_ctx->rt_tbl_cache, GFP_KERNEL);
899 if (!entry) {
900 IPAERR("failed to alloc RT tbl object\n");
901 goto error;
902 }
903 /* find a routing tbl index */
904 for (i = 0; i < IPA_RT_INDEX_BITMAP_SIZE; i++) {
905 if (!test_bit(i, &ipa_ctx->rt_idx_bitmap[ip])) {
906 entry->idx = i;
907 set_bit(i, &ipa_ctx->rt_idx_bitmap[ip]);
908 break;
909 }
910 }
911 if (i == IPA_RT_INDEX_BITMAP_SIZE) {
912 IPAERR("not free RT tbl indices left\n");
913 goto fail_rt_idx_alloc;
914 }
915
916 INIT_LIST_HEAD(&entry->head_rt_rule_list);
917 INIT_LIST_HEAD(&entry->link);
918 strlcpy(entry->name, name, IPA_RESOURCE_NAME_MAX);
919 entry->set = set;
Mohammed Javid93e94ce2017-06-15 15:39:04 +0530920 entry->cookie = IPA_RT_TBL_COOKIE;
Amir Levy9659e592016-10-27 18:08:27 +0300921 entry->in_sys = (ip == IPA_IP_v4) ?
922 !ipa_ctx->ip4_rt_tbl_lcl : !ipa_ctx->ip6_rt_tbl_lcl;
923 set->tbl_cnt++;
924 list_add(&entry->link, &set->head_rt_tbl_list);
925
926 IPADBG("add rt tbl idx=%d tbl_cnt=%d ip=%d\n", entry->idx,
927 set->tbl_cnt, ip);
928
929 id = ipa_id_alloc(entry);
930 if (id < 0) {
931 IPAERR("failed to add to tree\n");
932 WARN_ON(1);
Mohammed Javid93e94ce2017-06-15 15:39:04 +0530933 goto ipa_insert_failed;
Amir Levy9659e592016-10-27 18:08:27 +0300934 }
935 entry->id = id;
936 }
937
938 return entry;
939
Mohammed Javid93e94ce2017-06-15 15:39:04 +0530940ipa_insert_failed:
941 set->tbl_cnt--;
942 list_del(&entry->link);
Amir Levy9659e592016-10-27 18:08:27 +0300943fail_rt_idx_alloc:
944 entry->cookie = 0;
945 kmem_cache_free(ipa_ctx->rt_tbl_cache, entry);
946error:
947 return NULL;
948}
949
950static int __ipa_del_rt_tbl(struct ipa_rt_tbl *entry)
951{
952 enum ipa_ip_type ip = IPA_IP_MAX;
953 u32 id;
954
Mohammed Javid93e94ce2017-06-15 15:39:04 +0530955 if (entry == NULL || (entry->cookie != IPA_RT_TBL_COOKIE)) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +0530956 IPAERR_RL("bad parms\n");
Amir Levy9659e592016-10-27 18:08:27 +0300957 return -EINVAL;
958 }
959 id = entry->id;
960 if (ipa_id_find(id) == NULL) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +0530961 IPAERR_RL("lookup failed\n");
Amir Levy9659e592016-10-27 18:08:27 +0300962 return -EPERM;
963 }
964
965 if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v4])
966 ip = IPA_IP_v4;
967 else if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v6])
968 ip = IPA_IP_v6;
Mohammed Javid93e94ce2017-06-15 15:39:04 +0530969 else {
Amir Levy9659e592016-10-27 18:08:27 +0300970 WARN_ON(1);
Mohammed Javid93e94ce2017-06-15 15:39:04 +0530971 return -EPERM;
972 }
973
Amir Levy9659e592016-10-27 18:08:27 +0300974
975 if (!entry->in_sys) {
976 list_del(&entry->link);
977 clear_bit(entry->idx, &ipa_ctx->rt_idx_bitmap[ip]);
978 entry->set->tbl_cnt--;
Utkarsh Saxena41d57c52016-11-16 12:04:28 +0530979 IPADBG_LOW("del rt tbl_idx=%d tbl_cnt=%d\n", entry->idx,
Amir Levy9659e592016-10-27 18:08:27 +0300980 entry->set->tbl_cnt);
981 kmem_cache_free(ipa_ctx->rt_tbl_cache, entry);
982 } else {
983 list_move(&entry->link,
984 &ipa_ctx->reap_rt_tbl_set[ip].head_rt_tbl_list);
985 clear_bit(entry->idx, &ipa_ctx->rt_idx_bitmap[ip]);
986 entry->set->tbl_cnt--;
Utkarsh Saxena41d57c52016-11-16 12:04:28 +0530987 IPADBG_LOW("del sys rt tbl_idx=%d tbl_cnt=%d\n", entry->idx,
Amir Levy9659e592016-10-27 18:08:27 +0300988 entry->set->tbl_cnt);
989 }
990
991 /* remove the handle from the database */
992 ipa_id_remove(id);
993 return 0;
994}
995
996static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name,
997 const struct ipa_rt_rule *rule, u8 at_rear, u32 *rule_hdl)
998{
999 struct ipa_rt_tbl *tbl;
1000 struct ipa_rt_entry *entry;
1001 struct ipa_hdr_entry *hdr = NULL;
1002 struct ipa_hdr_proc_ctx_entry *proc_ctx = NULL;
1003 int id;
1004
1005 if (rule->hdr_hdl && rule->hdr_proc_ctx_hdl) {
1006 IPAERR("rule contains both hdr_hdl and hdr_proc_ctx_hdl\n");
1007 goto error;
1008 }
1009
1010 if (rule->hdr_hdl) {
1011 hdr = ipa_id_find(rule->hdr_hdl);
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301012 if ((hdr == NULL) || (hdr->cookie != IPA_HDR_COOKIE)) {
Amir Levy9659e592016-10-27 18:08:27 +03001013 IPAERR("rt rule does not point to valid hdr\n");
1014 goto error;
1015 }
1016 } else if (rule->hdr_proc_ctx_hdl) {
1017 proc_ctx = ipa_id_find(rule->hdr_proc_ctx_hdl);
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301018 if ((proc_ctx == NULL) ||
1019 (proc_ctx->cookie != IPA_PROC_HDR_COOKIE)) {
Amir Levy9659e592016-10-27 18:08:27 +03001020 IPAERR("rt rule does not point to valid proc ctx\n");
1021 goto error;
1022 }
1023 }
1024
1025
1026 tbl = __ipa_add_rt_tbl(ip, name);
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301027 if (tbl == NULL || (tbl->cookie != IPA_RT_TBL_COOKIE)) {
Amir Levy9659e592016-10-27 18:08:27 +03001028 IPAERR("bad params\n");
1029 goto error;
1030 }
1031 /*
1032 * do not allow any rules to be added at end of the "default" routing
1033 * tables
1034 */
1035 if (!strcmp(tbl->name, IPA_DFLT_RT_TBL_NAME) &&
1036 (tbl->rule_cnt > 0) && (at_rear != 0)) {
1037 IPAERR("cannot add rule at end of tbl rule_cnt=%d at_rear=%d\n",
1038 tbl->rule_cnt, at_rear);
1039 goto error;
1040 }
1041
1042 entry = kmem_cache_zalloc(ipa_ctx->rt_rule_cache, GFP_KERNEL);
1043 if (!entry) {
1044 IPAERR("failed to alloc RT rule object\n");
1045 goto error;
1046 }
1047 INIT_LIST_HEAD(&entry->link);
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301048 entry->cookie = IPA_RT_RULE_COOKIE;
Amir Levy9659e592016-10-27 18:08:27 +03001049 entry->rule = *rule;
1050 entry->tbl = tbl;
1051 entry->hdr = hdr;
1052 entry->proc_ctx = proc_ctx;
1053 if (at_rear)
1054 list_add_tail(&entry->link, &tbl->head_rt_rule_list);
1055 else
1056 list_add(&entry->link, &tbl->head_rt_rule_list);
1057 tbl->rule_cnt++;
1058 if (entry->hdr)
1059 entry->hdr->ref_cnt++;
1060 else if (entry->proc_ctx)
1061 entry->proc_ctx->ref_cnt++;
1062 id = ipa_id_alloc(entry);
1063 if (id < 0) {
1064 IPAERR("failed to add to tree\n");
1065 WARN_ON(1);
1066 goto ipa_insert_failed;
1067 }
Utkarsh Saxena41d57c52016-11-16 12:04:28 +05301068 IPADBG_LOW("add rt rule tbl_idx=%d", tbl->idx);
1069 IPADBG_LOW("rule_cnt=%d\n", tbl->rule_cnt);
Amir Levy9659e592016-10-27 18:08:27 +03001070 *rule_hdl = id;
1071 entry->id = id;
1072
1073 return 0;
1074
1075ipa_insert_failed:
1076 if (entry->hdr)
1077 entry->hdr->ref_cnt--;
1078 else if (entry->proc_ctx)
1079 entry->proc_ctx->ref_cnt--;
1080 list_del(&entry->link);
1081 kmem_cache_free(ipa_ctx->rt_rule_cache, entry);
1082error:
1083 return -EPERM;
1084}
1085
1086/**
1087 * ipa2_add_rt_rule() - Add the specified routing rules to SW and optionally
1088 * commit to IPA HW
1089 * @rules: [inout] set of routing rules to add
1090 *
1091 * Returns: 0 on success, negative on failure
1092 *
1093 * Note: Should not be called from atomic context
1094 */
1095int ipa2_add_rt_rule(struct ipa_ioc_add_rt_rule *rules)
1096{
1097 int i;
1098 int ret;
1099
1100 if (rules == NULL || rules->num_rules == 0 || rules->ip >= IPA_IP_MAX) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301101 IPAERR_RL("bad parm\n");
Amir Levy9659e592016-10-27 18:08:27 +03001102 return -EINVAL;
1103 }
1104
1105 mutex_lock(&ipa_ctx->lock);
1106 for (i = 0; i < rules->num_rules; i++) {
Mohammed Javidcd665892017-10-11 17:05:57 +05301107 rules->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = '\0';
Amir Levy9659e592016-10-27 18:08:27 +03001108 if (__ipa_add_rt_rule(rules->ip, rules->rt_tbl_name,
1109 &rules->rules[i].rule,
1110 rules->rules[i].at_rear,
1111 &rules->rules[i].rt_rule_hdl)) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301112 IPAERR_RL("failed to add rt rule %d\n", i);
Amir Levy9659e592016-10-27 18:08:27 +03001113 rules->rules[i].status = IPA_RT_STATUS_OF_ADD_FAILED;
1114 } else {
1115 rules->rules[i].status = 0;
1116 }
1117 }
1118
1119 if (rules->commit)
1120 if (ipa_ctx->ctrl->ipa_commit_rt(rules->ip)) {
1121 ret = -EPERM;
1122 goto bail;
1123 }
1124
1125 ret = 0;
1126bail:
1127 mutex_unlock(&ipa_ctx->lock);
1128 return ret;
1129}
1130
1131int __ipa_del_rt_rule(u32 rule_hdl)
1132{
1133 struct ipa_rt_entry *entry;
1134 int id;
1135
1136 entry = ipa_id_find(rule_hdl);
1137
1138 if (entry == NULL) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301139 IPAERR_RL("lookup failed\n");
Amir Levy9659e592016-10-27 18:08:27 +03001140 return -EINVAL;
1141 }
1142
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301143 if (entry->cookie != IPA_RT_RULE_COOKIE) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301144 IPAERR_RL("bad params\n");
Amir Levy9659e592016-10-27 18:08:27 +03001145 return -EINVAL;
1146 }
1147
Ghanim Fodi5fd0c952018-01-31 14:49:37 +02001148 if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) {
1149 IPADBG("Deleting rule from default rt table idx=%u\n",
1150 entry->tbl->idx);
1151 if (entry->tbl->rule_cnt == 1) {
1152 IPAERR_RL("Default tbl last rule cannot be deleted\n");
1153 return -EINVAL;
1154 }
1155 }
1156
Amir Levy9659e592016-10-27 18:08:27 +03001157 if (entry->hdr)
1158 __ipa_release_hdr(entry->hdr->id);
1159 else if (entry->proc_ctx)
1160 __ipa_release_hdr_proc_ctx(entry->proc_ctx->id);
1161 list_del(&entry->link);
1162 entry->tbl->rule_cnt--;
Utkarsh Saxena41d57c52016-11-16 12:04:28 +05301163 IPADBG_LOW("del rt rule tbl_idx=%d rule_cnt=%d\n", entry->tbl->idx,
Amir Levy9659e592016-10-27 18:08:27 +03001164 entry->tbl->rule_cnt);
1165 if (entry->tbl->rule_cnt == 0 && entry->tbl->ref_cnt == 0) {
1166 if (__ipa_del_rt_tbl(entry->tbl))
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301167 IPAERR_RL("fail to del RT tbl\n");
Amir Levy9659e592016-10-27 18:08:27 +03001168 }
1169 entry->cookie = 0;
1170 id = entry->id;
1171 kmem_cache_free(ipa_ctx->rt_rule_cache, entry);
1172
1173 /* remove the handle from the database */
1174 ipa_id_remove(id);
1175
1176 return 0;
1177}
1178
1179/**
1180 * ipa2_del_rt_rule() - Remove the specified routing rules to SW and optionally
1181 * commit to IPA HW
1182 * @hdls: [inout] set of routing rules to delete
1183 *
1184 * Returns: 0 on success, negative on failure
1185 *
1186 * Note: Should not be called from atomic context
1187 */
1188int ipa2_del_rt_rule(struct ipa_ioc_del_rt_rule *hdls)
1189{
1190 int i;
1191 int ret;
1192
1193 if (hdls == NULL || hdls->num_hdls == 0 || hdls->ip >= IPA_IP_MAX) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301194 IPAERR_RL("bad parm\n");
Amir Levy9659e592016-10-27 18:08:27 +03001195 return -EINVAL;
1196 }
1197
1198 mutex_lock(&ipa_ctx->lock);
1199 for (i = 0; i < hdls->num_hdls; i++) {
1200 if (__ipa_del_rt_rule(hdls->hdl[i].hdl)) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301201 IPAERR_RL("failed to del rt rule %i\n", i);
Amir Levy9659e592016-10-27 18:08:27 +03001202 hdls->hdl[i].status = IPA_RT_STATUS_OF_DEL_FAILED;
1203 } else {
1204 hdls->hdl[i].status = 0;
1205 }
1206 }
1207
1208 if (hdls->commit)
1209 if (ipa_ctx->ctrl->ipa_commit_rt(hdls->ip)) {
1210 ret = -EPERM;
1211 goto bail;
1212 }
1213
1214 ret = 0;
1215bail:
1216 mutex_unlock(&ipa_ctx->lock);
1217 return ret;
1218}
1219
1220/**
1221 * ipa2_commit_rt_rule() - Commit the current SW routing table of specified type
1222 * to IPA HW
1223 * @ip: The family of routing tables
1224 *
1225 * Returns: 0 on success, negative on failure
1226 *
1227 * Note: Should not be called from atomic context
1228 */
1229int ipa2_commit_rt(enum ipa_ip_type ip)
1230{
1231 int ret;
1232
1233 if (ip >= IPA_IP_MAX) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301234 IPAERR_RL("bad parm\n");
Amir Levy9659e592016-10-27 18:08:27 +03001235 return -EINVAL;
1236 }
1237
1238 /*
1239 * issue a commit on the filtering module of same IP type since
1240 * filtering rules point to routing tables
1241 */
1242 if (ipa2_commit_flt(ip))
1243 return -EPERM;
1244
1245 mutex_lock(&ipa_ctx->lock);
1246 if (ipa_ctx->ctrl->ipa_commit_rt(ip)) {
1247 ret = -EPERM;
1248 goto bail;
1249 }
1250
1251 ret = 0;
1252bail:
1253 mutex_unlock(&ipa_ctx->lock);
1254 return ret;
1255}
1256
1257/**
1258 * ipa2_reset_rt() - reset the current SW routing table of specified type
1259 * (does not commit to HW)
1260 * @ip: The family of routing tables
1261 *
1262 * Returns: 0 on success, negative on failure
1263 *
1264 * Note: Should not be called from atomic context
1265 */
1266int ipa2_reset_rt(enum ipa_ip_type ip)
1267{
1268 struct ipa_rt_tbl *tbl;
1269 struct ipa_rt_tbl *tbl_next;
1270 struct ipa_rt_tbl_set *set;
1271 struct ipa_rt_entry *rule;
1272 struct ipa_rt_entry *rule_next;
1273 struct ipa_rt_tbl_set *rset;
1274 u32 apps_start_idx;
1275 int id;
1276
1277 if (ip >= IPA_IP_MAX) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301278 IPAERR_RL("bad parm\n");
Amir Levy9659e592016-10-27 18:08:27 +03001279 return -EINVAL;
1280 }
1281
1282 if (ipa_ctx->ipa_hw_type >= IPA_HW_v2_0) {
1283 if (ip == IPA_IP_v4)
1284 apps_start_idx = IPA_MEM_PART(v4_apps_rt_index_lo);
1285 else
1286 apps_start_idx = IPA_MEM_PART(v6_apps_rt_index_lo);
1287 } else {
1288 apps_start_idx = 0;
1289 }
1290
1291 /*
1292 * issue a reset on the filtering module of same IP type since
1293 * filtering rules point to routing tables
1294 */
1295 if (ipa2_reset_flt(ip))
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301296 IPAERR_RL("fail to reset flt ip=%d\n", ip);
Amir Levy9659e592016-10-27 18:08:27 +03001297
1298 set = &ipa_ctx->rt_tbl_set[ip];
1299 rset = &ipa_ctx->reap_rt_tbl_set[ip];
1300 mutex_lock(&ipa_ctx->lock);
1301 IPADBG("reset rt ip=%d\n", ip);
1302 list_for_each_entry_safe(tbl, tbl_next, &set->head_rt_tbl_list, link) {
1303 list_for_each_entry_safe(rule, rule_next,
1304 &tbl->head_rt_rule_list, link) {
1305 if (ipa_id_find(rule->id) == NULL) {
1306 WARN_ON(1);
1307 mutex_unlock(&ipa_ctx->lock);
1308 return -EFAULT;
1309 }
1310
1311 /*
1312 * for the "default" routing tbl, remove all but the
1313 * last rule
1314 */
1315 if (tbl->idx == apps_start_idx && tbl->rule_cnt == 1)
1316 continue;
1317
1318 list_del(&rule->link);
1319 tbl->rule_cnt--;
1320 if (rule->hdr)
1321 __ipa_release_hdr(rule->hdr->id);
1322 else if (rule->proc_ctx)
1323 __ipa_release_hdr_proc_ctx(rule->proc_ctx->id);
1324 rule->cookie = 0;
1325 id = rule->id;
1326 kmem_cache_free(ipa_ctx->rt_rule_cache, rule);
1327
1328 /* remove the handle from the database */
1329 ipa_id_remove(id);
1330 }
1331
1332 if (ipa_id_find(tbl->id) == NULL) {
1333 WARN_ON(1);
1334 mutex_unlock(&ipa_ctx->lock);
1335 return -EFAULT;
1336 }
1337 id = tbl->id;
1338
1339 /* do not remove the "default" routing tbl which has index 0 */
1340 if (tbl->idx != apps_start_idx) {
1341 if (!tbl->in_sys) {
1342 list_del(&tbl->link);
1343 set->tbl_cnt--;
1344 clear_bit(tbl->idx,
1345 &ipa_ctx->rt_idx_bitmap[ip]);
1346 IPADBG("rst rt tbl_idx=%d tbl_cnt=%d\n",
1347 tbl->idx, set->tbl_cnt);
1348 kmem_cache_free(ipa_ctx->rt_tbl_cache, tbl);
1349 } else {
1350 list_move(&tbl->link, &rset->head_rt_tbl_list);
1351 clear_bit(tbl->idx,
1352 &ipa_ctx->rt_idx_bitmap[ip]);
1353 set->tbl_cnt--;
1354 IPADBG("rst sys rt tbl_idx=%d tbl_cnt=%d\n",
1355 tbl->idx, set->tbl_cnt);
1356 }
1357 /* remove the handle from the database */
1358 ipa_id_remove(id);
1359 }
1360 }
1361 mutex_unlock(&ipa_ctx->lock);
1362
1363 return 0;
1364}
1365
1366/**
1367 * ipa2_get_rt_tbl() - lookup the specified routing table and return handle if
1368 * it exists, if lookup succeeds the routing table ref cnt is increased
1369 * @lookup: [inout] routing table to lookup and its handle
1370 *
1371 * Returns: 0 on success, negative on failure
1372 *
1373 * Note: Should not be called from atomic context
1374 * Caller should call ipa_put_rt_tbl later if this function succeeds
1375 */
1376int ipa2_get_rt_tbl(struct ipa_ioc_get_rt_tbl *lookup)
1377{
1378 struct ipa_rt_tbl *entry;
1379 int result = -EFAULT;
1380
1381 if (lookup == NULL || lookup->ip >= IPA_IP_MAX) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301382 IPAERR_RL("bad parm\n");
Amir Levy9659e592016-10-27 18:08:27 +03001383 return -EINVAL;
1384 }
1385 mutex_lock(&ipa_ctx->lock);
Mohammed Javidcd665892017-10-11 17:05:57 +05301386 lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
Amir Levy9659e592016-10-27 18:08:27 +03001387 entry = __ipa_find_rt_tbl(lookup->ip, lookup->name);
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301388 if (entry && entry->cookie == IPA_RT_TBL_COOKIE) {
Utkarsh Saxena69e4ab0b2017-04-25 17:39:41 +05301389 if (entry->ref_cnt == U32_MAX) {
1390 IPAERR("fail: ref count crossed limit\n");
1391 goto ret;
1392 }
Amir Levy9659e592016-10-27 18:08:27 +03001393 entry->ref_cnt++;
1394 lookup->hdl = entry->id;
1395
1396 /* commit for get */
1397 if (ipa_ctx->ctrl->ipa_commit_rt(lookup->ip))
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301398 IPAERR_RL("fail to commit RT tbl\n");
Amir Levy9659e592016-10-27 18:08:27 +03001399
1400 result = 0;
1401 }
Utkarsh Saxena69e4ab0b2017-04-25 17:39:41 +05301402
1403ret:
Amir Levy9659e592016-10-27 18:08:27 +03001404 mutex_unlock(&ipa_ctx->lock);
1405
1406 return result;
1407}
1408
1409/**
1410 * ipa2_put_rt_tbl() - Release the specified routing table handle
1411 * @rt_tbl_hdl: [in] the routing table handle to release
1412 *
1413 * Returns: 0 on success, negative on failure
1414 *
1415 * Note: Should not be called from atomic context
1416 */
1417int ipa2_put_rt_tbl(u32 rt_tbl_hdl)
1418{
1419 struct ipa_rt_tbl *entry;
1420 enum ipa_ip_type ip = IPA_IP_MAX;
Mohammed Javid42ad67b2017-07-27 15:12:18 +05301421 int result = 0;
Amir Levy9659e592016-10-27 18:08:27 +03001422
1423 mutex_lock(&ipa_ctx->lock);
1424 entry = ipa_id_find(rt_tbl_hdl);
1425 if (entry == NULL) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301426 IPAERR_RL("lookup failed\n");
Amir Levy9659e592016-10-27 18:08:27 +03001427 result = -EINVAL;
1428 goto ret;
1429 }
1430
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301431 if ((entry->cookie != IPA_RT_TBL_COOKIE) || entry->ref_cnt == 0) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301432 IPAERR_RL("bad parms\n");
Amir Levy9659e592016-10-27 18:08:27 +03001433 result = -EINVAL;
1434 goto ret;
1435 }
1436
1437 if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v4])
1438 ip = IPA_IP_v4;
1439 else if (entry->set == &ipa_ctx->rt_tbl_set[IPA_IP_v6])
1440 ip = IPA_IP_v6;
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301441 else {
Amir Levy9659e592016-10-27 18:08:27 +03001442 WARN_ON(1);
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301443 result = -EINVAL;
1444 goto ret;
1445 }
Amir Levy9659e592016-10-27 18:08:27 +03001446
1447 entry->ref_cnt--;
1448 if (entry->ref_cnt == 0 && entry->rule_cnt == 0) {
1449 if (__ipa_del_rt_tbl(entry))
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301450 IPAERR_RL("fail to del RT tbl\n");
Amir Levy9659e592016-10-27 18:08:27 +03001451 /* commit for put */
1452 if (ipa_ctx->ctrl->ipa_commit_rt(ip))
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301453 IPAERR_RL("fail to commit RT tbl\n");
Amir Levy9659e592016-10-27 18:08:27 +03001454 }
1455
1456 result = 0;
1457
1458ret:
1459 mutex_unlock(&ipa_ctx->lock);
1460
1461 return result;
1462}
1463
1464
1465static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule)
1466{
1467 struct ipa_rt_entry *entry;
1468 struct ipa_hdr_entry *hdr = NULL;
1469
1470 if (rtrule->rule.hdr_hdl) {
1471 hdr = ipa_id_find(rtrule->rule.hdr_hdl);
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301472 if ((hdr == NULL) || (hdr->cookie != IPA_HDR_COOKIE)) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301473 IPAERR_RL("rt rule does not point to valid hdr\n");
Amir Levy9659e592016-10-27 18:08:27 +03001474 goto error;
1475 }
1476 }
1477
1478 entry = ipa_id_find(rtrule->rt_rule_hdl);
1479 if (entry == NULL) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301480 IPAERR_RL("lookup failed\n");
Amir Levy9659e592016-10-27 18:08:27 +03001481 goto error;
1482 }
1483
Mohammed Javid93e94ce2017-06-15 15:39:04 +05301484 if (entry->cookie != IPA_RT_RULE_COOKIE) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301485 IPAERR_RL("bad params\n");
Amir Levy9659e592016-10-27 18:08:27 +03001486 goto error;
1487 }
1488
1489 if (entry->hdr)
1490 entry->hdr->ref_cnt--;
1491
1492 entry->rule = rtrule->rule;
1493 entry->hdr = hdr;
1494
1495 if (entry->hdr)
1496 entry->hdr->ref_cnt++;
1497
1498 return 0;
1499
1500error:
1501 return -EPERM;
1502}
1503
1504/**
1505 * ipa2_mdfy_rt_rule() - Modify the specified routing rules in SW and optionally
1506 * commit to IPA HW
1507 *
1508 * Returns: 0 on success, negative on failure
1509 *
1510 * Note: Should not be called from atomic context
1511 */
1512int ipa2_mdfy_rt_rule(struct ipa_ioc_mdfy_rt_rule *hdls)
1513{
1514 int i;
1515 int result;
1516
1517 if (hdls == NULL || hdls->num_rules == 0 || hdls->ip >= IPA_IP_MAX) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301518 IPAERR_RL("bad parm\n");
Amir Levy9659e592016-10-27 18:08:27 +03001519 return -EINVAL;
1520 }
1521
1522 mutex_lock(&ipa_ctx->lock);
1523 for (i = 0; i < hdls->num_rules; i++) {
1524 if (__ipa_mdfy_rt_rule(&hdls->rules[i])) {
Utkarsh Saxenae9782812017-05-26 17:20:32 +05301525 IPAERR_RL("failed to mdfy rt rule %i\n", i);
Amir Levy9659e592016-10-27 18:08:27 +03001526 hdls->rules[i].status = IPA_RT_STATUS_OF_MDFY_FAILED;
1527 } else {
1528 hdls->rules[i].status = 0;
1529 }
1530 }
1531
1532 if (hdls->commit)
1533 if (ipa_ctx->ctrl->ipa_commit_rt(hdls->ip)) {
1534 result = -EPERM;
1535 goto bail;
1536 }
1537 result = 0;
1538bail:
1539 mutex_unlock(&ipa_ctx->lock);
1540
1541 return result;
1542}