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" 3941ec03e5SRahul Lakkireddy #include "cxgb4_filter.h" 40d8931847SRahul Lakkireddy #include "cxgb4_tc_u32_parse.h" 41d8931847SRahul Lakkireddy #include "cxgb4_tc_u32.h" 42d8931847SRahul Lakkireddy 43d8931847SRahul Lakkireddy /* Fill ch_filter_specification with parsed match value/mask pair. */ 44d8931847SRahul Lakkireddy static int fill_match_fields(struct adapter *adap, 45d8931847SRahul Lakkireddy struct ch_filter_specification *fs, 46d8931847SRahul Lakkireddy struct tc_cls_u32_offload *cls, 47d8931847SRahul Lakkireddy const struct cxgb4_match_field *entry, 48d8931847SRahul Lakkireddy bool next_header) 49d8931847SRahul Lakkireddy { 50d8931847SRahul Lakkireddy unsigned int i, j; 51*27f78cb2SRahul Lakkireddy __be32 val, mask; 52d8931847SRahul Lakkireddy int off, err; 53d8931847SRahul Lakkireddy bool found; 54d8931847SRahul Lakkireddy 55d8931847SRahul Lakkireddy for (i = 0; i < cls->knode.sel->nkeys; i++) { 56d8931847SRahul Lakkireddy off = cls->knode.sel->keys[i].off; 57d8931847SRahul Lakkireddy val = cls->knode.sel->keys[i].val; 58d8931847SRahul Lakkireddy mask = cls->knode.sel->keys[i].mask; 59d8931847SRahul Lakkireddy 60d8931847SRahul Lakkireddy if (next_header) { 61d8931847SRahul Lakkireddy /* For next headers, parse only keys with offmask */ 62d8931847SRahul Lakkireddy if (!cls->knode.sel->keys[i].offmask) 63d8931847SRahul Lakkireddy continue; 64d8931847SRahul Lakkireddy } else { 65d8931847SRahul Lakkireddy /* For the remaining, parse only keys without offmask */ 66d8931847SRahul Lakkireddy if (cls->knode.sel->keys[i].offmask) 67d8931847SRahul Lakkireddy continue; 68d8931847SRahul Lakkireddy } 69d8931847SRahul Lakkireddy 70d8931847SRahul Lakkireddy found = false; 71d8931847SRahul Lakkireddy 72d8931847SRahul Lakkireddy for (j = 0; entry[j].val; j++) { 73d8931847SRahul Lakkireddy if (off == entry[j].off) { 74d8931847SRahul Lakkireddy found = true; 75d8931847SRahul Lakkireddy err = entry[j].val(fs, val, mask); 76d8931847SRahul Lakkireddy if (err) 77d8931847SRahul Lakkireddy return err; 78d8931847SRahul Lakkireddy break; 79d8931847SRahul Lakkireddy } 80d8931847SRahul Lakkireddy } 81d8931847SRahul Lakkireddy 82d8931847SRahul Lakkireddy if (!found) 83d8931847SRahul Lakkireddy return -EINVAL; 84d8931847SRahul Lakkireddy } 85d8931847SRahul Lakkireddy 86d8931847SRahul Lakkireddy return 0; 87d8931847SRahul Lakkireddy } 88d8931847SRahul Lakkireddy 89b20ff726SRahul Lakkireddy /* Fill ch_filter_specification with parsed action. */ 90b20ff726SRahul Lakkireddy static int fill_action_fields(struct adapter *adap, 91b20ff726SRahul Lakkireddy struct ch_filter_specification *fs, 92b20ff726SRahul Lakkireddy struct tc_cls_u32_offload *cls) 93b20ff726SRahul Lakkireddy { 94b20ff726SRahul Lakkireddy unsigned int num_actions = 0; 95b20ff726SRahul Lakkireddy const struct tc_action *a; 96b20ff726SRahul Lakkireddy struct tcf_exts *exts; 97244cd96aSCong Wang int i; 98b20ff726SRahul Lakkireddy 99b20ff726SRahul Lakkireddy exts = cls->knode.exts; 1003bcc0cecSJiri Pirko if (!tcf_exts_has_actions(exts)) 101b20ff726SRahul Lakkireddy return -EINVAL; 102b20ff726SRahul Lakkireddy 103244cd96aSCong Wang tcf_exts_for_each_action(i, a, exts) { 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)) { 1179f8a739eSCong Wang struct net_device *n_dev, *target_dev; 118b20ff726SRahul Lakkireddy bool found = false; 1199f8a739eSCong Wang unsigned int i; 120b20ff726SRahul Lakkireddy 1219f8a739eSCong Wang target_dev = tcf_mirred_dev(a); 122b20ff726SRahul Lakkireddy for_each_port(adap, i) { 123b20ff726SRahul Lakkireddy n_dev = adap->port[i]; 1249f8a739eSCong Wang if (target_dev == n_dev) { 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 1495fd9fc4eSJiri Pirko int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) 150d8931847SRahul Lakkireddy { 151d8931847SRahul Lakkireddy const struct cxgb4_match_field *start, *link_start = NULL; 15241ec03e5SRahul Lakkireddy struct netlink_ext_ack *extack = cls->common.extack; 153d8931847SRahul Lakkireddy struct adapter *adapter = netdev2adap(dev); 1545fd9fc4eSJiri Pirko __be16 protocol = cls->common.protocol; 155d8931847SRahul Lakkireddy struct ch_filter_specification fs; 156d8931847SRahul Lakkireddy struct cxgb4_tc_u32_table *t; 157d8931847SRahul Lakkireddy struct cxgb4_link *link; 158d8931847SRahul Lakkireddy u32 uhtid, link_uhtid; 159d8931847SRahul Lakkireddy bool is_ipv6 = false; 1608d174351SRahul Lakkireddy u8 inet_family; 1618d174351SRahul Lakkireddy int filter_id; 162d8931847SRahul Lakkireddy int ret; 163d8931847SRahul Lakkireddy 164d8931847SRahul Lakkireddy if (!can_tc_u32_offload(dev)) 165d8931847SRahul Lakkireddy return -EOPNOTSUPP; 166d8931847SRahul Lakkireddy 167d8931847SRahul Lakkireddy if (protocol != htons(ETH_P_IP) && protocol != htons(ETH_P_IPV6)) 168d8931847SRahul Lakkireddy return -EOPNOTSUPP; 169d8931847SRahul Lakkireddy 1708d174351SRahul Lakkireddy inet_family = (protocol == htons(ETH_P_IPV6)) ? PF_INET6 : PF_INET; 171d8931847SRahul Lakkireddy 1728d174351SRahul Lakkireddy /* Get a free filter entry TID, where we can insert this new 1738d174351SRahul Lakkireddy * rule. Only insert rule if its prio doesn't conflict with 1748d174351SRahul Lakkireddy * existing rules. 17541ec03e5SRahul Lakkireddy */ 1768d174351SRahul Lakkireddy filter_id = cxgb4_get_free_ftid(dev, inet_family, false, 1778d174351SRahul Lakkireddy TC_U32_NODE(cls->knode.handle)); 1788d174351SRahul Lakkireddy if (filter_id < 0) { 17941ec03e5SRahul Lakkireddy NL_SET_ERR_MSG_MOD(extack, 18041ec03e5SRahul Lakkireddy "No free LETCAM index available"); 18141ec03e5SRahul Lakkireddy return -ENOMEM; 182d8931847SRahul Lakkireddy } 183d8931847SRahul Lakkireddy 184d8931847SRahul Lakkireddy t = adapter->tc_u32; 185d8931847SRahul Lakkireddy uhtid = TC_U32_USERHTID(cls->knode.handle); 186d8931847SRahul Lakkireddy link_uhtid = TC_U32_USERHTID(cls->knode.link_handle); 187d8931847SRahul Lakkireddy 188d8931847SRahul Lakkireddy /* Ensure that uhtid is either root u32 (i.e. 0x800) 189d8931847SRahul Lakkireddy * or a a valid linked bucket. 190d8931847SRahul Lakkireddy */ 191d8931847SRahul Lakkireddy if (uhtid != 0x800 && uhtid >= t->size) 192d8931847SRahul Lakkireddy return -EINVAL; 193d8931847SRahul Lakkireddy 194d8931847SRahul Lakkireddy /* Ensure link handle uhtid is sane, if specified. */ 195d8931847SRahul Lakkireddy if (link_uhtid >= t->size) 196d8931847SRahul Lakkireddy return -EINVAL; 197d8931847SRahul Lakkireddy 198d8931847SRahul Lakkireddy memset(&fs, 0, sizeof(fs)); 199d8931847SRahul Lakkireddy 200c2193999SShahjada Abul Husain if (filter_id < adapter->tids.nhpftids) 201c2193999SShahjada Abul Husain fs.prio = 1; 20241ec03e5SRahul Lakkireddy fs.tc_prio = cls->common.prio; 20341ec03e5SRahul Lakkireddy fs.tc_cookie = cls->knode.handle; 20441ec03e5SRahul Lakkireddy 205d8931847SRahul Lakkireddy if (protocol == htons(ETH_P_IPV6)) { 206d8931847SRahul Lakkireddy start = cxgb4_ipv6_fields; 207d8931847SRahul Lakkireddy is_ipv6 = true; 208d8931847SRahul Lakkireddy } else { 209d8931847SRahul Lakkireddy start = cxgb4_ipv4_fields; 210d8931847SRahul Lakkireddy is_ipv6 = false; 211d8931847SRahul Lakkireddy } 212d8931847SRahul Lakkireddy 213d8931847SRahul Lakkireddy if (uhtid != 0x800) { 214d8931847SRahul Lakkireddy /* Link must exist from root node before insertion. */ 215d8931847SRahul Lakkireddy if (!t->table[uhtid - 1].link_handle) 216d8931847SRahul Lakkireddy return -EINVAL; 217d8931847SRahul Lakkireddy 218d8931847SRahul Lakkireddy /* Link must have a valid supported next header. */ 219d8931847SRahul Lakkireddy link_start = t->table[uhtid - 1].match_field; 220d8931847SRahul Lakkireddy if (!link_start) 221d8931847SRahul Lakkireddy return -EINVAL; 222d8931847SRahul Lakkireddy } 223d8931847SRahul Lakkireddy 224d8931847SRahul Lakkireddy /* Parse links and record them for subsequent jumps to valid 225d8931847SRahul Lakkireddy * next headers. 226d8931847SRahul Lakkireddy */ 227d8931847SRahul Lakkireddy if (link_uhtid) { 228d8931847SRahul Lakkireddy const struct cxgb4_next_header *next; 229d8931847SRahul Lakkireddy bool found = false; 230d8931847SRahul Lakkireddy unsigned int i, j; 231*27f78cb2SRahul Lakkireddy __be32 val, mask; 232d8931847SRahul Lakkireddy int off; 233d8931847SRahul Lakkireddy 234d8931847SRahul Lakkireddy if (t->table[link_uhtid - 1].link_handle) { 235d8931847SRahul Lakkireddy dev_err(adapter->pdev_dev, 236d8931847SRahul Lakkireddy "Link handle exists for: 0x%x\n", 237d8931847SRahul Lakkireddy link_uhtid); 238d8931847SRahul Lakkireddy return -EINVAL; 239d8931847SRahul Lakkireddy } 240d8931847SRahul Lakkireddy 241d8931847SRahul Lakkireddy next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps; 242d8931847SRahul Lakkireddy 243d8931847SRahul Lakkireddy /* Try to find matches that allow jumps to next header. */ 244d8931847SRahul Lakkireddy for (i = 0; next[i].jump; i++) { 245*27f78cb2SRahul Lakkireddy if (next[i].sel.offoff != cls->knode.sel->offoff || 246*27f78cb2SRahul Lakkireddy next[i].sel.offshift != cls->knode.sel->offshift || 247*27f78cb2SRahul Lakkireddy next[i].sel.offmask != cls->knode.sel->offmask || 248*27f78cb2SRahul Lakkireddy next[i].sel.off != cls->knode.sel->off) 249d8931847SRahul Lakkireddy continue; 250d8931847SRahul Lakkireddy 251d8931847SRahul Lakkireddy /* Found a possible candidate. Find a key that 252d8931847SRahul Lakkireddy * matches the corresponding offset, value, and 253d8931847SRahul Lakkireddy * mask to jump to next header. 254d8931847SRahul Lakkireddy */ 255d8931847SRahul Lakkireddy for (j = 0; j < cls->knode.sel->nkeys; j++) { 256d8931847SRahul Lakkireddy off = cls->knode.sel->keys[j].off; 257d8931847SRahul Lakkireddy val = cls->knode.sel->keys[j].val; 258d8931847SRahul Lakkireddy mask = cls->knode.sel->keys[j].mask; 259d8931847SRahul Lakkireddy 260*27f78cb2SRahul Lakkireddy if (next[i].key.off == off && 261*27f78cb2SRahul Lakkireddy next[i].key.val == val && 262*27f78cb2SRahul Lakkireddy next[i].key.mask == mask) { 263d8931847SRahul Lakkireddy found = true; 264d8931847SRahul Lakkireddy break; 265d8931847SRahul Lakkireddy } 266d8931847SRahul Lakkireddy } 267d8931847SRahul Lakkireddy 268d8931847SRahul Lakkireddy if (!found) 269d8931847SRahul Lakkireddy continue; /* Try next candidate. */ 270d8931847SRahul Lakkireddy 271d8931847SRahul Lakkireddy /* Candidate to jump to next header found. 272d8931847SRahul Lakkireddy * Translate all keys to internal specification 273d8931847SRahul Lakkireddy * and store them in jump table. This spec is copied 274d8931847SRahul Lakkireddy * later to set the actual filters. 275d8931847SRahul Lakkireddy */ 276d8931847SRahul Lakkireddy ret = fill_match_fields(adapter, &fs, cls, 277d8931847SRahul Lakkireddy start, false); 278d8931847SRahul Lakkireddy if (ret) 279d8931847SRahul Lakkireddy goto out; 280d8931847SRahul Lakkireddy 281d8931847SRahul Lakkireddy link = &t->table[link_uhtid - 1]; 282d8931847SRahul Lakkireddy link->match_field = next[i].jump; 283d8931847SRahul Lakkireddy link->link_handle = cls->knode.handle; 284d8931847SRahul Lakkireddy memcpy(&link->fs, &fs, sizeof(fs)); 285d8931847SRahul Lakkireddy break; 286d8931847SRahul Lakkireddy } 287d8931847SRahul Lakkireddy 288d8931847SRahul Lakkireddy /* No candidate found to jump to next header. */ 289d8931847SRahul Lakkireddy if (!found) 290d8931847SRahul Lakkireddy return -EINVAL; 291d8931847SRahul Lakkireddy 292d8931847SRahul Lakkireddy return 0; 293d8931847SRahul Lakkireddy } 294d8931847SRahul Lakkireddy 295d8931847SRahul Lakkireddy /* Fill ch_filter_specification match fields to be shipped to hardware. 296d8931847SRahul Lakkireddy * Copy the linked spec (if any) first. And then update the spec as 297d8931847SRahul Lakkireddy * needed. 298d8931847SRahul Lakkireddy */ 299d8931847SRahul Lakkireddy if (uhtid != 0x800 && t->table[uhtid - 1].link_handle) { 300d8931847SRahul Lakkireddy /* Copy linked ch_filter_specification */ 301d8931847SRahul Lakkireddy memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs)); 302d8931847SRahul Lakkireddy ret = fill_match_fields(adapter, &fs, cls, 303d8931847SRahul Lakkireddy link_start, true); 304d8931847SRahul Lakkireddy if (ret) 305d8931847SRahul Lakkireddy goto out; 306d8931847SRahul Lakkireddy } 307d8931847SRahul Lakkireddy 308d8931847SRahul Lakkireddy ret = fill_match_fields(adapter, &fs, cls, start, false); 309d8931847SRahul Lakkireddy if (ret) 310d8931847SRahul Lakkireddy goto out; 311d8931847SRahul Lakkireddy 312b20ff726SRahul Lakkireddy /* Fill ch_filter_specification action fields to be shipped to 313b20ff726SRahul Lakkireddy * hardware. 314b20ff726SRahul Lakkireddy */ 315b20ff726SRahul Lakkireddy ret = fill_action_fields(adapter, &fs, cls); 316b20ff726SRahul Lakkireddy if (ret) 317b20ff726SRahul Lakkireddy goto out; 318b20ff726SRahul Lakkireddy 319d8931847SRahul Lakkireddy /* The filter spec has been completely built from the info 320d8931847SRahul Lakkireddy * provided from u32. We now set some default fields in the 321d8931847SRahul Lakkireddy * spec for sanity. 322d8931847SRahul Lakkireddy */ 323d8931847SRahul Lakkireddy 324d8931847SRahul Lakkireddy /* Match only packets coming from the ingress port where this 325d8931847SRahul Lakkireddy * filter will be created. 326d8931847SRahul Lakkireddy */ 327d8931847SRahul Lakkireddy fs.val.iport = netdev2pinfo(dev)->port_id; 328d8931847SRahul Lakkireddy fs.mask.iport = ~0; 329d8931847SRahul Lakkireddy 330d8931847SRahul Lakkireddy /* Enable filter hit counts. */ 331d8931847SRahul Lakkireddy fs.hitcnts = 1; 332d8931847SRahul Lakkireddy 333d8931847SRahul Lakkireddy /* Set type of filter - IPv6 or IPv4 */ 334d8931847SRahul Lakkireddy fs.type = is_ipv6 ? 1 : 0; 335d8931847SRahul Lakkireddy 336d8931847SRahul Lakkireddy /* Set the filter */ 337d8931847SRahul Lakkireddy ret = cxgb4_set_filter(dev, filter_id, &fs); 338d8931847SRahul Lakkireddy if (ret) 339d8931847SRahul Lakkireddy goto out; 340d8931847SRahul Lakkireddy 341d8931847SRahul Lakkireddy /* If this is a linked bucket, then set the corresponding 342d8931847SRahul Lakkireddy * entry in the bitmap to mark it as belonging to this linked 343d8931847SRahul Lakkireddy * bucket. 344d8931847SRahul Lakkireddy */ 345d8931847SRahul Lakkireddy if (uhtid != 0x800 && t->table[uhtid - 1].link_handle) 346d8931847SRahul Lakkireddy set_bit(filter_id, t->table[uhtid - 1].tid_map); 347d8931847SRahul Lakkireddy 348d8931847SRahul Lakkireddy out: 349d8931847SRahul Lakkireddy return ret; 350d8931847SRahul Lakkireddy } 351d8931847SRahul Lakkireddy 3525fd9fc4eSJiri Pirko int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) 353d8931847SRahul Lakkireddy { 354d8931847SRahul Lakkireddy struct adapter *adapter = netdev2adap(dev); 355d8931847SRahul Lakkireddy unsigned int filter_id, max_tids, i, j; 356d8931847SRahul Lakkireddy struct cxgb4_link *link = NULL; 357d8931847SRahul Lakkireddy struct cxgb4_tc_u32_table *t; 358c2193999SShahjada Abul Husain struct filter_entry *f; 3598d174351SRahul Lakkireddy bool found = false; 360d8931847SRahul Lakkireddy u32 handle, uhtid; 3618d174351SRahul Lakkireddy u8 nslots; 362d8931847SRahul Lakkireddy int ret; 363d8931847SRahul Lakkireddy 364d8931847SRahul Lakkireddy if (!can_tc_u32_offload(dev)) 365d8931847SRahul Lakkireddy return -EOPNOTSUPP; 366d8931847SRahul Lakkireddy 367d8931847SRahul Lakkireddy /* Fetch the location to delete the filter. */ 3688d174351SRahul Lakkireddy max_tids = adapter->tids.nhpftids + adapter->tids.nftids; 369c2193999SShahjada Abul Husain 3708d174351SRahul Lakkireddy spin_lock_bh(&adapter->tids.ftid_lock); 3718d174351SRahul Lakkireddy filter_id = 0; 3728d174351SRahul Lakkireddy while (filter_id < max_tids) { 3738d174351SRahul Lakkireddy if (filter_id < adapter->tids.nhpftids) { 3748d174351SRahul Lakkireddy i = filter_id; 3758d174351SRahul Lakkireddy f = &adapter->tids.hpftid_tab[i]; 3768d174351SRahul Lakkireddy if (f->valid && f->fs.tc_cookie == cls->knode.handle) { 3778d174351SRahul Lakkireddy found = true; 3788d174351SRahul Lakkireddy break; 3798d174351SRahul Lakkireddy } 380c2193999SShahjada Abul Husain 3818d174351SRahul Lakkireddy i = find_next_bit(adapter->tids.hpftid_bmap, 3828d174351SRahul Lakkireddy adapter->tids.nhpftids, i + 1); 3838d174351SRahul Lakkireddy if (i >= adapter->tids.nhpftids) { 3848d174351SRahul Lakkireddy filter_id = adapter->tids.nhpftids; 3858d174351SRahul Lakkireddy continue; 3868d174351SRahul Lakkireddy } 3878d174351SRahul Lakkireddy 3888d174351SRahul Lakkireddy filter_id = i; 3898d174351SRahul Lakkireddy } else { 3908d174351SRahul Lakkireddy i = filter_id - adapter->tids.nhpftids; 3918d174351SRahul Lakkireddy f = &adapter->tids.ftid_tab[i]; 3928d174351SRahul Lakkireddy if (f->valid && f->fs.tc_cookie == cls->knode.handle) { 3938d174351SRahul Lakkireddy found = true; 3948d174351SRahul Lakkireddy break; 3958d174351SRahul Lakkireddy } 3968d174351SRahul Lakkireddy 3978d174351SRahul Lakkireddy i = find_next_bit(adapter->tids.ftid_bmap, 3988d174351SRahul Lakkireddy adapter->tids.nftids, i + 1); 3998d174351SRahul Lakkireddy if (i >= adapter->tids.nftids) 4008d174351SRahul Lakkireddy break; 4018d174351SRahul Lakkireddy 4028d174351SRahul Lakkireddy filter_id = i + adapter->tids.nhpftids; 4038d174351SRahul Lakkireddy } 4048d174351SRahul Lakkireddy 4058d174351SRahul Lakkireddy nslots = 0; 4068d174351SRahul Lakkireddy if (f->fs.type) { 4078d174351SRahul Lakkireddy nslots++; 4088d174351SRahul Lakkireddy if (CHELSIO_CHIP_VERSION(adapter->params.chip) < 4098d174351SRahul Lakkireddy CHELSIO_T6) 4108d174351SRahul Lakkireddy nslots += 2; 4118d174351SRahul Lakkireddy } 4128d174351SRahul Lakkireddy 4138d174351SRahul Lakkireddy filter_id += nslots; 4148d174351SRahul Lakkireddy } 4158d174351SRahul Lakkireddy spin_unlock_bh(&adapter->tids.ftid_lock); 4168d174351SRahul Lakkireddy 4178d174351SRahul Lakkireddy if (!found) 418d8931847SRahul Lakkireddy return -ERANGE; 419d8931847SRahul Lakkireddy 420d8931847SRahul Lakkireddy t = adapter->tc_u32; 421d8931847SRahul Lakkireddy handle = cls->knode.handle; 422d8931847SRahul Lakkireddy uhtid = TC_U32_USERHTID(cls->knode.handle); 423d8931847SRahul Lakkireddy 424d8931847SRahul Lakkireddy /* Ensure that uhtid is either root u32 (i.e. 0x800) 425d8931847SRahul Lakkireddy * or a a valid linked bucket. 426d8931847SRahul Lakkireddy */ 427d8931847SRahul Lakkireddy if (uhtid != 0x800 && uhtid >= t->size) 428d8931847SRahul Lakkireddy return -EINVAL; 429d8931847SRahul Lakkireddy 430d8931847SRahul Lakkireddy /* Delete the specified filter */ 431d8931847SRahul Lakkireddy if (uhtid != 0x800) { 432d8931847SRahul Lakkireddy link = &t->table[uhtid - 1]; 433d8931847SRahul Lakkireddy if (!link->link_handle) 434d8931847SRahul Lakkireddy return -EINVAL; 435d8931847SRahul Lakkireddy 436d8931847SRahul Lakkireddy if (!test_bit(filter_id, link->tid_map)) 437d8931847SRahul Lakkireddy return -EINVAL; 438d8931847SRahul Lakkireddy } 439d8931847SRahul Lakkireddy 4403b0b3beeSKumar Sanghvi ret = cxgb4_del_filter(dev, filter_id, NULL); 441d8931847SRahul Lakkireddy if (ret) 442d8931847SRahul Lakkireddy goto out; 443d8931847SRahul Lakkireddy 444d8931847SRahul Lakkireddy if (link) 445d8931847SRahul Lakkireddy clear_bit(filter_id, link->tid_map); 446d8931847SRahul Lakkireddy 447d8931847SRahul Lakkireddy /* If a link is being deleted, then delete all filters 448d8931847SRahul Lakkireddy * associated with the link. 449d8931847SRahul Lakkireddy */ 450d8931847SRahul Lakkireddy for (i = 0; i < t->size; i++) { 451d8931847SRahul Lakkireddy link = &t->table[i]; 452d8931847SRahul Lakkireddy 453d8931847SRahul Lakkireddy if (link->link_handle == handle) { 454d8931847SRahul Lakkireddy for (j = 0; j < max_tids; j++) { 455d8931847SRahul Lakkireddy if (!test_bit(j, link->tid_map)) 456d8931847SRahul Lakkireddy continue; 457d8931847SRahul Lakkireddy 4583b0b3beeSKumar Sanghvi ret = __cxgb4_del_filter(dev, j, NULL, NULL); 459d8931847SRahul Lakkireddy if (ret) 460d8931847SRahul Lakkireddy goto out; 461d8931847SRahul Lakkireddy 462d8931847SRahul Lakkireddy clear_bit(j, link->tid_map); 463d8931847SRahul Lakkireddy } 464d8931847SRahul Lakkireddy 465d8931847SRahul Lakkireddy /* Clear the link state */ 466d8931847SRahul Lakkireddy link->match_field = NULL; 467d8931847SRahul Lakkireddy link->link_handle = 0; 468d8931847SRahul Lakkireddy memset(&link->fs, 0, sizeof(link->fs)); 469d8931847SRahul Lakkireddy break; 470d8931847SRahul Lakkireddy } 471d8931847SRahul Lakkireddy } 472d8931847SRahul Lakkireddy 473d8931847SRahul Lakkireddy out: 474d8931847SRahul Lakkireddy return ret; 475d8931847SRahul Lakkireddy } 476d8931847SRahul Lakkireddy 477d8931847SRahul Lakkireddy void cxgb4_cleanup_tc_u32(struct adapter *adap) 478d8931847SRahul Lakkireddy { 479d8931847SRahul Lakkireddy struct cxgb4_tc_u32_table *t; 480d8931847SRahul Lakkireddy unsigned int i; 481d8931847SRahul Lakkireddy 482d8931847SRahul Lakkireddy if (!adap->tc_u32) 483d8931847SRahul Lakkireddy return; 484d8931847SRahul Lakkireddy 485d8931847SRahul Lakkireddy /* Free up all allocated memory. */ 486d8931847SRahul Lakkireddy t = adap->tc_u32; 487d8931847SRahul Lakkireddy for (i = 0; i < t->size; i++) { 488d8931847SRahul Lakkireddy struct cxgb4_link *link = &t->table[i]; 489d8931847SRahul Lakkireddy 490752ade68SMichal Hocko kvfree(link->tid_map); 491d8931847SRahul Lakkireddy } 492752ade68SMichal Hocko kvfree(adap->tc_u32); 493d8931847SRahul Lakkireddy } 494d8931847SRahul Lakkireddy 49545da1ca2SArjun V struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap) 496d8931847SRahul Lakkireddy { 497c2193999SShahjada Abul Husain unsigned int max_tids = adap->tids.nftids + adap->tids.nhpftids; 498d8931847SRahul Lakkireddy struct cxgb4_tc_u32_table *t; 499d8931847SRahul Lakkireddy unsigned int i; 500d8931847SRahul Lakkireddy 50145da1ca2SArjun V if (!max_tids) 502d8931847SRahul Lakkireddy return NULL; 503d8931847SRahul Lakkireddy 504c829f5f5SGustavo A. R. Silva t = kvzalloc(struct_size(t, table, max_tids), GFP_KERNEL); 505d8931847SRahul Lakkireddy if (!t) 506d8931847SRahul Lakkireddy return NULL; 507d8931847SRahul Lakkireddy 50845da1ca2SArjun V t->size = max_tids; 509d8931847SRahul Lakkireddy 510d8931847SRahul Lakkireddy for (i = 0; i < t->size; i++) { 511d8931847SRahul Lakkireddy struct cxgb4_link *link = &t->table[i]; 512d8931847SRahul Lakkireddy unsigned int bmap_size; 513d8931847SRahul Lakkireddy 514d8931847SRahul Lakkireddy bmap_size = BITS_TO_LONGS(max_tids); 515778e1cddSKees Cook link->tid_map = kvcalloc(bmap_size, sizeof(unsigned long), 516778e1cddSKees Cook GFP_KERNEL); 517d8931847SRahul Lakkireddy if (!link->tid_map) 518d8931847SRahul Lakkireddy goto out_no_mem; 519d8931847SRahul Lakkireddy bitmap_zero(link->tid_map, max_tids); 520d8931847SRahul Lakkireddy } 521d8931847SRahul Lakkireddy 522d8931847SRahul Lakkireddy return t; 523d8931847SRahul Lakkireddy 524d8931847SRahul Lakkireddy out_no_mem: 525d8931847SRahul Lakkireddy for (i = 0; i < t->size; i++) { 526d8931847SRahul Lakkireddy struct cxgb4_link *link = &t->table[i]; 527752ade68SMichal Hocko kvfree(link->tid_map); 528d8931847SRahul Lakkireddy } 529752ade68SMichal Hocko kvfree(t); 530d8931847SRahul Lakkireddy 531d8931847SRahul Lakkireddy return NULL; 532d8931847SRahul Lakkireddy } 533