123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881 |
- /* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include "ipa_i.h"
- #define IPA_FLT_TABLE_WORD_SIZE (4)
- #define IPA_FLT_ENTRY_MEMORY_ALLIGNMENT (0x3)
- #define IPA_FLT_BIT_MASK (0x1)
- #define IPA_FLT_TABLE_INDEX_NOT_FOUND (-1)
- #define IPA_FLT_STATUS_OF_ADD_FAILED (-1)
- #define IPA_FLT_STATUS_OF_DEL_FAILED (-1)
- /**
- * ipa_generate_flt_hw_rule() - generates the filtering hardware rule
- * @ip: the ip address family type
- * @entry: routing entry
- * @buf: output buffer, buf == NULL means
- * caller wants to know the size of the rule as seen
- * by HW so they did not pass a valid buffer, we will use a
- * scratch buffer instead.
- * With this scheme we are going to
- * generate the rule twice, once to know size using scratch
- * buffer and second to write the rule to the actual caller
- * supplied buffer which is of required size
- *
- * Returns: 0 on success, negative on failure
- *
- * caller needs to hold any needed locks to ensure integrity
- *
- */
- static int ipa_generate_flt_hw_rule(enum ipa_ip_type ip,
- struct ipa_flt_entry *entry, u8 *buf)
- {
- struct ipa_flt_rule_hw_hdr *hdr;
- const struct ipa_flt_rule *rule =
- (const struct ipa_flt_rule *)&entry->rule;
- u16 en_rule = 0;
- u32 tmp[IPA_RT_FLT_HW_RULE_BUF_SIZE/4];
- u8 *start;
- if (buf == NULL) {
- memset(tmp, 0, IPA_RT_FLT_HW_RULE_BUF_SIZE);
- buf = (u8 *)tmp;
- }
- start = buf;
- hdr = (struct ipa_flt_rule_hw_hdr *)buf;
- hdr->u.hdr.action = entry->rule.action;
- if (entry->rt_tbl)
- hdr->u.hdr.rt_tbl_idx = entry->rt_tbl->idx;
- else
- /* for excp action flt rules, rt tbl index is meaningless */
- hdr->u.hdr.rt_tbl_idx = 0;
- hdr->u.hdr.rsvd = 0;
- buf += sizeof(struct ipa_flt_rule_hw_hdr);
- if (ipa_generate_hw_rule(ip, &rule->attrib, &buf, &en_rule)) {
- IPAERR("fail to generate hw rule\n");
- return -EPERM;
- }
- IPADBG("en_rule %x\n", en_rule);
- hdr->u.hdr.en_rule = en_rule;
- ipa_write_32(hdr->u.word, (u8 *)hdr);
- if (entry->hw_len == 0) {
- entry->hw_len = buf - start;
- } else if (entry->hw_len != (buf - start)) {
- IPAERR("hw_len differs b/w passes passed=%x calc=%x\n",
- entry->hw_len, (buf - start));
- return -EPERM;
- }
- return 0;
- }
- /**
- * ipa_get_flt_hw_tbl_size() - returns the size of HW filtering table
- * @ip: the ip address family type
- * @hdr_sz: header size
- *
- * Returns: size on success, negative on failure
- *
- * caller needs to hold any needed locks to ensure integrity
- *
- */
- static int ipa_get_flt_hw_tbl_size(enum ipa_ip_type ip, u32 *hdr_sz)
- {
- struct ipa_flt_tbl *tbl;
- struct ipa_flt_entry *entry;
- u32 total_sz = 0;
- u32 rule_set_sz;
- int i;
- *hdr_sz = 0;
- tbl = &ipa_ctx->glob_flt_tbl[ip];
- rule_set_sz = 0;
- list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
- if (ipa_generate_flt_hw_rule(ip, entry, NULL)) {
- IPAERR("failed to find HW FLT rule size\n");
- return -EPERM;
- }
- IPADBG("glob ip %d len %d\n", ip, entry->hw_len);
- rule_set_sz += entry->hw_len;
- }
- if (rule_set_sz) {
- tbl->sz = rule_set_sz + IPA_FLT_TABLE_WORD_SIZE;
- /* this rule-set uses a word in header block */
- *hdr_sz += IPA_FLT_TABLE_WORD_SIZE;
- if (!tbl->in_sys) {
- /* add the terminator */
- total_sz += (rule_set_sz + IPA_FLT_TABLE_WORD_SIZE);
- total_sz = (total_sz +
- IPA_FLT_ENTRY_MEMORY_ALLIGNMENT) &
- ~IPA_FLT_ENTRY_MEMORY_ALLIGNMENT;
- }
- }
- for (i = 0; i < IPA_NUM_PIPES; i++) {
- tbl = &ipa_ctx->flt_tbl[i][ip];
- rule_set_sz = 0;
- list_for_each_entry(entry, &tbl->head_flt_rule_list, link) {
- if (ipa_generate_flt_hw_rule(ip, entry, NULL)) {
- IPAERR("failed to find HW FLT rule size\n");
- return -EPERM;
- }
- IPADBG("pipe %d len %d\n", i, entry->hw_len);
- rule_set_sz += entry->hw_len;
- }
- if (rule_set_sz) {
- tbl->sz = rule_set_sz + IPA_FLT_TABLE_WORD_SIZE;
- /* this rule-set uses a word in header block */
- *hdr_sz += IPA_FLT_TABLE_WORD_SIZE;
- if (!tbl->in_sys) {
- /* add the terminator */
- total_sz += (rule_set_sz +
- IPA_FLT_TABLE_WORD_SIZE);
- total_sz = (total_sz +
- IPA_FLT_ENTRY_MEMORY_ALLIGNMENT) &
- ~IPA_FLT_ENTRY_MEMORY_ALLIGNMENT;
- }
- }
- }
- *hdr_sz += IPA_FLT_TABLE_WORD_SIZE;
- total_sz += *hdr_sz;
- IPADBG("FLT HW TBL SZ %d HDR SZ %d IP %d\n", total_sz, *hdr_sz, ip);
- return total_sz;
- }
- /**
- * ipa_generate_flt_hw_tbl() - generates the filtering hardware table
- * @ip: [in] the ip address family type
- * @mem: [out] buffer to put the filtering table
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa_generate_flt_hw_tbl(enum ipa_ip_type ip, struct ipa_mem_buffer *mem)
- {
- struct ipa_flt_tbl *tbl;
- struct ipa_flt_entry *entry;
- u32 hdr_top = 0;
- int i;
- u32 hdr_sz;
- u32 offset;
- u8 *hdr;
- u8 *body;
- u8 *base;
- int res;
- struct ipa_mem_buffer flt_tbl_mem;
- u8 *ftbl_membody;
- res = ipa_get_flt_hw_tbl_size(ip, &hdr_sz);
- if (res < 0) {
- IPAERR("ipa_get_flt_hw_tbl_size failed %d\n", res);
- return res;
- }
- mem->size = res;
- mem->size = IPA_HW_TABLE_ALIGNMENT(mem->size);
- if (mem->size == 0) {
- IPAERR("flt tbl empty ip=%d\n", ip);
- goto error;
- }
- mem->base = dma_alloc_coherent(NULL, mem->size, &mem->phys_base,
- GFP_KERNEL);
- if (!mem->base) {
- IPAERR("fail to alloc DMA buff of size %d\n", mem->size);
- goto error;
- }
- memset(mem->base, 0, mem->size);
- /* build the flt tbl in the DMA buffer to submit to IPA HW */
- base = hdr = (u8 *)mem->base;
- body = base + hdr_sz;
- /* write a dummy header to move cursor */
- hdr = ipa_write_32(hdr_top, hdr);
- tbl = &ipa_ctx->glob_flt_tbl[ip];
- if (!list_empty(&tbl->head_flt_rule_list)) {
- hdr_top |= IPA_FLT_BIT_MASK;
- if (!tbl->in_sys) {
- offset = body - base;
- if (offset & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT) {
- IPAERR("offset is not word multiple %d\n",
- offset);
- goto proc_err;
- }
- offset &= ~IPA_FLT_ENTRY_MEMORY_ALLIGNMENT;
- /* rule is at an offset from base */
- offset |= IPA_FLT_BIT_MASK;
- hdr = ipa_write_32(offset, hdr);
- /* generate the rule-set */
- list_for_each_entry(entry, &tbl->head_flt_rule_list,
- link) {
- if (ipa_generate_flt_hw_rule(ip, entry, body)) {
- IPAERR("failed to gen HW FLT rule\n");
- goto proc_err;
- }
- body += entry->hw_len;
- }
- /* write the rule-set terminator */
- body = ipa_write_32(0, body);
- if ((u32)body & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT)
- /* advance body to next word boundary */
- body = body + (IPA_FLT_TABLE_WORD_SIZE -
- ((u32)body &
- IPA_FLT_ENTRY_MEMORY_ALLIGNMENT));
- } else {
- if (tbl->sz == 0) {
- IPAERR("tbl size is 0\n");
- WARN_ON(1);
- goto proc_err;
- }
- /* allocate memory for the flt tbl */
- flt_tbl_mem.size = tbl->sz;
- flt_tbl_mem.base =
- dma_alloc_coherent(NULL, flt_tbl_mem.size,
- &flt_tbl_mem.phys_base, GFP_KERNEL);
- if (!flt_tbl_mem.base) {
- IPAERR("fail to alloc DMA buff of size %d\n",
- flt_tbl_mem.size);
- WARN_ON(1);
- goto proc_err;
- }
- WARN_ON(flt_tbl_mem.phys_base &
- IPA_FLT_ENTRY_MEMORY_ALLIGNMENT);
- ftbl_membody = flt_tbl_mem.base;
- memset(flt_tbl_mem.base, 0, flt_tbl_mem.size);
- hdr = ipa_write_32(flt_tbl_mem.phys_base, hdr);
- /* generate the rule-set */
- list_for_each_entry(entry, &tbl->head_flt_rule_list,
- link) {
- if (ipa_generate_flt_hw_rule(ip, entry,
- ftbl_membody)) {
- IPAERR("failed to gen HW FLT rule\n");
- WARN_ON(1);
- }
- ftbl_membody += entry->hw_len;
- }
- /* write the rule-set terminator */
- ftbl_membody = ipa_write_32(0, ftbl_membody);
- if (tbl->curr_mem.phys_base) {
- WARN_ON(tbl->prev_mem.phys_base);
- tbl->prev_mem = tbl->curr_mem;
- }
- tbl->curr_mem = flt_tbl_mem;
- }
- }
- for (i = 0; i < IPA_NUM_PIPES; i++) {
- tbl = &ipa_ctx->flt_tbl[i][ip];
- if (!list_empty(&tbl->head_flt_rule_list)) {
- /* pipe "i" is at bit "i+1" */
- hdr_top |= (1 << (i + 1));
- if (!tbl->in_sys) {
- offset = body - base;
- if (offset & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT) {
- IPAERR("ofst is not word multiple %d\n",
- offset);
- goto proc_err;
- }
- offset &= ~IPA_FLT_ENTRY_MEMORY_ALLIGNMENT;
- /* rule is at an offset from base */
- offset |= IPA_FLT_BIT_MASK;
- hdr = ipa_write_32(offset, hdr);
- /* generate the rule-set */
- list_for_each_entry(entry,
- &tbl->head_flt_rule_list,
- link) {
- if (ipa_generate_flt_hw_rule(ip, entry,
- body)) {
- IPAERR("fail gen FLT rule\n");
- goto proc_err;
- }
- body += entry->hw_len;
- }
- /* write the rule-set terminator */
- body = ipa_write_32(0, body);
- if ((u32)body & IPA_FLT_ENTRY_MEMORY_ALLIGNMENT)
- /* advance body to next word boundary */
- body = body + (IPA_FLT_TABLE_WORD_SIZE -
- ((u32)body &
- IPA_FLT_ENTRY_MEMORY_ALLIGNMENT));
- } else {
- if (tbl->sz == 0) {
- IPAERR("tbl size is 0\n");
- WARN_ON(1);
- goto proc_err;
- }
- /* allocate memory for the flt tbl */
- flt_tbl_mem.size = tbl->sz;
- flt_tbl_mem.base =
- dma_alloc_coherent(NULL, flt_tbl_mem.size,
- &flt_tbl_mem.phys_base,
- GFP_KERNEL);
- if (!flt_tbl_mem.base) {
- IPAERR("fail alloc DMA buff size %d\n",
- flt_tbl_mem.size);
- WARN_ON(1);
- goto proc_err;
- }
- WARN_ON(flt_tbl_mem.phys_base &
- IPA_FLT_ENTRY_MEMORY_ALLIGNMENT);
- ftbl_membody = flt_tbl_mem.base;
- memset(flt_tbl_mem.base, 0, flt_tbl_mem.size);
- hdr = ipa_write_32(flt_tbl_mem.phys_base, hdr);
- /* generate the rule-set */
- list_for_each_entry(entry,
- &tbl->head_flt_rule_list,
- link) {
- if (ipa_generate_flt_hw_rule(ip, entry,
- ftbl_membody)) {
- IPAERR("fail gen FLT rule\n");
- WARN_ON(1);
- }
- ftbl_membody += entry->hw_len;
- }
- /* write the rule-set terminator */
- ftbl_membody =
- ipa_write_32(0, ftbl_membody);
- if (tbl->curr_mem.phys_base) {
- WARN_ON(tbl->prev_mem.phys_base);
- tbl->prev_mem = tbl->curr_mem;
- }
- tbl->curr_mem = flt_tbl_mem;
- }
- }
- }
- /* now write the hdr_top */
- ipa_write_32(hdr_top, base);
- return 0;
- proc_err:
- dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
- mem->base = NULL;
- error:
- return -EPERM;
- }
- static void __ipa_reap_sys_flt_tbls(enum ipa_ip_type ip)
- {
- struct ipa_flt_tbl *tbl;
- int i;
- tbl = &ipa_ctx->glob_flt_tbl[ip];
- if (tbl->prev_mem.phys_base) {
- IPADBG("reaping glob flt tbl (prev) ip=%d\n", ip);
- dma_free_coherent(NULL, tbl->prev_mem.size, tbl->prev_mem.base,
- tbl->prev_mem.phys_base);
- memset(&tbl->prev_mem, 0, sizeof(tbl->prev_mem));
- }
- if (list_empty(&tbl->head_flt_rule_list)) {
- if (tbl->curr_mem.phys_base) {
- IPADBG("reaping glob flt tbl (curr) ip=%d\n", ip);
- dma_free_coherent(NULL, tbl->curr_mem.size,
- tbl->curr_mem.base,
- tbl->curr_mem.phys_base);
- memset(&tbl->curr_mem, 0, sizeof(tbl->curr_mem));
- }
- }
- for (i = 0; i < IPA_NUM_PIPES; i++) {
- tbl = &ipa_ctx->flt_tbl[i][ip];
- if (tbl->prev_mem.phys_base) {
- IPADBG("reaping flt tbl (prev) pipe=%d ip=%d\n", i, ip);
- dma_free_coherent(NULL, tbl->prev_mem.size,
- tbl->prev_mem.base,
- tbl->prev_mem.phys_base);
- memset(&tbl->prev_mem, 0, sizeof(tbl->prev_mem));
- }
- if (list_empty(&tbl->head_flt_rule_list)) {
- if (tbl->curr_mem.phys_base) {
- IPADBG("reaping flt tbl (curr) pipe=%d ip=%d\n",
- i, ip);
- dma_free_coherent(NULL, tbl->curr_mem.size,
- tbl->curr_mem.base,
- tbl->curr_mem.phys_base);
- memset(&tbl->curr_mem, 0,
- sizeof(tbl->curr_mem));
- }
- }
- }
- }
- static int __ipa_commit_flt(enum ipa_ip_type ip)
- {
- struct ipa_desc desc = { 0 };
- struct ipa_mem_buffer *mem;
- void *cmd;
- struct ipa_ip_v4_filter_init *v4;
- struct ipa_ip_v6_filter_init *v6;
- u16 avail;
- u16 size;
- mem = kmalloc(sizeof(struct ipa_mem_buffer), GFP_KERNEL);
- if (!mem) {
- IPAERR("failed to alloc memory object\n");
- goto fail_alloc_mem;
- }
- if (ip == IPA_IP_v4) {
- avail = IPA_RAM_V4_FLT_SIZE;
- size = sizeof(struct ipa_ip_v4_filter_init);
- } else {
- avail = IPA_RAM_V6_FLT_SIZE;
- size = sizeof(struct ipa_ip_v6_filter_init);
- }
- cmd = kmalloc(size, GFP_KERNEL);
- if (!cmd) {
- IPAERR("failed to alloc immediate command object\n");
- goto fail_alloc_cmd;
- }
- if (ipa_generate_flt_hw_tbl(ip, mem)) {
- IPAERR("fail to generate FLT HW TBL ip %d\n", ip);
- goto fail_hw_tbl_gen;
- }
- if (mem->size > avail) {
- IPAERR("tbl too big, needed %d avail %d\n", mem->size, avail);
- goto fail_send_cmd;
- }
- if (ip == IPA_IP_v4) {
- v4 = (struct ipa_ip_v4_filter_init *)cmd;
- desc.opcode = IPA_IP_V4_FILTER_INIT;
- v4->ipv4_rules_addr = mem->phys_base;
- v4->size_ipv4_rules = mem->size;
- v4->ipv4_addr = IPA_RAM_V4_FLT_OFST;
- } else {
- v6 = (struct ipa_ip_v6_filter_init *)cmd;
- desc.opcode = IPA_IP_V6_FILTER_INIT;
- v6->ipv6_rules_addr = mem->phys_base;
- v6->size_ipv6_rules = mem->size;
- v6->ipv6_addr = IPA_RAM_V6_FLT_OFST;
- }
- desc.pyld = cmd;
- desc.len = size;
- desc.type = IPA_IMM_CMD_DESC;
- IPA_DUMP_BUFF(mem->base, mem->phys_base, mem->size);
- if (ipa_send_cmd(1, &desc)) {
- IPAERR("fail to send immediate command\n");
- goto fail_send_cmd;
- }
- __ipa_reap_sys_flt_tbls(ip);
- dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
- kfree(cmd);
- kfree(mem);
- return 0;
- fail_send_cmd:
- if (mem->phys_base)
- dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
- fail_hw_tbl_gen:
- kfree(cmd);
- fail_alloc_cmd:
- kfree(mem);
- fail_alloc_mem:
- return -EPERM;
- }
- static int __ipa_add_flt_rule(struct ipa_flt_tbl *tbl, enum ipa_ip_type ip,
- const struct ipa_flt_rule *rule, u8 add_rear,
- u32 *rule_hdl)
- {
- struct ipa_flt_entry *entry;
- struct ipa_tree_node *node;
- if (rule->action != IPA_PASS_TO_EXCEPTION) {
- if (!rule->rt_tbl_hdl) {
- IPAERR("flt rule does not point to valid RT tbl\n");
- goto error;
- }
- if (ipa_search(&ipa_ctx->rt_tbl_hdl_tree,
- rule->rt_tbl_hdl) == NULL) {
- IPAERR("RT tbl not found\n");
- goto error;
- }
- if (((struct ipa_rt_tbl *)rule->rt_tbl_hdl)->cookie !=
- IPA_RT_TBL_COOKIE) {
- IPAERR("RT table cookie is invalid\n");
- goto error;
- }
- }
- node = kmem_cache_zalloc(ipa_ctx->tree_node_cache, GFP_KERNEL);
- if (!node) {
- IPAERR("failed to alloc tree node object\n");
- goto error;
- }
- entry = kmem_cache_zalloc(ipa_ctx->flt_rule_cache, GFP_KERNEL);
- if (!entry) {
- IPAERR("failed to alloc FLT rule object\n");
- goto error;
- }
- INIT_LIST_HEAD(&entry->link);
- entry->rule = *rule;
- entry->cookie = IPA_FLT_COOKIE;
- entry->rt_tbl = (struct ipa_rt_tbl *)rule->rt_tbl_hdl;
- entry->tbl = tbl;
- if (add_rear)
- list_add_tail(&entry->link, &tbl->head_flt_rule_list);
- else
- list_add(&entry->link, &tbl->head_flt_rule_list);
- tbl->rule_cnt++;
- if (entry->rt_tbl)
- entry->rt_tbl->ref_cnt++;
- *rule_hdl = (u32)entry;
- IPADBG("add flt rule rule_cnt=%d\n", tbl->rule_cnt);
- node->hdl = *rule_hdl;
- if (ipa_insert(&ipa_ctx->flt_rule_hdl_tree, node)) {
- IPAERR("failed to add to tree\n");
- WARN_ON(1);
- goto ipa_insert_failed;
- }
- return 0;
- ipa_insert_failed:
- tbl->rule_cnt--;
- if (entry->rt_tbl)
- entry->rt_tbl->ref_cnt--;
- list_del(&entry->link);
- kmem_cache_free(ipa_ctx->flt_rule_cache, entry);
- error:
- return -EPERM;
- }
- static int __ipa_del_flt_rule(u32 rule_hdl)
- {
- struct ipa_flt_entry *entry = (struct ipa_flt_entry *)rule_hdl;
- struct ipa_tree_node *node;
- node = ipa_search(&ipa_ctx->flt_rule_hdl_tree, rule_hdl);
- if (node == NULL) {
- IPAERR("lookup failed\n");
- return -EINVAL;
- }
- if (entry == NULL || (entry->cookie != IPA_FLT_COOKIE)) {
- IPAERR("bad params\n");
- return -EINVAL;
- }
- list_del(&entry->link);
- entry->tbl->rule_cnt--;
- if (entry->rt_tbl)
- entry->rt_tbl->ref_cnt--;
- IPADBG("del flt rule rule_cnt=%d\n", entry->tbl->rule_cnt);
- entry->cookie = 0;
- kmem_cache_free(ipa_ctx->flt_rule_cache, entry);
- /* remove the handle from the database */
- rb_erase(&node->node, &ipa_ctx->flt_rule_hdl_tree);
- kmem_cache_free(ipa_ctx->tree_node_cache, node);
- return 0;
- }
- static int __ipa_add_global_flt_rule(enum ipa_ip_type ip,
- const struct ipa_flt_rule *rule, u8 add_rear, u32 *rule_hdl)
- {
- struct ipa_flt_tbl *tbl;
- if (rule == NULL || rule_hdl == NULL) {
- IPAERR("bad parms rule=%p rule_hdl=%p\n", rule, rule_hdl);
- return -EINVAL;
- }
- tbl = &ipa_ctx->glob_flt_tbl[ip];
- IPADBG("add global flt rule ip=%d\n", ip);
- return __ipa_add_flt_rule(tbl, ip, rule, add_rear, rule_hdl);
- }
- static int __ipa_add_ep_flt_rule(enum ipa_ip_type ip, enum ipa_client_type ep,
- const struct ipa_flt_rule *rule, u8 add_rear,
- u32 *rule_hdl)
- {
- struct ipa_flt_tbl *tbl;
- int ipa_ep_idx;
- if (rule == NULL || rule_hdl == NULL || ep >= IPA_CLIENT_MAX) {
- IPAERR("bad parms rule=%p rule_hdl=%p ep=%d\n", rule,
- rule_hdl, ep);
- return -EINVAL;
- }
- ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode, ep);
- if (ipa_ep_idx == IPA_FLT_TABLE_INDEX_NOT_FOUND) {
- IPAERR("ep not valid ep=%d\n", ep);
- return -EINVAL;
- }
- if (ipa_ctx->ep[ipa_ep_idx].valid == 0)
- IPADBG("ep not connected ep_idx=%d\n", ipa_ep_idx);
- tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][ip];
- IPADBG("add ep flt rule ip=%d ep=%d\n", ip, ep);
- return __ipa_add_flt_rule(tbl, ip, rule, add_rear, rule_hdl);
- }
- /**
- * ipa_add_flt_rule() - Add the specified filtering rules to SW and optionally
- * commit to IPA HW
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_add_flt_rule(struct ipa_ioc_add_flt_rule *rules)
- {
- int i;
- int result;
- if (rules == NULL || rules->num_rules == 0 ||
- rules->ip >= IPA_IP_MAX) {
- IPAERR("bad parm\n");
- return -EINVAL;
- }
- mutex_lock(&ipa_ctx->lock);
- for (i = 0; i < rules->num_rules; i++) {
- if (rules->global)
- result = __ipa_add_global_flt_rule(rules->ip,
- &rules->rules[i].rule,
- rules->rules[i].at_rear,
- &rules->rules[i].flt_rule_hdl);
- else
- result = __ipa_add_ep_flt_rule(rules->ip, rules->ep,
- &rules->rules[i].rule,
- rules->rules[i].at_rear,
- &rules->rules[i].flt_rule_hdl);
- if (result) {
- IPAERR("failed to add flt rule %d\n", i);
- rules->rules[i].status = IPA_FLT_STATUS_OF_ADD_FAILED;
- } else {
- rules->rules[i].status = 0;
- }
- }
- if (rules->commit)
- if (__ipa_commit_flt(rules->ip)) {
- result = -EPERM;
- goto bail;
- }
- result = 0;
- bail:
- mutex_unlock(&ipa_ctx->lock);
- return result;
- }
- EXPORT_SYMBOL(ipa_add_flt_rule);
- /**
- * ipa_del_flt_rule() - Remove the specified filtering rules from SW and
- * optionally commit to IPA HW
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_del_flt_rule(struct ipa_ioc_del_flt_rule *hdls)
- {
- int i;
- int result;
- if (hdls == NULL || hdls->num_hdls == 0 || hdls->ip >= IPA_IP_MAX) {
- IPAERR("bad parm\n");
- return -EINVAL;
- }
- mutex_lock(&ipa_ctx->lock);
- for (i = 0; i < hdls->num_hdls; i++) {
- if (__ipa_del_flt_rule(hdls->hdl[i].hdl)) {
- IPAERR("failed to del rt rule %i\n", i);
- hdls->hdl[i].status = IPA_FLT_STATUS_OF_DEL_FAILED;
- } else {
- hdls->hdl[i].status = 0;
- }
- }
- if (hdls->commit)
- if (__ipa_commit_flt(hdls->ip)) {
- mutex_unlock(&ipa_ctx->lock);
- result = -EPERM;
- goto bail;
- }
- result = 0;
- bail:
- mutex_unlock(&ipa_ctx->lock);
- return result;
- }
- EXPORT_SYMBOL(ipa_del_flt_rule);
- /**
- * ipa_commit_flt() - Commit the current SW filtering table of specified type to
- * IPA HW
- * @ip: [in] the family of routing tables
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_commit_flt(enum ipa_ip_type ip)
- {
- int result;
- if (ip >= IPA_IP_MAX) {
- IPAERR("bad parm\n");
- return -EINVAL;
- }
- mutex_lock(&ipa_ctx->lock);
- if (__ipa_commit_flt(ip)) {
- result = -EPERM;
- goto bail;
- }
- result = 0;
- bail:
- mutex_unlock(&ipa_ctx->lock);
- return result;
- }
- EXPORT_SYMBOL(ipa_commit_flt);
- /**
- * ipa_reset_flt() - Reset the current SW filtering table of specified type
- * (does not commit to HW)
- * @ip: [in] the family of routing tables
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_reset_flt(enum ipa_ip_type ip)
- {
- struct ipa_flt_tbl *tbl;
- struct ipa_flt_entry *entry;
- struct ipa_flt_entry *next;
- struct ipa_tree_node *node;
- int i;
- if (ip >= IPA_IP_MAX) {
- IPAERR("bad parm\n");
- return -EINVAL;
- }
- tbl = &ipa_ctx->glob_flt_tbl[ip];
- mutex_lock(&ipa_ctx->lock);
- IPADBG("reset flt ip=%d\n", ip);
- list_for_each_entry_safe(entry, next, &tbl->head_flt_rule_list, link) {
- node = ipa_search(&ipa_ctx->flt_rule_hdl_tree, (u32)entry);
- if (node == NULL) {
- WARN_ON(1);
- mutex_unlock(&ipa_ctx->lock);
- return -EFAULT;
- }
- if ((ip == IPA_IP_v4 &&
- entry->rule.attrib.attrib_mask == IPA_FLT_PROTOCOL &&
- entry->rule.attrib.u.v4.protocol ==
- IPA_INVALID_L4_PROTOCOL) ||
- (ip == IPA_IP_v6 &&
- entry->rule.attrib.attrib_mask == IPA_FLT_NEXT_HDR &&
- entry->rule.attrib.u.v6.next_hdr ==
- IPA_INVALID_L4_PROTOCOL))
- continue;
- list_del(&entry->link);
- entry->tbl->rule_cnt--;
- if (entry->rt_tbl)
- entry->rt_tbl->ref_cnt--;
- entry->cookie = 0;
- kmem_cache_free(ipa_ctx->flt_rule_cache, entry);
- /* remove the handle from the database */
- rb_erase(&node->node, &ipa_ctx->flt_rule_hdl_tree);
- kmem_cache_free(ipa_ctx->tree_node_cache, node);
- }
- for (i = 0; i < IPA_NUM_PIPES; i++) {
- tbl = &ipa_ctx->flt_tbl[i][ip];
- list_for_each_entry_safe(entry, next, &tbl->head_flt_rule_list,
- link) {
- node = ipa_search(&ipa_ctx->flt_rule_hdl_tree,
- (u32)entry);
- if (node == NULL) {
- WARN_ON(1);
- mutex_unlock(&ipa_ctx->lock);
- return -EFAULT;
- }
- list_del(&entry->link);
- entry->tbl->rule_cnt--;
- if (entry->rt_tbl)
- entry->rt_tbl->ref_cnt--;
- entry->cookie = 0;
- kmem_cache_free(ipa_ctx->flt_rule_cache, entry);
- /* remove the handle from the database */
- rb_erase(&node->node, &ipa_ctx->flt_rule_hdl_tree);
- kmem_cache_free(ipa_ctx->tree_node_cache, node);
- }
- }
- mutex_unlock(&ipa_ctx->lock);
- return 0;
- }
- EXPORT_SYMBOL(ipa_reset_flt);
|