123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 |
- /* Copyright (c) 2012-2017, 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"
- static const u32 ipa_hdr_bin_sz[IPA_HDR_BIN_MAX] = { 8, 16, 24, 36 };
- /**
- * ipa_generate_hdr_hw_tbl() - generates the headers table
- * @mem: [out] buffer to put the header table
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa_generate_hdr_hw_tbl(struct ipa_mem_buffer *mem)
- {
- struct ipa_hdr_entry *entry;
- mem->size = ipa_ctx->hdr_tbl.end;
- if (mem->size == 0) {
- IPAERR("hdr tbl empty\n");
- return -EPERM;
- }
- IPADBG("tbl_sz=%d\n", ipa_ctx->hdr_tbl.end);
- 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);
- return -ENOMEM;
- }
- memset(mem->base, 0, mem->size);
- list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list,
- link) {
- IPADBG("hdr of len %d ofst=%d\n", entry->hdr_len,
- entry->offset_entry->offset);
- memcpy(mem->base + entry->offset_entry->offset, entry->hdr,
- entry->hdr_len);
- }
- return 0;
- }
- /*
- * __ipa_commit_hdr() commits hdr to hardware
- * This function needs to be called with a locked mutex.
- */
- static int __ipa_commit_hdr(void)
- {
- struct ipa_desc desc = { 0 };
- struct ipa_mem_buffer *mem;
- struct ipa_hdr_init_local *cmd;
- u16 len;
- mem = kmalloc(sizeof(struct ipa_mem_buffer), GFP_KERNEL);
- if (!mem) {
- IPAERR("failed to alloc memory object\n");
- goto fail_alloc_mem;
- }
- /* the immediate command param size is same for both local and system */
- len = sizeof(struct ipa_hdr_init_local);
- /*
- * we can use init_local ptr for init_system due to layout of the
- * struct
- */
- cmd = kmalloc(len, GFP_KERNEL);
- if (!cmd) {
- IPAERR("failed to alloc immediate command object\n");
- goto fail_alloc_cmd;
- }
- if (ipa_generate_hdr_hw_tbl(mem)) {
- IPAERR("fail to generate HDR HW TBL\n");
- goto fail_hw_tbl_gen;
- }
- if (ipa_ctx->hdr_tbl_lcl && mem->size > IPA_RAM_HDR_SIZE) {
- IPAERR("tbl too big, needed %d avail %d\n", mem->size,
- IPA_RAM_HDR_SIZE);
- goto fail_send_cmd;
- }
- cmd->hdr_table_addr = mem->phys_base;
- if (ipa_ctx->hdr_tbl_lcl) {
- cmd->size_hdr_table = mem->size;
- cmd->hdr_addr = IPA_RAM_HDR_OFST;
- desc.opcode = IPA_HDR_INIT_LOCAL;
- } else {
- desc.opcode = IPA_HDR_INIT_SYSTEM;
- }
- desc.pyld = cmd;
- desc.len = sizeof(struct ipa_hdr_init_local);
- 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;
- }
- if (ipa_ctx->hdr_tbl_lcl) {
- dma_free_coherent(NULL, mem->size, mem->base, mem->phys_base);
- } else {
- if (ipa_ctx->hdr_mem.phys_base) {
- dma_free_coherent(NULL, ipa_ctx->hdr_mem.size,
- ipa_ctx->hdr_mem.base,
- ipa_ctx->hdr_mem.phys_base);
- }
- ipa_ctx->hdr_mem = *mem;
- }
- kfree(cmd);
- kfree(mem);
- return 0;
- fail_send_cmd:
- if (mem->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_hdr(struct ipa_hdr_add *hdr)
- {
- struct ipa_hdr_entry *entry;
- struct ipa_hdr_offset_entry *offset = NULL;
- struct ipa_tree_node *node;
- u32 bin;
- struct ipa_hdr_tbl *htbl = &ipa_ctx->hdr_tbl;
- if (hdr->hdr_len == 0) {
- IPAERR("bad parm\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->hdr_cache, GFP_KERNEL);
- if (!entry) {
- IPAERR("failed to alloc hdr object\n");
- goto hdr_alloc_fail;
- }
- INIT_LIST_HEAD(&entry->link);
- memcpy(entry->hdr, hdr->hdr, hdr->hdr_len);
- entry->hdr_len = hdr->hdr_len;
- strlcpy(entry->name, hdr->name, IPA_RESOURCE_NAME_MAX);
- entry->is_partial = hdr->is_partial;
- entry->cookie = IPA_HDR_COOKIE;
- if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN0])
- bin = IPA_HDR_BIN0;
- else if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN1])
- bin = IPA_HDR_BIN1;
- else if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN2])
- bin = IPA_HDR_BIN2;
- else if (hdr->hdr_len <= ipa_hdr_bin_sz[IPA_HDR_BIN3])
- bin = IPA_HDR_BIN3;
- else {
- IPAERR("unexpected hdr len %d\n", hdr->hdr_len);
- goto bad_hdr_len;
- }
- if (list_empty(&htbl->head_free_offset_list[bin])) {
- offset = kmem_cache_zalloc(ipa_ctx->hdr_offset_cache,
- GFP_KERNEL);
- if (!offset) {
- IPAERR("failed to alloc hdr offset object\n");
- goto ofst_alloc_fail;
- }
- INIT_LIST_HEAD(&offset->link);
- /*
- * for a first item grow, set the bin and offset which are set
- * in stone
- */
- offset->offset = htbl->end;
- offset->bin = bin;
- htbl->end += ipa_hdr_bin_sz[bin];
- list_add(&offset->link,
- &htbl->head_offset_list[bin]);
- } else {
- /* get the first free slot */
- offset =
- list_first_entry(&htbl->head_free_offset_list[bin],
- struct ipa_hdr_offset_entry, link);
- list_move(&offset->link, &htbl->head_offset_list[bin]);
- }
- entry->offset_entry = offset;
- list_add(&entry->link, &htbl->head_hdr_entry_list);
- htbl->hdr_cnt++;
- IPADBG("add hdr of sz=%d hdr_cnt=%d ofst=%d\n", hdr->hdr_len,
- htbl->hdr_cnt, offset->offset);
- hdr->hdr_hdl = (u32) entry;
- node->hdl = hdr->hdr_hdl;
- if (ipa_insert(&ipa_ctx->hdr_hdl_tree, node)) {
- IPAERR("failed to add to tree\n");
- WARN_ON(1);
- goto ipa_insert_failed;
- }
- entry->ref_cnt++;
- return 0;
- ofst_alloc_fail:
- kmem_cache_free(ipa_ctx->hdr_offset_cache, offset);
- ipa_insert_failed:
- if (offset)
- list_move(&offset->link,
- &htbl->head_free_offset_list[offset->bin]);
- entry->offset_entry = NULL;
- htbl->hdr_cnt--;
- list_del(&entry->link);
- bad_hdr_len:
- entry->cookie = 0;
- kmem_cache_free(ipa_ctx->hdr_cache, entry);
- hdr_alloc_fail:
- kmem_cache_free(ipa_ctx->tree_node_cache, node);
- error:
- return -EPERM;
- }
- int __ipa_del_hdr(u32 hdr_hdl, bool by_user)
- {
- struct ipa_hdr_entry *entry = (struct ipa_hdr_entry *)hdr_hdl;
- struct ipa_tree_node *node;
- struct ipa_hdr_tbl *htbl = &ipa_ctx->hdr_tbl;
- node = ipa_search(&ipa_ctx->hdr_hdl_tree, hdr_hdl);
- if (node == NULL) {
- IPAERR("lookup failed\n");
- return -EINVAL;
- }
- if (!entry || (entry->cookie != IPA_HDR_COOKIE)) {
- IPAERR("bad parm\n");
- return -EINVAL;
- }
- IPADBG("del hdr of sz=%d hdr_cnt=%d ofst=%d\n", entry->hdr_len,
- htbl->hdr_cnt, entry->offset_entry->offset);
- if (by_user && entry->user_deleted) {
- IPAERR("hdr already deleted by user\n");
- return -EINVAL;
- }
- if (by_user) {
- if (!strcmp(entry->name, IPA_LAN_RX_HDR_NAME)) {
- IPADBG("Trying to delete hdr %s offset=%u\n",
- entry->name, entry->offset_entry->offset);
- if (!entry->offset_entry->offset) {
- IPAERR("User cannot delete default header\n");
- return -EPERM;
- }
- }
- entry->user_deleted = true;
- }
- if (--entry->ref_cnt) {
- IPADBG("hdr_hdl %x ref_cnt %d\n", hdr_hdl, entry->ref_cnt);
- return 0;
- }
- /* move the offset entry to appropriate free list */
- list_move(&entry->offset_entry->link,
- &htbl->head_free_offset_list[entry->offset_entry->bin]);
- list_del(&entry->link);
- htbl->hdr_cnt--;
- entry->cookie = 0;
- kmem_cache_free(ipa_ctx->hdr_cache, entry);
- /* remove the handle from the database */
- rb_erase(&node->node, &ipa_ctx->hdr_hdl_tree);
- kmem_cache_free(ipa_ctx->tree_node_cache, node);
- return 0;
- }
- /**
- * ipa_add_hdr() - add the specified headers to SW and optionally commit them to
- * IPA HW
- * @hdrs: [inout] set of headers to add
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_add_hdr(struct ipa_ioc_add_hdr *hdrs)
- {
- int i;
- int result = -EFAULT;
- if (hdrs == NULL || hdrs->num_hdrs == 0) {
- IPAERR("bad parm\n");
- return -EINVAL;
- }
- mutex_lock(&ipa_ctx->lock);
- for (i = 0; i < hdrs->num_hdrs; i++) {
- if (__ipa_add_hdr(&hdrs->hdr[i])) {
- IPAERR("failed to add hdr %d\n", i);
- hdrs->hdr[i].status = -1;
- } else {
- hdrs->hdr[i].status = 0;
- }
- }
- if (hdrs->commit) {
- if (__ipa_commit_hdr()) {
- result = -EPERM;
- goto bail;
- }
- }
- result = 0;
- bail:
- mutex_unlock(&ipa_ctx->lock);
- return result;
- }
- EXPORT_SYMBOL(ipa_add_hdr);
- /**
- * ipa_del_hdr_by_user() - Remove the specified headers
- * from SW and optionally commit them to IPA HW
- * @hdls: [inout] set of headers to delete
- * @by_user: Operation requested by user?
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_del_hdr_by_user(struct ipa_ioc_del_hdr *hdls, bool by_user)
- {
- int i;
- int result = -EFAULT;
- if (hdls == NULL || hdls->num_hdls == 0) {
- IPAERR("bad parm\n");
- return -EINVAL;
- }
- mutex_lock(&ipa_ctx->lock);
- for (i = 0; i < hdls->num_hdls; i++) {
- if (__ipa_del_hdr(hdls->hdl[i].hdl, by_user)) {
- IPAERR("failed to del hdr %i\n", i);
- hdls->hdl[i].status = -1;
- } else {
- hdls->hdl[i].status = 0;
- }
- }
- if (hdls->commit) {
- if (__ipa_commit_hdr()) {
- result = -EPERM;
- goto bail;
- }
- }
- result = 0;
- bail:
- mutex_unlock(&ipa_ctx->lock);
- return result;
- }
- /**
- * ipa_del_hdr() - Remove the specified headers from SW and optionally commit them
- * to IPA HW
- * @hdls: [inout] set of headers to delete
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_del_hdr(struct ipa_ioc_del_hdr *hdls)
- {
- return ipa_del_hdr_by_user(hdls, false);
- }
- EXPORT_SYMBOL(ipa_del_hdr);
- /**
- * ipa_dump_hdr() - prints all the headers in the header table in SW
- *
- * Note: Should not be called from atomic context
- */
- void ipa_dump_hdr(void)
- {
- struct ipa_hdr_entry *entry;
- IPADBG("START\n");
- mutex_lock(&ipa_ctx->lock);
- list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list,
- link) {
- IPADBG("hdr_len=%4d off=%4d bin=%4d\n", entry->hdr_len,
- entry->offset_entry->offset,
- entry->offset_entry->bin);
- }
- mutex_unlock(&ipa_ctx->lock);
- IPADBG("END\n");
- }
- /**
- * ipa_commit_hdr() - commit to IPA HW the current header table in SW
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_commit_hdr(void)
- {
- int result = -EFAULT;
- /*
- * issue a commit on the routing module since routing rules point to
- * header table entries
- */
- if (ipa_commit_rt(IPA_IP_v4))
- return -EPERM;
- if (ipa_commit_rt(IPA_IP_v6))
- return -EPERM;
- mutex_lock(&ipa_ctx->lock);
- if (__ipa_commit_hdr()) {
- result = -EPERM;
- goto bail;
- }
- result = 0;
- bail:
- mutex_unlock(&ipa_ctx->lock);
- return result;
- }
- EXPORT_SYMBOL(ipa_commit_hdr);
- /**
- * ipa_reset_hdr() - reset the current header table in SW (does not commit to
- * HW)
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_reset_hdr(void)
- {
- struct ipa_hdr_entry *entry;
- struct ipa_hdr_entry *next;
- struct ipa_hdr_offset_entry *off_entry;
- struct ipa_hdr_offset_entry *off_next;
- struct ipa_tree_node *node;
- int i;
- /*
- * issue a reset on the routing module since routing rules point to
- * header table entries
- */
- if (ipa_reset_rt(IPA_IP_v4))
- IPAERR("fail to reset v4 rt\n");
- if (ipa_reset_rt(IPA_IP_v6))
- IPAERR("fail to reset v4 rt\n");
- mutex_lock(&ipa_ctx->lock);
- IPADBG("reset hdr\n");
- list_for_each_entry_safe(entry, next,
- &ipa_ctx->hdr_tbl.head_hdr_entry_list, link) {
- /* do not remove the default exception header */
- if (!strncmp(entry->name, IPA_DFLT_HDR_NAME,
- IPA_RESOURCE_NAME_MAX)) {
- IPADBG("Trying to remove hdr %s offset=%u\n",
- entry->name, entry->offset_entry->offset);
- if (!entry->offset_entry->offset) {
- if (entry->is_hdr_proc_ctx) {
- mutex_unlock(&ipa_ctx->lock);
- WARN_ON(1);
- return -EFAULT;
- }
- IPADBG("skip default header\n");
- continue;
- }
- }
- node = ipa_search(&ipa_ctx->hdr_hdl_tree, (u32) entry);
- if (node == NULL) {
- WARN_ON(1);
- mutex_unlock(&ipa_ctx->lock);
- return -EFAULT;
- }
- list_del(&entry->link);
- entry->cookie = 0;
- kmem_cache_free(ipa_ctx->hdr_cache, entry);
- /* remove the handle from the database */
- rb_erase(&node->node, &ipa_ctx->hdr_hdl_tree);
- kmem_cache_free(ipa_ctx->tree_node_cache, node);
- }
- for (i = 0; i < IPA_HDR_BIN_MAX; i++) {
- list_for_each_entry_safe(off_entry, off_next,
- &ipa_ctx->hdr_tbl.head_offset_list[i],
- link) {
- /*
- * do not remove the default exception header which is
- * at offset 0
- */
- if (off_entry->offset == 0)
- continue;
- list_del(&off_entry->link);
- kmem_cache_free(ipa_ctx->hdr_offset_cache, off_entry);
- }
- list_for_each_entry_safe(off_entry, off_next,
- &ipa_ctx->hdr_tbl.head_free_offset_list[i],
- link) {
- list_del(&off_entry->link);
- kmem_cache_free(ipa_ctx->hdr_offset_cache, off_entry);
- }
- }
- /* there is one header of size 8 */
- ipa_ctx->hdr_tbl.end = 8;
- ipa_ctx->hdr_tbl.hdr_cnt = 1;
- mutex_unlock(&ipa_ctx->lock);
- return 0;
- }
- EXPORT_SYMBOL(ipa_reset_hdr);
- static struct ipa_hdr_entry *__ipa_find_hdr(const char *name)
- {
- struct ipa_hdr_entry *entry;
- list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list,
- link) {
- if (!strncmp(name, entry->name, IPA_RESOURCE_NAME_MAX))
- return entry;
- }
- return NULL;
- }
- /**
- * ipa_get_hdr() - Lookup the specified header resource
- * @lookup: [inout] header to lookup and its handle
- *
- * lookup the specified header resource and return handle if it exists
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- * Caller should call ipa_put_hdr later if this function succeeds
- */
- int ipa_get_hdr(struct ipa_ioc_get_hdr *lookup)
- {
- struct ipa_hdr_entry *entry;
- int result = -1;
- if (lookup == NULL) {
- IPAERR("bad parm\n");
- return -EINVAL;
- }
- mutex_lock(&ipa_ctx->lock);
- entry = __ipa_find_hdr(lookup->name);
- if (entry) {
- lookup->hdl = (uint32_t) entry;
- result = 0;
- }
- mutex_unlock(&ipa_ctx->lock);
- return result;
- }
- EXPORT_SYMBOL(ipa_get_hdr);
- /**
- * __ipa_release_hdr() - drop reference to header and cause
- * deletion if reference count permits
- * @hdr_hdl: [in] handle of header to be released
- *
- * Returns: 0 on success, negative on failure
- */
- int __ipa_release_hdr(u32 hdr_hdl)
- {
- int result = 0;
- if (__ipa_del_hdr(hdr_hdl, false)) {
- IPADBG("fail to del hdr %x\n", hdr_hdl);
- result = -EFAULT;
- goto bail;
- }
- /* commit for put */
- if (__ipa_commit_hdr()) {
- IPAERR("fail to commit hdr\n");
- result = -EFAULT;
- goto bail;
- }
- bail:
- return result;
- }
- /**
- * ipa_put_hdr() - Release the specified header handle
- * @hdr_hdl: [in] the header handle to release
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_put_hdr(u32 hdr_hdl)
- {
- struct ipa_hdr_entry *entry = (struct ipa_hdr_entry *)hdr_hdl;
- struct ipa_tree_node *node;
- int result = -EFAULT;
- mutex_lock(&ipa_ctx->lock);
- node = ipa_search(&ipa_ctx->hdr_hdl_tree, hdr_hdl);
- if (node == NULL) {
- IPAERR("lookup failed\n");
- result = -EINVAL;
- goto bail;
- }
- if (entry == NULL || entry->cookie != IPA_HDR_COOKIE) {
- IPAERR("bad params\n");
- result = -EINVAL;
- goto bail;
- }
- result = 0;
- bail:
- mutex_unlock(&ipa_ctx->lock);
- return result;
- }
- EXPORT_SYMBOL(ipa_put_hdr);
- /**
- * ipa_copy_hdr() - Lookup the specified header resource and return a copy of it
- * @copy: [inout] header to lookup and its copy
- *
- * lookup the specified header resource and return a copy of it (along with its
- * attributes) if it exists, this would be called for partial headers
- *
- * Returns: 0 on success, negative on failure
- *
- * Note: Should not be called from atomic context
- */
- int ipa_copy_hdr(struct ipa_ioc_copy_hdr *copy)
- {
- struct ipa_hdr_entry *entry;
- int result = -EFAULT;
- if (copy == NULL) {
- IPAERR("bad parm\n");
- return -EINVAL;
- }
- mutex_lock(&ipa_ctx->lock);
- entry = __ipa_find_hdr(copy->name);
- if (entry) {
- memcpy(copy->hdr, entry->hdr, entry->hdr_len);
- copy->hdr_len = entry->hdr_len;
- copy->is_partial = entry->is_partial;
- result = 0;
- }
- mutex_unlock(&ipa_ctx->lock);
- return result;
- }
- EXPORT_SYMBOL(ipa_copy_hdr);
|