Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * This file is part of the Chelsio T4 Ethernet driver for Linux.
0003  *
0004  * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
0005  *
0006  * This software is available to you under a choice of one of two
0007  * licenses.  You may choose to be licensed under the terms of the GNU
0008  * General Public License (GPL) Version 2, available from the file
0009  * COPYING in the main directory of this source tree, or the
0010  * OpenIB.org BSD license below:
0011  *
0012  *     Redistribution and use in source and binary forms, with or
0013  *     without modification, are permitted provided that the following
0014  *     conditions are met:
0015  *
0016  *      - Redistributions of source code must retain the above
0017  *        copyright notice, this list of conditions and the following
0018  *        disclaimer.
0019  *
0020  *      - Redistributions in binary form must reproduce the above
0021  *        copyright notice, this list of conditions and the following
0022  *        disclaimer in the documentation and/or other materials
0023  *        provided with the distribution.
0024  *
0025  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0026  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0027  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0028  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
0029  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
0030  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
0031  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
0032  * SOFTWARE.
0033  */
0034 
0035 #include <net/tc_act/tc_gact.h>
0036 #include <net/tc_act/tc_mirred.h>
0037 
0038 #include "cxgb4.h"
0039 #include "cxgb4_filter.h"
0040 #include "cxgb4_tc_u32_parse.h"
0041 #include "cxgb4_tc_u32.h"
0042 
0043 /* Fill ch_filter_specification with parsed match value/mask pair. */
0044 static int fill_match_fields(struct adapter *adap,
0045                  struct ch_filter_specification *fs,
0046                  struct tc_cls_u32_offload *cls,
0047                  const struct cxgb4_match_field *entry,
0048                  bool next_header)
0049 {
0050     unsigned int i, j;
0051     __be32 val, mask;
0052     int off, err;
0053     bool found;
0054 
0055     for (i = 0; i < cls->knode.sel->nkeys; i++) {
0056         off = cls->knode.sel->keys[i].off;
0057         val = cls->knode.sel->keys[i].val;
0058         mask = cls->knode.sel->keys[i].mask;
0059 
0060         if (next_header) {
0061             /* For next headers, parse only keys with offmask */
0062             if (!cls->knode.sel->keys[i].offmask)
0063                 continue;
0064         } else {
0065             /* For the remaining, parse only keys without offmask */
0066             if (cls->knode.sel->keys[i].offmask)
0067                 continue;
0068         }
0069 
0070         found = false;
0071 
0072         for (j = 0; entry[j].val; j++) {
0073             if (off == entry[j].off) {
0074                 found = true;
0075                 err = entry[j].val(fs, val, mask);
0076                 if (err)
0077                     return err;
0078                 break;
0079             }
0080         }
0081 
0082         if (!found)
0083             return -EINVAL;
0084     }
0085 
0086     return 0;
0087 }
0088 
0089 /* Fill ch_filter_specification with parsed action. */
0090 static int fill_action_fields(struct adapter *adap,
0091                   struct ch_filter_specification *fs,
0092                   struct tc_cls_u32_offload *cls)
0093 {
0094     unsigned int num_actions = 0;
0095     const struct tc_action *a;
0096     struct tcf_exts *exts;
0097     int i;
0098 
0099     exts = cls->knode.exts;
0100     if (!tcf_exts_has_actions(exts))
0101         return -EINVAL;
0102 
0103     tcf_exts_for_each_action(i, a, exts) {
0104         /* Don't allow more than one action per rule. */
0105         if (num_actions)
0106             return -EINVAL;
0107 
0108         /* Drop in hardware. */
0109         if (is_tcf_gact_shot(a)) {
0110             fs->action = FILTER_DROP;
0111             num_actions++;
0112             continue;
0113         }
0114 
0115         /* Re-direct to specified port in hardware. */
0116         if (is_tcf_mirred_egress_redirect(a)) {
0117             struct net_device *n_dev, *target_dev;
0118             bool found = false;
0119             unsigned int i;
0120 
0121             target_dev = tcf_mirred_dev(a);
0122             for_each_port(adap, i) {
0123                 n_dev = adap->port[i];
0124                 if (target_dev == n_dev) {
0125                     fs->action = FILTER_SWITCH;
0126                     fs->eport = i;
0127                     found = true;
0128                     break;
0129                 }
0130             }
0131 
0132             /* Interface doesn't belong to any port of
0133              * the underlying hardware.
0134              */
0135             if (!found)
0136                 return -EINVAL;
0137 
0138             num_actions++;
0139             continue;
0140         }
0141 
0142         /* Un-supported action. */
0143         return -EINVAL;
0144     }
0145 
0146     return 0;
0147 }
0148 
0149 int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
0150 {
0151     const struct cxgb4_match_field *start, *link_start = NULL;
0152     struct netlink_ext_ack *extack = cls->common.extack;
0153     struct adapter *adapter = netdev2adap(dev);
0154     __be16 protocol = cls->common.protocol;
0155     struct ch_filter_specification fs;
0156     struct cxgb4_tc_u32_table *t;
0157     struct cxgb4_link *link;
0158     u32 uhtid, link_uhtid;
0159     bool is_ipv6 = false;
0160     u8 inet_family;
0161     int filter_id;
0162     int ret;
0163 
0164     if (!can_tc_u32_offload(dev))
0165         return -EOPNOTSUPP;
0166 
0167     if (protocol != htons(ETH_P_IP) && protocol != htons(ETH_P_IPV6))
0168         return -EOPNOTSUPP;
0169 
0170     inet_family = (protocol == htons(ETH_P_IPV6)) ? PF_INET6 : PF_INET;
0171 
0172     /* Get a free filter entry TID, where we can insert this new
0173      * rule. Only insert rule if its prio doesn't conflict with
0174      * existing rules.
0175      */
0176     filter_id = cxgb4_get_free_ftid(dev, inet_family, false,
0177                     TC_U32_NODE(cls->knode.handle));
0178     if (filter_id < 0) {
0179         NL_SET_ERR_MSG_MOD(extack,
0180                    "No free LETCAM index available");
0181         return -ENOMEM;
0182     }
0183 
0184     t = adapter->tc_u32;
0185     uhtid = TC_U32_USERHTID(cls->knode.handle);
0186     link_uhtid = TC_U32_USERHTID(cls->knode.link_handle);
0187 
0188     /* Ensure that uhtid is either root u32 (i.e. 0x800)
0189      * or a a valid linked bucket.
0190      */
0191     if (uhtid != 0x800 && uhtid >= t->size)
0192         return -EINVAL;
0193 
0194     /* Ensure link handle uhtid is sane, if specified. */
0195     if (link_uhtid >= t->size)
0196         return -EINVAL;
0197 
0198     memset(&fs, 0, sizeof(fs));
0199 
0200     if (filter_id < adapter->tids.nhpftids)
0201         fs.prio = 1;
0202     fs.tc_prio = cls->common.prio;
0203     fs.tc_cookie = cls->knode.handle;
0204 
0205     if (protocol == htons(ETH_P_IPV6)) {
0206         start = cxgb4_ipv6_fields;
0207         is_ipv6 = true;
0208     } else {
0209         start = cxgb4_ipv4_fields;
0210         is_ipv6 = false;
0211     }
0212 
0213     if (uhtid != 0x800) {
0214         /* Link must exist from root node before insertion. */
0215         if (!t->table[uhtid - 1].link_handle)
0216             return -EINVAL;
0217 
0218         /* Link must have a valid supported next header. */
0219         link_start = t->table[uhtid - 1].match_field;
0220         if (!link_start)
0221             return -EINVAL;
0222     }
0223 
0224     /* Parse links and record them for subsequent jumps to valid
0225      * next headers.
0226      */
0227     if (link_uhtid) {
0228         const struct cxgb4_next_header *next;
0229         bool found = false;
0230         unsigned int i, j;
0231         __be32 val, mask;
0232         int off;
0233 
0234         if (t->table[link_uhtid - 1].link_handle) {
0235             dev_err(adapter->pdev_dev,
0236                 "Link handle exists for: 0x%x\n",
0237                 link_uhtid);
0238             return -EINVAL;
0239         }
0240 
0241         next = is_ipv6 ? cxgb4_ipv6_jumps : cxgb4_ipv4_jumps;
0242 
0243         /* Try to find matches that allow jumps to next header. */
0244         for (i = 0; next[i].jump; i++) {
0245             if (next[i].sel.offoff != cls->knode.sel->offoff ||
0246                 next[i].sel.offshift != cls->knode.sel->offshift ||
0247                 next[i].sel.offmask != cls->knode.sel->offmask ||
0248                 next[i].sel.off != cls->knode.sel->off)
0249                 continue;
0250 
0251             /* Found a possible candidate.  Find a key that
0252              * matches the corresponding offset, value, and
0253              * mask to jump to next header.
0254              */
0255             for (j = 0; j < cls->knode.sel->nkeys; j++) {
0256                 off = cls->knode.sel->keys[j].off;
0257                 val = cls->knode.sel->keys[j].val;
0258                 mask = cls->knode.sel->keys[j].mask;
0259 
0260                 if (next[i].key.off == off &&
0261                     next[i].key.val == val &&
0262                     next[i].key.mask == mask) {
0263                     found = true;
0264                     break;
0265                 }
0266             }
0267 
0268             if (!found)
0269                 continue; /* Try next candidate. */
0270 
0271             /* Candidate to jump to next header found.
0272              * Translate all keys to internal specification
0273              * and store them in jump table. This spec is copied
0274              * later to set the actual filters.
0275              */
0276             ret = fill_match_fields(adapter, &fs, cls,
0277                         start, false);
0278             if (ret)
0279                 goto out;
0280 
0281             link = &t->table[link_uhtid - 1];
0282             link->match_field = next[i].jump;
0283             link->link_handle = cls->knode.handle;
0284             memcpy(&link->fs, &fs, sizeof(fs));
0285             break;
0286         }
0287 
0288         /* No candidate found to jump to next header. */
0289         if (!found)
0290             return -EINVAL;
0291 
0292         return 0;
0293     }
0294 
0295     /* Fill ch_filter_specification match fields to be shipped to hardware.
0296      * Copy the linked spec (if any) first.  And then update the spec as
0297      * needed.
0298      */
0299     if (uhtid != 0x800 && t->table[uhtid - 1].link_handle) {
0300         /* Copy linked ch_filter_specification */
0301         memcpy(&fs, &t->table[uhtid - 1].fs, sizeof(fs));
0302         ret = fill_match_fields(adapter, &fs, cls,
0303                     link_start, true);
0304         if (ret)
0305             goto out;
0306     }
0307 
0308     ret = fill_match_fields(adapter, &fs, cls, start, false);
0309     if (ret)
0310         goto out;
0311 
0312     /* Fill ch_filter_specification action fields to be shipped to
0313      * hardware.
0314      */
0315     ret = fill_action_fields(adapter, &fs, cls);
0316     if (ret)
0317         goto out;
0318 
0319     /* The filter spec has been completely built from the info
0320      * provided from u32.  We now set some default fields in the
0321      * spec for sanity.
0322      */
0323 
0324     /* Match only packets coming from the ingress port where this
0325      * filter will be created.
0326      */
0327     fs.val.iport = netdev2pinfo(dev)->port_id;
0328     fs.mask.iport = ~0;
0329 
0330     /* Enable filter hit counts. */
0331     fs.hitcnts = 1;
0332 
0333     /* Set type of filter - IPv6 or IPv4 */
0334     fs.type = is_ipv6 ? 1 : 0;
0335 
0336     /* Set the filter */
0337     ret = cxgb4_set_filter(dev, filter_id, &fs);
0338     if (ret)
0339         goto out;
0340 
0341     /* If this is a linked bucket, then set the corresponding
0342      * entry in the bitmap to mark it as belonging to this linked
0343      * bucket.
0344      */
0345     if (uhtid != 0x800 && t->table[uhtid - 1].link_handle)
0346         set_bit(filter_id, t->table[uhtid - 1].tid_map);
0347 
0348 out:
0349     return ret;
0350 }
0351 
0352 int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls)
0353 {
0354     struct adapter *adapter = netdev2adap(dev);
0355     unsigned int filter_id, max_tids, i, j;
0356     struct cxgb4_link *link = NULL;
0357     struct cxgb4_tc_u32_table *t;
0358     struct filter_entry *f;
0359     bool found = false;
0360     u32 handle, uhtid;
0361     u8 nslots;
0362     int ret;
0363 
0364     if (!can_tc_u32_offload(dev))
0365         return -EOPNOTSUPP;
0366 
0367     /* Fetch the location to delete the filter. */
0368     max_tids = adapter->tids.nhpftids + adapter->tids.nftids;
0369 
0370     spin_lock_bh(&adapter->tids.ftid_lock);
0371     filter_id = 0;
0372     while (filter_id < max_tids) {
0373         if (filter_id < adapter->tids.nhpftids) {
0374             i = filter_id;
0375             f = &adapter->tids.hpftid_tab[i];
0376             if (f->valid && f->fs.tc_cookie == cls->knode.handle) {
0377                 found = true;
0378                 break;
0379             }
0380 
0381             i = find_next_bit(adapter->tids.hpftid_bmap,
0382                       adapter->tids.nhpftids, i + 1);
0383             if (i >= adapter->tids.nhpftids) {
0384                 filter_id = adapter->tids.nhpftids;
0385                 continue;
0386             }
0387 
0388             filter_id = i;
0389         } else {
0390             i = filter_id - adapter->tids.nhpftids;
0391             f = &adapter->tids.ftid_tab[i];
0392             if (f->valid && f->fs.tc_cookie == cls->knode.handle) {
0393                 found = true;
0394                 break;
0395             }
0396 
0397             i = find_next_bit(adapter->tids.ftid_bmap,
0398                       adapter->tids.nftids, i + 1);
0399             if (i >= adapter->tids.nftids)
0400                 break;
0401 
0402             filter_id = i + adapter->tids.nhpftids;
0403         }
0404 
0405         nslots = 0;
0406         if (f->fs.type) {
0407             nslots++;
0408             if (CHELSIO_CHIP_VERSION(adapter->params.chip) <
0409                 CHELSIO_T6)
0410                 nslots += 2;
0411         }
0412 
0413         filter_id += nslots;
0414     }
0415     spin_unlock_bh(&adapter->tids.ftid_lock);
0416 
0417     if (!found)
0418         return -ERANGE;
0419 
0420     t = adapter->tc_u32;
0421     handle = cls->knode.handle;
0422     uhtid = TC_U32_USERHTID(cls->knode.handle);
0423 
0424     /* Ensure that uhtid is either root u32 (i.e. 0x800)
0425      * or a a valid linked bucket.
0426      */
0427     if (uhtid != 0x800 && uhtid >= t->size)
0428         return -EINVAL;
0429 
0430     /* Delete the specified filter */
0431     if (uhtid != 0x800) {
0432         link = &t->table[uhtid - 1];
0433         if (!link->link_handle)
0434             return -EINVAL;
0435 
0436         if (!test_bit(filter_id, link->tid_map))
0437             return -EINVAL;
0438     }
0439 
0440     ret = cxgb4_del_filter(dev, filter_id, NULL);
0441     if (ret)
0442         goto out;
0443 
0444     if (link)
0445         clear_bit(filter_id, link->tid_map);
0446 
0447     /* If a link is being deleted, then delete all filters
0448      * associated with the link.
0449      */
0450     for (i = 0; i < t->size; i++) {
0451         link = &t->table[i];
0452 
0453         if (link->link_handle == handle) {
0454             for (j = 0; j < max_tids; j++) {
0455                 if (!test_bit(j, link->tid_map))
0456                     continue;
0457 
0458                 ret = __cxgb4_del_filter(dev, j, NULL, NULL);
0459                 if (ret)
0460                     goto out;
0461 
0462                 clear_bit(j, link->tid_map);
0463             }
0464 
0465             /* Clear the link state */
0466             link->match_field = NULL;
0467             link->link_handle = 0;
0468             memset(&link->fs, 0, sizeof(link->fs));
0469             break;
0470         }
0471     }
0472 
0473 out:
0474     return ret;
0475 }
0476 
0477 void cxgb4_cleanup_tc_u32(struct adapter *adap)
0478 {
0479     struct cxgb4_tc_u32_table *t;
0480     unsigned int i;
0481 
0482     if (!adap->tc_u32)
0483         return;
0484 
0485     /* Free up all allocated memory. */
0486     t = adap->tc_u32;
0487     for (i = 0; i < t->size; i++) {
0488         struct cxgb4_link *link = &t->table[i];
0489 
0490         kvfree(link->tid_map);
0491     }
0492     kvfree(adap->tc_u32);
0493 }
0494 
0495 struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap)
0496 {
0497     unsigned int max_tids = adap->tids.nftids + adap->tids.nhpftids;
0498     struct cxgb4_tc_u32_table *t;
0499     unsigned int i;
0500 
0501     if (!max_tids)
0502         return NULL;
0503 
0504     t = kvzalloc(struct_size(t, table, max_tids), GFP_KERNEL);
0505     if (!t)
0506         return NULL;
0507 
0508     t->size = max_tids;
0509 
0510     for (i = 0; i < t->size; i++) {
0511         struct cxgb4_link *link = &t->table[i];
0512         unsigned int bmap_size;
0513 
0514         bmap_size = BITS_TO_LONGS(max_tids);
0515         link->tid_map = kvcalloc(bmap_size, sizeof(unsigned long),
0516                      GFP_KERNEL);
0517         if (!link->tid_map)
0518             goto out_no_mem;
0519         bitmap_zero(link->tid_map, max_tids);
0520     }
0521 
0522     return t;
0523 
0524 out_no_mem:
0525     for (i = 0; i < t->size; i++) {
0526         struct cxgb4_link *link = &t->table[i];
0527         kvfree(link->tid_map);
0528     }
0529     kvfree(t);
0530 
0531     return NULL;
0532 }