1d8931847SRahul Lakkireddy /* 2d8931847SRahul Lakkireddy * This file is part of the Chelsio T4 Ethernet driver for Linux. 3d8931847SRahul Lakkireddy * 4d8931847SRahul Lakkireddy * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved. 5d8931847SRahul Lakkireddy * 6d8931847SRahul Lakkireddy * This software is available to you under a choice of one of two 7d8931847SRahul Lakkireddy * licenses. You may choose to be licensed under the terms of the GNU 8d8931847SRahul Lakkireddy * General Public License (GPL) Version 2, available from the file 9d8931847SRahul Lakkireddy * COPYING in the main directory of this source tree, or the 10d8931847SRahul Lakkireddy * OpenIB.org BSD license below: 11d8931847SRahul Lakkireddy * 12d8931847SRahul Lakkireddy * Redistribution and use in source and binary forms, with or 13d8931847SRahul Lakkireddy * without modification, are permitted provided that the following 14d8931847SRahul Lakkireddy * conditions are met: 15d8931847SRahul Lakkireddy * 16d8931847SRahul Lakkireddy * - Redistributions of source code must retain the above 17d8931847SRahul Lakkireddy * copyright notice, this list of conditions and the following 18d8931847SRahul Lakkireddy * disclaimer. 19d8931847SRahul Lakkireddy * 20d8931847SRahul Lakkireddy * - Redistributions in binary form must reproduce the above 21d8931847SRahul Lakkireddy * copyright notice, this list of conditions and the following 22d8931847SRahul Lakkireddy * disclaimer in the documentation and/or other materials 23d8931847SRahul Lakkireddy * provided with the distribution. 24d8931847SRahul Lakkireddy * 25d8931847SRahul Lakkireddy * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26d8931847SRahul Lakkireddy * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27d8931847SRahul Lakkireddy * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28d8931847SRahul Lakkireddy * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 29d8931847SRahul Lakkireddy * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 30d8931847SRahul Lakkireddy * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 31d8931847SRahul Lakkireddy * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32d8931847SRahul Lakkireddy * SOFTWARE. 33d8931847SRahul Lakkireddy */ 34d8931847SRahul Lakkireddy 35b20ff726SRahul Lakkireddy #include <net/tc_act/tc_gact.h> 36b20ff726SRahul Lakkireddy #include <net/tc_act/tc_mirred.h> 37b20ff726SRahul Lakkireddy 38d8931847SRahul Lakkireddy #include "cxgb4.h" 39d8931847SRahul Lakkireddy #include "cxgb4_tc_u32_parse.h" 40d8931847SRahul Lakkireddy #include "cxgb4_tc_u32.h" 41d8931847SRahul Lakkireddy 42d8931847SRahul Lakkireddy /* Fill ch_filter_specification with parsed match value/mask pair. */ 43d8931847SRahul Lakkireddy static int fill_match_fields(struct adapter *adap, 44d8931847SRahul Lakkireddy struct ch_filter_specification *fs, 45d8931847SRahul Lakkireddy struct tc_cls_u32_offload *cls, 46d8931847SRahul Lakkireddy const struct cxgb4_match_field *entry, 47d8931847SRahul Lakkireddy bool next_header) 48d8931847SRahul Lakkireddy { 49d8931847SRahul Lakkireddy unsigned int i, j; 50d8931847SRahul Lakkireddy u32 val, mask; 51d8931847SRahul Lakkireddy int off, err; 52d8931847SRahul Lakkireddy bool found; 53d8931847SRahul Lakkireddy 54d8931847SRahul Lakkireddy for (i = 0; i < cls->knode.sel->nkeys; i++) { 55d8931847SRahul Lakkireddy off = cls->knode.sel->keys[i].off; 56d8931847SRahul Lakkireddy val = cls->knode.sel->keys[i].val; 57d8931847SRahul Lakkireddy mask = cls->knode.sel->keys[i].mask; 58d8931847SRahul Lakkireddy 59d8931847SRahul Lakkireddy if (next_header) { 60d8931847SRahul Lakkireddy /* For next headers, parse only keys with offmask */ 61d8931847SRahul Lakkireddy if (!cls->knode.sel->keys[i].offmask) 62d8931847SRahul Lakkireddy continue; 63d8931847SRahul Lakkireddy } else { 64d8931847SRahul Lakkireddy /* For the remaining, parse only keys without offmask */ 65d8931847SRahul Lakkireddy if (cls->knode.sel->keys[i].offmask) 66d8931847SRahul Lakkireddy continue; 67d8931847SRahul Lakkireddy } 68d8931847SRahul Lakkireddy 69d8931847SRahul Lakkireddy found = false; 70d8931847SRahul Lakkireddy 71d8931847SRahul Lakkireddy for (j = 0; entry[j].val; j++) { 72d8931847SRahul Lakkireddy if (off == entry[j].off) { 73d8931847SRahul Lakkireddy found = true; 74d8931847SRahul Lakkireddy err = entry[j].val(fs, val, mask); 75d8931847SRahul Lakkireddy if (err) 76d8931847SRahul Lakkireddy return err; 77d8931847SRahul Lakkireddy break; 78d8931847SRahul Lakkireddy } 79d8931847SRahul Lakkireddy } 80d8931847SRahul Lakkireddy 81d8931847SRahul Lakkireddy if (!found) 82d8931847SRahul Lakkireddy return -EINVAL; 83d8931847SRahul Lakkireddy } 84d8931847SRahul Lakkireddy 85d8931847SRahul Lakkireddy return 0; 86d8931847SRahul Lakkireddy } 87d8931847SRahul Lakkireddy 88b20ff726SRahul Lakkireddy /* Fill ch_filter_specification with parsed action. */ 89b20ff726SRahul Lakkireddy static int fill_action_fields(struct adapter *adap, 90b20ff726SRahul Lakkireddy struct ch_filter_specification *fs, 91b20ff726SRahul Lakkireddy struct tc_cls_u32_offload *cls) 92b20ff726SRahul Lakkireddy { 93b20ff726SRahul Lakkireddy unsigned int num_actions = 0; 94b20ff726SRahul Lakkireddy const struct tc_action *a; 95b20ff726SRahul Lakkireddy struct tcf_exts *exts; 96b20ff726SRahul Lakkireddy LIST_HEAD(actions); 97b20ff726SRahul Lakkireddy 98b20ff726SRahul Lakkireddy exts = cls->knode.exts; 99b20ff726SRahul Lakkireddy if (tc_no_actions(exts)) 100b20ff726SRahul Lakkireddy return -EINVAL; 101b20ff726SRahul Lakkireddy 102b20ff726SRahul Lakkireddy tcf_exts_to_list(exts, &actions); 103b20ff726SRahul Lakkireddy list_for_each_entry(a, &actions, list) { 104b20ff726SRahul Lakkireddy /* Don't allow more than one action per rule. */ 105b20ff726SRahul Lakkireddy if (num_actions) 106b20ff726SRahul Lakkireddy return -EINVAL; 107b20ff726SRahul Lakkireddy 108b20ff726SRahul Lakkireddy /* Drop in hardware. */ 109b20ff726SRahul Lakkireddy if (is_tcf_gact_shot(a)) { 110b20ff726SRahul Lakkireddy fs->action = FILTER_DROP; 111b20ff726SRahul Lakkireddy num_actions++; 112b20ff726SRahul Lakkireddy continue; 113b20ff726SRahul Lakkireddy } 114b20ff726SRahul Lakkireddy 115b20ff726SRahul Lakkireddy /* Re-direct to specified port in hardware. */ 1165724b8b5SShmulik Ladkani if (is_tcf_mirred_egress_redirect(a)) { 117b20ff726SRahul Lakkireddy struct net_device *n_dev; 118b20ff726SRahul Lakkireddy unsigned int i, index; 119b20ff726SRahul Lakkireddy bool found = false; 120b20ff726SRahul Lakkireddy 121b20ff726SRahul Lakkireddy index = tcf_mirred_ifindex(a); 122b20ff726SRahul Lakkireddy for_each_port(adap, i) { 123b20ff726SRahul Lakkireddy n_dev = adap->port[i]; 124b20ff726SRahul Lakkireddy if (index == n_dev->ifindex) { 125b20ff726SRahul Lakkireddy fs->action = FILTER_SWITCH; 126b20ff726SRahul Lakkireddy fs->eport = i; 127b20ff726SRahul Lakkireddy found = true; 128b20ff726SRahul Lakkireddy break; 129b20ff726SRahul Lakkireddy } 130b20ff726SRahul Lakkireddy } 131b20ff726SRahul Lakkireddy 132b20ff726SRahul Lakkireddy /* Interface doesn't belong to any port of 133b20ff726SRahul Lakkireddy * the underlying hardware. 134b20ff726SRahul Lakkireddy */ 135b20ff726SRahul Lakkireddy if (!found) 136b20ff726SRahul Lakkireddy return -EINVAL; 137b20ff726SRahul Lakkireddy 138b20ff726SRahul Lakkireddy num_actions++; 139b20ff726SRahul Lakkireddy continue; 140b20ff726SRahul Lakkireddy } 141b20ff726SRahul Lakkireddy 142b20ff726SRahul Lakkireddy /* Un-supported action. */ 143b20ff726SRahul Lakkireddy return -EINVAL; 144b20ff726SRahul Lakkireddy } 145b20ff726SRahul Lakkireddy 146b20ff726SRahul Lakkireddy return 0; 147b20ff726SRahul Lakkireddy } 148b20ff726SRahul Lakkireddy 149d8931847SRahul Lakkireddy int cxgb4_config_knode(struct net_device *dev, __be16 protocol, 150d8931847SRahul Lakkireddy struct tc_cls_u32_offload *cls) 151d8931847SRahul Lakkireddy { 152d8931847SRahul Lakkireddy const struct cxgb4_match_field *start, *link_start = NULL; 153d8931847SRahul Lakkireddy struct adapter *adapter = netdev2adap(dev); 154d8931847SRahul Lakkireddy struct ch_filter_specification fs; 155d8931847SRahul Lakkireddy struct cxgb4_tc_u32_table *t; 156d8931847SRahul Lakkireddy struct cxgb4_link *link; 157d8931847SRahul Lakkireddy unsigned int filter_id; 158d8931847SRahul Lakkireddy u32 uhtid, link_uhtid; 159d8931847SRahul Lakkireddy bool is_ipv6 = false; 160d8931847SRahul Lakkireddy int ret; 161d8931847SRahul Lakkireddy 162d8931847SRahul Lakkireddy if (!can_tc_u32_offload(dev)) 163d8931847SRahul Lakkireddy return -EOPNOTSUPP; 164d8931847SRahul Lakkireddy 165d8931847SRahul Lakkireddy if (protocol != htons(ETH_P_IP) && protocol != htons(ETH_P_IPV6)) 166d8931847SRahul Lakkireddy return -EOPNOTSUPP; 167d8931847SRahul Lakkireddy 168d8931847SRahul Lakkireddy /* Fetch the location to insert the filter. */ 169d8931847SRahul Lakkireddy filter_id = cls->knode.handle & 0xFFFFF; 170d8931847SRahul Lakkireddy 171d8931847SRahul Lakkireddy if (filter_id > adapter->tids.nftids) { 172d8931847SRahul Lakkireddy dev_err(adapter->pdev_dev, 173d8931847SRahul Lakkireddy "Location %d out of range for insertion. Max: %d\n", 174d8931847SRahul Lakkireddy filter_id, adapter->tids.nftids); 175d8931847SRahul Lakkireddy return -ERANGE; 176d8931847SRahul Lakkireddy } 177d8931847SRahul Lakkireddy 178d8931847SRahul Lakkireddy t = adapter->tc_u32; 179d8931847SRahul Lakkireddy uhtid = TC_U32_USERHTID(cls->knode.handle); 180d8931847SRahul Lakkireddy link_uhtid = TC_U32_USERHTID(cls->knode.link_handle); 181d8931847SRahul Lakkireddy 182d8931847SRahul Lakkireddy /* Ensure that uhtid is either root u32 (i.e. 0x800) 183d8931847SRahul Lakkireddy * or a a valid linked bucket. 184d8931847SRahul Lakkireddy */ 185d8931847SRahul Lakkireddy if (uhtid != 0x800 && uhtid >= t->size) 186d8931847SRahul Lakkireddy return -EINVAL; 187d8931847SRahul Lakkireddy 188d8931847SRahul Lakkireddy /* Ensure link handle uhtid is sane, if specified. */ 189d8931847SRahul Lakkireddy if (link_uhtid >= t->size) 190d8931847SRahul Lakkireddy return -EINVAL; 191d8931847SRahul Lakkireddy 192d8931847SRahul Lakkireddy memset(&fs, 0, sizeof(fs)); 193d8931847SRahul Lakkireddy 194d8931847SRahul Lakkireddy if (protocol == htons(ETH_P_IPV6)) { 195d8931847SRahul Lakkireddy start = cxgb4_ipv6_fields; 196d8931847SRahul Lakkireddy is_ipv6 = true; 197d8931847SRahul Lakkireddy } else { 198d8931847SRahul Lakkireddy start = cxgb4_ipv4_fields; 199d8931847SRahul Lakkireddy is_ipv6 = false; 200d8931847SRahul Lakkireddy } 201d8931847SRahul Lakkireddy 202d8931847SRahul Lakkireddy if (uhtid != 0x800) { 203d8931847SRahul Lakkireddy /* Link must exist from root node before insertion. */ 204d8931847SRahul Lakkireddy if (!t->table[uhtid - 1].link_handle) 205d8931847SRahul Lakkireddy return -EINVAL; 206d8931847SRahul Lakkireddy 207d8931847SRahul Lakkireddy /* Link must have a valid supported next header. */ 208d8931847SRahul Lakkireddy link_start = t->table[uhtid - 1].match_field; 209d8931847SRahul Lakkireddy if (!link_start) 210d8931847SRahul Lakkireddy return -EINVAL; 211d8931847SRahul Lakkireddy } 212d8931847SRahul Lakkireddy 213d8931847SRahul Lakkireddy /* Parse links and record them for subsequent jumps to valid 214d8931847SRahul Lakkireddy * next headers. 215d8931847SRahul Lakkireddy */ 216d8931847SRahul Lakkireddy if (link_uhtid) { 217d8931847SRahul Lakkireddy const struct cxgb4_next_header *next; 218d8931847SRahul Lakkireddy bool found = false; 219d8931847SRahul Lakkireddy unsigned int i, j; 220d8931847SRahul Lakkireddy u32 val, mask; 221d8931847SRahul Lakkireddy int off; 222d8931847SRahul Lakkireddy 223d8931847SRahul Lakkireddy if (t->table[link_uhtid - 1].link_handle) { 224d8931847SRahul Lakkireddy dev_err(adapter->pdev_dev, 225d8931847SRahul Lakkireddy "Link handle exists for: 0x%x\n", 226d8931847SRahul Lakkireddy link_uhtid); 227d8931847SRahul Lakkireddy return -EINVAL; 228d8931847SRahul Lakkireddy } 229d8931847SRahul Lakkireddy 230d8931847SRahul Lakkireddy next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps; 231d8931847SRahul Lakkireddy 232d8931847SRahul Lakkireddy /* Try to find matches that allow jumps to next header. */ 233d8931847SRahul Lakkireddy for (i = 0; next[i].jump; i++) { 234d8931847SRahul Lakkireddy if (next[i].offoff != cls->knode.sel->offoff || 235d8931847SRahul Lakkireddy next[i].shift != cls->knode.sel->offshift || 236d8931847SRahul Lakkireddy next[i].mask != cls->knode.sel->offmask || 237d8931847SRahul Lakkireddy next[i].offset != cls->knode.sel->off) 238d8931847SRahul Lakkireddy continue; 239d8931847SRahul Lakkireddy 240d8931847SRahul Lakkireddy /* Found a possible candidate. Find a key that 241d8931847SRahul Lakkireddy * matches the corresponding offset, value, and 242d8931847SRahul Lakkireddy * mask to jump to next header. 243d8931847SRahul Lakkireddy */ 244d8931847SRahul Lakkireddy for (j = 0; j < cls->knode.sel->nkeys; j++) { 245d8931847SRahul Lakkireddy off = cls->knode.sel->keys[j].off; 246d8931847SRahul Lakkireddy val = cls->knode.sel->keys[j].val; 247d8931847SRahul Lakkireddy mask = cls->knode.sel->keys[j].mask; 248d8931847SRahul Lakkireddy 249d8931847SRahul Lakkireddy if (next[i].match_off == off && 250d8931847SRahul Lakkireddy next[i].match_val == val && 251d8931847SRahul Lakkireddy next[i].match_mask == mask) { 252d8931847SRahul Lakkireddy found = true; 253d8931847SRahul Lakkireddy break; 254d8931847SRahul Lakkireddy } 255d8931847SRahul Lakkireddy } 256d8931847SRahul Lakkireddy 257d8931847SRahul Lakkireddy if (!found) 258d8931847SRahul Lakkireddy continue; /* Try next candidate. */ 259d8931847SRahul Lakkireddy 260d8931847SRahul Lakkireddy /* Candidate to jump to next header found. 261d8931847SRahul Lakkireddy * Translate all keys to internal specification 262d8931847SRahul Lakkireddy * and store them in jump table. This spec is copied 263d8931847SRahul Lakkireddy * later to set the actual filters. 264d8931847SRahul Lakkireddy */ 265d8931847SRahul Lakkireddy ret = fill_match_fields(adapter, &fs, cls, 266d8931847SRahul Lakkireddy start, false); 267d8931847SRahul Lakkireddy if (ret) 268d8931847SRahul Lakkireddy goto out; 269d8931847SRahul Lakkireddy 270d8931847SRahul Lakkireddy link = &t->table[link_uhtid - 1]; 271d8931847SRahul Lakkireddy link->match_field = next[i].jump; 272d8931847SRahul Lakkireddy link->link_handle = cls->knode.handle; 273d8931847SRahul Lakkireddy memcpy(&link->fs, &fs, sizeof(fs)); 274d8931847SRahul Lakkireddy break; 275d8931847SRahul Lakkireddy } 276d8931847SRahul Lakkireddy 277d8931847SRahul Lakkireddy /* No candidate found to jump to next header. */ 278d8931847SRahul Lakkireddy if (!found) 279d8931847SRahul Lakkireddy return -EINVAL; 280d8931847SRahul Lakkireddy 281d8931847SRahul Lakkireddy return 0; 282d8931847SRahul Lakkireddy } 283d8931847SRahul Lakkireddy 284d8931847SRahul Lakkireddy /* Fill ch_filter_specification match fields to be shipped to hardware. 285d8931847SRahul Lakkireddy * Copy the linked spec (if any) first. And then update the spec as 286d8931847SRahul Lakkireddy * needed. 287d8931847SRahul Lakkireddy */ 288d8931847SRahul Lakkireddy if (uhtid != 0x800 && t->table[uhtid - 1].link_handle) { 289d8931847SRahul Lakkireddy /* Copy linked ch_filter_specification */ 290d8931847SRahul Lakkireddy memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs)); 291d8931847SRahul Lakkireddy ret = fill_match_fields(adapter, &fs, cls, 292d8931847SRahul Lakkireddy link_start, true); 293d8931847SRahul Lakkireddy if (ret) 294d8931847SRahul Lakkireddy goto out; 295d8931847SRahul Lakkireddy } 296d8931847SRahul Lakkireddy 297d8931847SRahul Lakkireddy ret = fill_match_fields(adapter, &fs, cls, start, false); 298d8931847SRahul Lakkireddy if (ret) 299d8931847SRahul Lakkireddy goto out; 300d8931847SRahul Lakkireddy 301b20ff726SRahul Lakkireddy /* Fill ch_filter_specification action fields to be shipped to 302b20ff726SRahul Lakkireddy * hardware. 303b20ff726SRahul Lakkireddy */ 304b20ff726SRahul Lakkireddy ret = fill_action_fields(adapter, &fs, cls); 305b20ff726SRahul Lakkireddy if (ret) 306b20ff726SRahul Lakkireddy goto out; 307b20ff726SRahul Lakkireddy 308d8931847SRahul Lakkireddy /* The filter spec has been completely built from the info 309d8931847SRahul Lakkireddy * provided from u32. We now set some default fields in the 310d8931847SRahul Lakkireddy * spec for sanity. 311d8931847SRahul Lakkireddy */ 312d8931847SRahul Lakkireddy 313d8931847SRahul Lakkireddy /* Match only packets coming from the ingress port where this 314d8931847SRahul Lakkireddy * filter will be created. 315d8931847SRahul Lakkireddy */ 316d8931847SRahul Lakkireddy fs.val.iport = netdev2pinfo(dev)->port_id; 317d8931847SRahul Lakkireddy fs.mask.iport = ~0; 318d8931847SRahul Lakkireddy 319d8931847SRahul Lakkireddy /* Enable filter hit counts. */ 320d8931847SRahul Lakkireddy fs.hitcnts = 1; 321d8931847SRahul Lakkireddy 322d8931847SRahul Lakkireddy /* Set type of filter - IPv6 or IPv4 */ 323d8931847SRahul Lakkireddy fs.type = is_ipv6 ? 1 : 0; 324d8931847SRahul Lakkireddy 325d8931847SRahul Lakkireddy /* Set the filter */ 326d8931847SRahul Lakkireddy ret = cxgb4_set_filter(dev, filter_id, &fs); 327d8931847SRahul Lakkireddy if (ret) 328d8931847SRahul Lakkireddy goto out; 329d8931847SRahul Lakkireddy 330d8931847SRahul Lakkireddy /* If this is a linked bucket, then set the corresponding 331d8931847SRahul Lakkireddy * entry in the bitmap to mark it as belonging to this linked 332d8931847SRahul Lakkireddy * bucket. 333d8931847SRahul Lakkireddy */ 334d8931847SRahul Lakkireddy if (uhtid != 0x800 && t->table[uhtid - 1].link_handle) 335d8931847SRahul Lakkireddy set_bit(filter_id, t->table[uhtid - 1].tid_map); 336d8931847SRahul Lakkireddy 337d8931847SRahul Lakkireddy out: 338d8931847SRahul Lakkireddy return ret; 339d8931847SRahul Lakkireddy } 340d8931847SRahul Lakkireddy 341d8931847SRahul Lakkireddy int cxgb4_delete_knode(struct net_device *dev, __be16 protocol, 342d8931847SRahul Lakkireddy struct tc_cls_u32_offload *cls) 343d8931847SRahul Lakkireddy { 344d8931847SRahul Lakkireddy struct adapter *adapter = netdev2adap(dev); 345d8931847SRahul Lakkireddy unsigned int filter_id, max_tids, i, j; 346d8931847SRahul Lakkireddy struct cxgb4_link *link = NULL; 347d8931847SRahul Lakkireddy struct cxgb4_tc_u32_table *t; 348d8931847SRahul Lakkireddy u32 handle, uhtid; 349d8931847SRahul Lakkireddy int ret; 350d8931847SRahul Lakkireddy 351d8931847SRahul Lakkireddy if (!can_tc_u32_offload(dev)) 352d8931847SRahul Lakkireddy return -EOPNOTSUPP; 353d8931847SRahul Lakkireddy 354d8931847SRahul Lakkireddy /* Fetch the location to delete the filter. */ 355d8931847SRahul Lakkireddy filter_id = cls->knode.handle & 0xFFFFF; 356d8931847SRahul Lakkireddy 357d8931847SRahul Lakkireddy if (filter_id > adapter->tids.nftids) { 358d8931847SRahul Lakkireddy dev_err(adapter->pdev_dev, 359d8931847SRahul Lakkireddy "Location %d out of range for deletion. Max: %d\n", 360d8931847SRahul Lakkireddy filter_id, adapter->tids.nftids); 361d8931847SRahul Lakkireddy return -ERANGE; 362d8931847SRahul Lakkireddy } 363d8931847SRahul Lakkireddy 364d8931847SRahul Lakkireddy t = adapter->tc_u32; 365d8931847SRahul Lakkireddy handle = cls->knode.handle; 366d8931847SRahul Lakkireddy uhtid = TC_U32_USERHTID(cls->knode.handle); 367d8931847SRahul Lakkireddy 368d8931847SRahul Lakkireddy /* Ensure that uhtid is either root u32 (i.e. 0x800) 369d8931847SRahul Lakkireddy * or a a valid linked bucket. 370d8931847SRahul Lakkireddy */ 371d8931847SRahul Lakkireddy if (uhtid != 0x800 && uhtid >= t->size) 372d8931847SRahul Lakkireddy return -EINVAL; 373d8931847SRahul Lakkireddy 374d8931847SRahul Lakkireddy /* Delete the specified filter */ 375d8931847SRahul Lakkireddy if (uhtid != 0x800) { 376d8931847SRahul Lakkireddy link = &t->table[uhtid - 1]; 377d8931847SRahul Lakkireddy if (!link->link_handle) 378d8931847SRahul Lakkireddy return -EINVAL; 379d8931847SRahul Lakkireddy 380d8931847SRahul Lakkireddy if (!test_bit(filter_id, link->tid_map)) 381d8931847SRahul Lakkireddy return -EINVAL; 382d8931847SRahul Lakkireddy } 383d8931847SRahul Lakkireddy 384d8931847SRahul Lakkireddy ret = cxgb4_del_filter(dev, filter_id); 385d8931847SRahul Lakkireddy if (ret) 386d8931847SRahul Lakkireddy goto out; 387d8931847SRahul Lakkireddy 388d8931847SRahul Lakkireddy if (link) 389d8931847SRahul Lakkireddy clear_bit(filter_id, link->tid_map); 390d8931847SRahul Lakkireddy 391d8931847SRahul Lakkireddy /* If a link is being deleted, then delete all filters 392d8931847SRahul Lakkireddy * associated with the link. 393d8931847SRahul Lakkireddy */ 394d8931847SRahul Lakkireddy max_tids = adapter->tids.nftids; 395d8931847SRahul Lakkireddy for (i = 0; i < t->size; i++) { 396d8931847SRahul Lakkireddy link = &t->table[i]; 397d8931847SRahul Lakkireddy 398d8931847SRahul Lakkireddy if (link->link_handle == handle) { 399d8931847SRahul Lakkireddy for (j = 0; j < max_tids; j++) { 400d8931847SRahul Lakkireddy if (!test_bit(j, link->tid_map)) 401d8931847SRahul Lakkireddy continue; 402d8931847SRahul Lakkireddy 403d8931847SRahul Lakkireddy ret = __cxgb4_del_filter(dev, j, NULL); 404d8931847SRahul Lakkireddy if (ret) 405d8931847SRahul Lakkireddy goto out; 406d8931847SRahul Lakkireddy 407d8931847SRahul Lakkireddy clear_bit(j, link->tid_map); 408d8931847SRahul Lakkireddy } 409d8931847SRahul Lakkireddy 410d8931847SRahul Lakkireddy /* Clear the link state */ 411d8931847SRahul Lakkireddy link->match_field = NULL; 412d8931847SRahul Lakkireddy link->link_handle = 0; 413d8931847SRahul Lakkireddy memset(&link->fs, 0, sizeof(link->fs)); 414d8931847SRahul Lakkireddy break; 415d8931847SRahul Lakkireddy } 416d8931847SRahul Lakkireddy } 417d8931847SRahul Lakkireddy 418d8931847SRahul Lakkireddy out: 419d8931847SRahul Lakkireddy return ret; 420d8931847SRahul Lakkireddy } 421d8931847SRahul Lakkireddy 422d8931847SRahul Lakkireddy void cxgb4_cleanup_tc_u32(struct adapter *adap) 423d8931847SRahul Lakkireddy { 424d8931847SRahul Lakkireddy struct cxgb4_tc_u32_table *t; 425d8931847SRahul Lakkireddy unsigned int i; 426d8931847SRahul Lakkireddy 427d8931847SRahul Lakkireddy if (!adap->tc_u32) 428d8931847SRahul Lakkireddy return; 429d8931847SRahul Lakkireddy 430d8931847SRahul Lakkireddy /* Free up all allocated memory. */ 431d8931847SRahul Lakkireddy t = adap->tc_u32; 432d8931847SRahul Lakkireddy for (i = 0; i < t->size; i++) { 433d8931847SRahul Lakkireddy struct cxgb4_link *link = &t->table[i]; 434d8931847SRahul Lakkireddy 435d8931847SRahul Lakkireddy t4_free_mem(link->tid_map); 436d8931847SRahul Lakkireddy } 437d8931847SRahul Lakkireddy t4_free_mem(adap->tc_u32); 438d8931847SRahul Lakkireddy } 439d8931847SRahul Lakkireddy 44045da1ca2SArjun V struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap) 441d8931847SRahul Lakkireddy { 44245da1ca2SArjun V unsigned int max_tids = adap->tids.nftids; 443d8931847SRahul Lakkireddy struct cxgb4_tc_u32_table *t; 444d8931847SRahul Lakkireddy unsigned int i; 445d8931847SRahul Lakkireddy 44645da1ca2SArjun V if (!max_tids) 447d8931847SRahul Lakkireddy return NULL; 448d8931847SRahul Lakkireddy 449d8931847SRahul Lakkireddy t = t4_alloc_mem(sizeof(*t) + 45045da1ca2SArjun V (max_tids * sizeof(struct cxgb4_link))); 451d8931847SRahul Lakkireddy if (!t) 452d8931847SRahul Lakkireddy return NULL; 453d8931847SRahul Lakkireddy 45445da1ca2SArjun V t->size = max_tids; 455d8931847SRahul Lakkireddy 456d8931847SRahul Lakkireddy for (i = 0; i < t->size; i++) { 457d8931847SRahul Lakkireddy struct cxgb4_link *link = &t->table[i]; 458d8931847SRahul Lakkireddy unsigned int bmap_size; 459d8931847SRahul Lakkireddy 460d8931847SRahul Lakkireddy bmap_size = BITS_TO_LONGS(max_tids); 461d8931847SRahul Lakkireddy link->tid_map = t4_alloc_mem(sizeof(unsigned long) * bmap_size); 462d8931847SRahul Lakkireddy if (!link->tid_map) 463d8931847SRahul Lakkireddy goto out_no_mem; 464d8931847SRahul Lakkireddy bitmap_zero(link->tid_map, max_tids); 465d8931847SRahul Lakkireddy } 466d8931847SRahul Lakkireddy 467d8931847SRahul Lakkireddy return t; 468d8931847SRahul Lakkireddy 469d8931847SRahul Lakkireddy out_no_mem: 470d8931847SRahul Lakkireddy for (i = 0; i < t->size; i++) { 471d8931847SRahul Lakkireddy struct cxgb4_link *link = &t->table[i]; 472d8931847SRahul Lakkireddy 473d8931847SRahul Lakkireddy if (link->tid_map) 474d8931847SRahul Lakkireddy t4_free_mem(link->tid_map); 475d8931847SRahul Lakkireddy } 476d8931847SRahul Lakkireddy 477d8931847SRahul Lakkireddy if (t) 478d8931847SRahul Lakkireddy t4_free_mem(t); 479d8931847SRahul Lakkireddy 480d8931847SRahul Lakkireddy return NULL; 481d8931847SRahul Lakkireddy } 482