switchdev(二)

void br_set_state(struct net_bridge_port *p, unsigned int state)
{
    struct switchdev_attr attr = {
        .orig_dev = p->dev,
        .id = SWITCHDEV_ATTR_ID_PORT_STP_STATE,
        .flags = SWITCHDEV_F_DEFER,
        .u.stp_state = state,
    };
    int err;

    p->state = state;
    err = switchdev_port_attr_set(p->dev, &attr);
    if (err && err != -EOPNOTSUPP)
        br_warn(p->br, "error setting offload STP state on port %u(%s)\n",
                (unsigned int) p->port_no, p->dev->name);
    else
        br_info(p->br, "port %u(%s) entered %s state\n",
                (unsigned int) p->port_no, p->dev->name,
                br_port_state_names[p->state]);
}

 

 

/* Caller must hold RTNL. */
int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
{
    struct trie *t = (struct trie *)tb->tb_data;
    struct fib_alias *fa, *new_fa;
    struct key_vector *l, *tp;
    struct fib_info *fi;
    u8 plen = cfg->fc_dst_len;
    u8 slen = KEYLENGTH - plen;
    u8 tos = cfg->fc_tos;
    u32 key;
    int err;

    if (plen > KEYLENGTH)
        return -EINVAL;

    key = ntohl(cfg->fc_dst);

    pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen);

    if ((plen < KEYLENGTH) && (key << plen))
        return -EINVAL;

    fi = fib_create_info(cfg);
    if (IS_ERR(fi)) {
        err = PTR_ERR(fi);
        goto err;
    }

    l = fib_find_node(t, &tp, key);
    fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority) : NULL;

    /* Now fa, if non-NULL, points to the first fib alias
     * with the same keys [prefix,tos,priority], if such key already
     * exists or to the node before which we will insert new one.
     *
     * If fa is NULL, we will need to allocate a new one and
     * insert to the tail of the section matching the suffix length
     * of the new alias.
     */

    if (fa && fa->fa_tos == tos &&
        fa->fa_info->fib_priority == fi->fib_priority) {
        struct fib_alias *fa_first, *fa_match;

        err = -EEXIST;
        if (cfg->fc_nlflags & NLM_F_EXCL)
            goto out;

        /* We have 2 goals:
         * 1. Find exact match for type, scope, fib_info to avoid
         * duplicate routes
         * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
         */
        fa_match = NULL;
        fa_first = fa;
        hlist_for_each_entry_from(fa, fa_list) {
            if ((fa->fa_slen != slen) || (fa->fa_tos != tos))
                break;
            if (fa->fa_info->fib_priority != fi->fib_priority)
                break;
            if (fa->fa_type == cfg->fc_type &&
                fa->fa_info == fi) {
                fa_match = fa;
                break;
            }
        }

        if (cfg->fc_nlflags & NLM_F_REPLACE) {
            struct fib_info *fi_drop;
            u8 state;

            fa = fa_first;
            if (fa_match) {
                if (fa == fa_match)
                    err = 0;
                goto out;
            }
            err = -ENOBUFS;
            new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
            if (new_fa == NULL)
                goto out;

            fi_drop = fa->fa_info;
            new_fa->fa_tos = fa->fa_tos;
            new_fa->fa_info = fi;
            new_fa->fa_type = cfg->fc_type;
            state = fa->fa_state;
            new_fa->fa_state = state & ~FA_S_ACCESSED;
            new_fa->fa_slen = fa->fa_slen;

            err = netdev_switch_fib_ipv4_add(key, plen, fi,
                             new_fa->fa_tos,
                             cfg->fc_type,
                             cfg->fc_nlflags,
                             tb->tb_id);
            if (err) {
                netdev_switch_fib_ipv4_abort(fi);
                kmem_cache_free(fn_alias_kmem, new_fa);
                goto out;
            }

            hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list);

            alias_free_mem_rcu(fa);

            fib_release_info(fi_drop);
            if (state & FA_S_ACCESSED)
                rt_cache_flush(cfg->fc_nlinfo.nl_net);
            rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
                tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE);

            goto succeeded;
        }
        /* Error if we find a perfect match which
         * uses the same scope, type, and nexthop
         * information.
         */
        if (fa_match)
            goto out;

        if (!(cfg->fc_nlflags & NLM_F_APPEND))
            fa = fa_first;
    }
    err = -ENOENT;
    if (!(cfg->fc_nlflags & NLM_F_CREATE))
        goto out;

    err = -ENOBUFS;
    new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
    if (new_fa == NULL)
        goto out;

    new_fa->fa_info = fi;
    new_fa->fa_tos = tos;
    new_fa->fa_type = cfg->fc_type;
    new_fa->fa_state = 0;
    new_fa->fa_slen = slen;

    /* (Optionally) offload fib entry to switch hardware. */
    err = netdev_switch_fib_ipv4_add(key, plen, fi, tos,
                     cfg->fc_type,
                     cfg->fc_nlflags,
                     tb->tb_id);
    if (err) {
        netdev_switch_fib_ipv4_abort(fi);
        goto out_free_new_fa;
    }

    /* Insert new entry to the list. */
    err = fib_insert_alias(t, tp, l, new_fa, fa, key);
    if (err)
        goto out_sw_fib_del;

    if (!plen)
        tb->tb_num_default++;

    rt_cache_flush(cfg->fc_nlinfo.nl_net);
    rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,
          &cfg->fc_nlinfo, 0);
succeeded:
    return 0;

out_sw_fib_del:
    netdev_switch_fib_ipv4_del(key, plen, fi, tos, cfg->fc_type, tb->tb_id);
out_free_new_fa:
    kmem_cache_free(fn_alias_kmem, new_fa);
out:
    fib_release_info(fi);
err:
    return err;
}

 

/**
 *    netdev_switch_fib_ipv4_add - Add IPv4 route entry to switch
 *
 *    @dst: route's IPv4 destination address
 *    @dst_len: destination address length (prefix length)
 *    @fi: route FIB info structure
 *    @tos: route TOS
 *    @type: route type
 *    @nlflags: netlink flags passed in (NLM_F_*)
 *    @tb_id: route table ID
 *
 *    Add IPv4 route entry to switch device.
 */
int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
                   u8 tos, u8 type, u32 nlflags, u32 tb_id)
{
    struct net_device *dev;
    const struct swdev_ops *ops;
    int err = 0;

    /* Don't offload route if using custom ip rules or if
     * IPv4 FIB offloading has been disabled completely.
     */

#ifdef CONFIG_IP_MULTIPLE_TABLES
    if (fi->fib_net->ipv4.fib_has_custom_rules)
        return 0;
#endif

    if (fi->fib_net->ipv4.fib_offload_disabled)
        return 0;

    dev = netdev_switch_get_dev_by_nhs(fi);
    if (!dev)
        return 0;
    ops = dev->swdev_ops;

    if (ops->swdev_fib_ipv4_add) {
        err = ops->swdev_fib_ipv4_add(dev, htonl(dst), dst_len,
                          fi, tos, type, nlflags,
                          tb_id);
        if (!err)
            fi->fib_flags |= RTNH_F_OFFLOAD;
    }

    return err;
}
EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_add);

 

posted on 2020-05-06 12:03  tycoon3  阅读(642)  评论(0编辑  收藏  举报

导航