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