--- zzzz-none-000/linux-5.15.111/net/openvswitch/vport-internal_dev.c 2023-05-11 14:00:40.000000000 +0000 +++ puma7-atom-6670-761/linux-5.15.111/net/openvswitch/vport-internal_dev.c 2024-02-07 10:23:30.000000000 +0000 @@ -3,6 +3,16 @@ * Copyright (c) 2007-2012 Nicira, Inc. */ +/* + * Includes Inango Systems Ltd’s changes/modifications dated: 2021. + * Changed/modified portions - Copyright (c) 2021 , Inango Systems Ltd. + */ + +/* + Includes MaxLinear's changes dated: 2021, 2022, 2023. + Changed portions - Copyright 2021-2023 MaxLinear, Inc. +*/ + #include #include #include @@ -17,6 +27,10 @@ #include "datapath.h" #include "vport-internal_dev.h" #include "vport-netdev.h" +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +#include +#endif + struct internal_dev { struct vport *vport; @@ -24,6 +38,10 @@ static struct vport_ops ovs_internal_vport_ops; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +static void do_setup(struct net_device *netdev); +#endif + static struct internal_dev *internal_dev_priv(struct net_device *netdev) { return netdev_priv(netdev); @@ -50,14 +68,48 @@ return NETDEV_TX_OK; } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +static int internal_dev_init(struct net_device *dev) +{ + struct vport *vport; + vport = ovs_internal_dev_get_vport(dev); + + if (ovs_dp_dev_init) + ovs_dp_dev_init(vport); + + return 0; +} + +static void internal_dev_uninit(struct net_device *dev) +{ + return; +} +#endif + static int internal_dev_open(struct net_device *netdev) { +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + struct vport *vport; + vport = ovs_internal_dev_get_vport(netdev); +#endif + netif_start_queue(netdev); +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + if (ovs_dp_dev_open) + ovs_dp_dev_open(vport); +#endif return 0; } static int internal_dev_stop(struct net_device *netdev) { +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + struct vport *vport; + vport = ovs_internal_dev_get_vport(netdev); + + if (ovs_dp_dev_stop) + ovs_dp_dev_stop(vport); +#endif netif_stop_queue(netdev); return 0; } @@ -73,6 +125,100 @@ .get_link = ethtool_op_get_link, }; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +static int internal_dev_change_mtu(struct net_device *dev, int new_mtu) +{ + const uint32_t flags_mask = OVS_VPORT_FLAG_SET_MTU_BY_USER | OVS_VPORT_FLAG_SET_MTU_AUTO; + uint32_t old_flags; + struct vport *vport; + int ret; + + if (new_mtu < ETH_MIN_MTU) { + net_err_ratelimited("%s: Invalid MTU %d requested, hw min %d\n", + dev->name, new_mtu, ETH_MIN_MTU); + return -EINVAL; + } + + if (new_mtu > ETH_MAX_MTU) { + net_err_ratelimited("%s: Invalid MTU %d requested, hw max %d\n", + dev->name, new_mtu, ETH_MAX_MTU); + return -EINVAL; + } + + if (ovs_dp_mtu_hook) { + vport = ovs_internal_dev_get_vport(dev); + old_flags = del_vport_flags(vport, flags_mask); + if (0 == (old_flags & flags_mask)) { + ret = ovs_dp_mtu_hook(dev, new_mtu); + if (ret < 0) { + printk(KERN_WARNING "internal_dev_change_mtu(dev=\"%s\", mtu=%d): error: ovs_dp_mtu_hook() -> %d\n", dev->name, new_mtu, ret); + return ret; + } + + return 0; + } + } + + if (ovs_dp_dev_set_mtu_set_by_user) + ovs_dp_dev_set_mtu_set_by_user(dev, (old_flags & OVS_VPORT_FLAG_SET_MTU_BY_USER) ? 1 : 0); + + dev->mtu = new_mtu; + return 0; +} + +static int internal_dev_do_ioctl(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd) +{ + if (ovs_dp_ioctl_hook) + return ovs_dp_ioctl_hook(dev, ifr, data, cmd); + + return -EOPNOTSUPP; +} + +static int internal_eth_mac_addr(struct net_device *dev, void *p) +{ + int ret; + unsigned char *sa_data = (unsigned char *)(((struct sockaddr *)p)->sa_data); + const uint32_t flags_mask = OVS_VPORT_FLAG_SET_MAC; + uint32_t old_flags; + struct vport *vport; + + #define MAC_FMT_ "\"%02x:%02x:%02x:%02x:%02x:%02x\"" + #define MAC_ARG_(X__) X__[0], X__[1], X__[2], X__[3], X__[4], X__[5] + + if (ovs_dp_mac_addr_hook) { + if (0 == memcmp(dev->dev_addr, ((struct sockaddr *)p)->sa_data, ETH_ALEN)) { + /* Same MAC - nothing to do */ + return 0; + } + + ret = eth_prepare_mac_addr_change(dev, p); + if (ret < 0) { + printk(KERN_WARNING "internal_eth_mac_addr(dev=\"%s\", mac=" MAC_FMT_ "): error: eth_prepare_mac_addr_change() -> %d\n", dev->name, MAC_ARG_(sa_data), ret); + return ret; + } + + vport = ovs_internal_dev_get_vport(dev); + old_flags = del_vport_flags(vport, flags_mask); + if (0 == (old_flags & flags_mask)) { + ret = ovs_dp_mac_addr_hook(dev, p); + } + + if (ret < 0) { + printk(KERN_WARNING "internal_eth_mac_addr(dev=\"%s\", mac=" MAC_FMT_ "): error: ovs_dp_mac_addr_hook() -> %d\n", dev->name, MAC_ARG_(sa_data), ret); + return ret; + } + + eth_commit_mac_addr_change(dev, p); + return 0; + } + + #undef MAC_FMT_ + #undef MAC_ARG_ + + return eth_mac_addr(dev, p); +} +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ + static void internal_dev_destructor(struct net_device *dev) { struct vport *vport = ovs_internal_dev_get_vport(dev); @@ -80,20 +226,190 @@ ovs_vport_free(vport); } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +static int internal_add_slave(struct net_device *br_dev, struct net_device *p_dev, struct netlink_ext_ack *ack) +{ + if (ovs_dp_add_del_port_hook) { + return ovs_dp_add_del_port_hook(br_dev, p_dev, 1); + } + return -EOPNOTSUPP; +} + +static int internal_del_slave(struct net_device *br_dev, struct net_device *p_dev) +{ + if (ovs_dp_add_del_port_hook) { + return ovs_dp_add_del_port_hook(br_dev, p_dev, 0); + } + return -EOPNOTSUPP; +} + +static int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags, struct netlink_ext_ack *ack) +{ + struct vport *vport; + + vport = ovs_netdev_get_vport(dev); + if (!vport) + return -EINVAL; + + if (ovs_dp_br_setlink_hook) { + return ovs_dp_br_setlink_hook(vport, dev, nlh, flags); + } + return -EOPNOTSUPP; +} + +static int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev, u32 filter_mask, int nlflags) +{ + struct vport *vport; + + vport = ovs_netdev_get_vport(dev); + if (!vport) + return 0; + + if (ovs_dp_br_fill_ifinfo_hook) { + return ovs_dp_br_fill_ifinfo_hook(vport, skb, dev, pid, seq, RTM_NEWLINK, nlflags); + } + return -EOPNOTSUPP; +} +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ + static const struct net_device_ops internal_dev_netdev_ops = { +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + .ndo_init = internal_dev_init, + .ndo_uninit = internal_dev_uninit, +#endif .ndo_open = internal_dev_open, .ndo_stop = internal_dev_stop, .ndo_start_xmit = internal_dev_xmit, +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + .ndo_siocdevprivate = internal_dev_do_ioctl, + .ndo_set_mac_address = internal_eth_mac_addr, + .ndo_change_mtu = internal_dev_change_mtu, +#else .ndo_set_mac_address = eth_mac_addr, +#endif .ndo_get_stats64 = dev_get_tstats64, +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + .ndo_add_slave = internal_add_slave, + .ndo_del_slave = internal_del_slave, + .ndo_bridge_setlink = br_setlink, + .ndo_bridge_getlink = br_getlink, +#endif }; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +static int br_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) +{ + int err; + + if (ovs_dp_br_brc_add_bridge_netlink_hook) + err = ovs_dp_br_brc_add_bridge_netlink_hook(src_net, dev); + else + err = -EOPNOTSUPP; + + return err; +} + +static int br_changelink(struct net_device *br_dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *ack) +{ + struct vport *vport; + + vport = ovs_internal_dev_get_vport(br_dev); + if (!vport) + return -EINVAL; + + if (ovs_dp_br_changelink_hook) { + return ovs_dp_br_changelink_hook(vport, tb, data); + } + return -EOPNOTSUPP; +} + +static void br_dellink(struct net_device *dev, struct list_head *head) +{ + struct vport *vport; + + vport = ovs_internal_dev_get_vport(dev); + if (!vport) + return; + + if (ovs_dp_br_brc_del_bridge_netlink_hook) + ovs_dp_br_brc_del_bridge_netlink_hook(dev_net(dev), dev); + +} + +static int br_port_slave_changelink(struct net_device *br_dev, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *ack) +{ + struct vport *vport; + + vport = ovs_netdev_get_vport(dev); + if (!vport) + return -EINVAL; + + if (ovs_dp_br_port_slave_changelink_hook) { + return ovs_dp_br_port_slave_changelink_hook(vport, br_dev, dev, tb, data, ack); + } + return -EOPNOTSUPP; +} + +static int br_fill_info(struct sk_buff *skb, const struct net_device *br_dev) +{ + struct vport *vport; + + vport = ovs_internal_dev_get_vport((struct net_device *)br_dev); + if (!vport) + return -EINVAL; + + if (ovs_dp_br_fill_info_hook) { + return ovs_dp_br_fill_info_hook(vport, skb, br_dev); + } + /* + If brcompat is not loaded, we consider that we don't support these methods. + For functions like br_fill_info / br_port_fill_slave_info any negative value indicates + that it wasn't possible to fill the nla structure, which leads to kernel panic. + We don't support this operation, which means that we don't need to write anything to nla; so we return 0 + */ + return 0; +} + +static int br_port_fill_slave_info(struct sk_buff *skb, const struct net_device *br_dev, const struct net_device *dev) +{ + struct vport *vport; + + vport = ovs_netdev_get_vport((struct net_device *)dev); + if (!vport) + return -EINVAL; + + if (ovs_dp_br_port_fill_slave_info_hook) { + return ovs_dp_br_port_fill_slave_info_hook(vport, skb, br_dev, dev); + } + /* + If brcompat is not loaded, we consider that we don't support these methods. + For functions like br_fill_info / br_port_fill_slave_info any negative value indicates + that it wasn't possible to fill the nla structure, which leads to kernel panic. + We don't support this operation, which means that we don't need to write anything to nla; so we return 0 + */ + return 0; +} +#endif /* CONFIG_OPENVSWITCH_BRCOMPAT */ + static struct rtnl_link_ops internal_dev_link_ops __read_mostly = { .kind = "openvswitch", +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + .setup = do_setup, + .newlink = br_newlink, + .changelink = br_changelink, + .dellink = br_dellink, + .slave_changelink = br_port_slave_changelink, + .fill_info = br_fill_info, + .fill_slave_info = br_port_fill_slave_info, +#endif }; static void do_setup(struct net_device *netdev) { +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + struct vport *vport; +#endif ether_setup(netdev); netdev->max_mtu = ETH_MAX_MTU; @@ -106,7 +422,15 @@ netdev->needs_free_netdev = true; netdev->priv_destructor = NULL; netdev->ethtool_ops = &internal_dev_ethtool_ops; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + vport = ovs_internal_dev_get_vport(netdev); + if (vport != NULL && vport->port_no != OVSP_LOCAL && ovs_dp_get_rtnl_link_ops_hook) + netdev->rtnl_link_ops = ovs_dp_get_rtnl_link_ops_hook(); + else + netdev->rtnl_link_ops = &internal_dev_link_ops; +#else netdev->rtnl_link_ops = &internal_dev_link_ops; +#endif netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | @@ -120,11 +444,24 @@ eth_hw_addr_random(netdev); } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +static int ovs_brcompat_bridge_setup(struct vport *vport){ + if (ovs_dp_br_bridge_setup) { + return ovs_dp_br_bridge_setup(vport, 1); + } + return -EOPNOTSUPP; +} +#endif + static struct vport *internal_dev_create(const struct vport_parms *parms) { struct vport *vport; struct internal_dev *internal_dev; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + struct net_device *netlink_dev = NULL; +#else struct net_device *dev; +#endif int err; vport = ovs_vport_alloc(0, &ovs_internal_vport_ops, parms); @@ -133,9 +470,24 @@ goto error; } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + /* OVSP_LOCAL is a datapath internal vport. + * net_device data should be allocated if the first device is created via netlink + */ + if (ovs_dp_br_get_netdev_hook) { + netlink_dev = ovs_dp_br_get_netdev_hook(); + } + + if (netlink_dev && (vport->port_no != OVSP_LOCAL)) + vport->dev = netlink_dev; + else + vport->dev = alloc_netdev(sizeof(struct internal_dev), + parms->name, NET_NAME_USER, do_setup); +#else dev = alloc_netdev(sizeof(struct internal_dev), parms->name, NET_NAME_USER, do_setup); vport->dev = dev; +#endif if (!vport->dev) { err = -ENOMEM; goto error_free_vport; @@ -153,6 +505,10 @@ /* Restrict bridge port to current netns. */ if (vport->port_no == OVSP_LOCAL) vport->dev->features |= NETIF_F_NETNS_LOCAL; +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + else + ovs_brcompat_bridge_setup(vport); +#endif rtnl_lock(); err = register_netdevice(vport->dev); @@ -168,9 +524,17 @@ error_unlock: rtnl_unlock(); +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + free_percpu(vport->dev->tstats); +#else free_percpu(dev->tstats); +#endif error_free_netdev: +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + free_netdev(vport->dev); +#else free_netdev(dev); +#endif error_free_vport: ovs_vport_free(vport); error: @@ -216,6 +580,17 @@ .type = OVS_VPORT_TYPE_INTERNAL, .create = internal_dev_create, .destroy = internal_dev_destroy, +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT + .set_addr = ovs_netdev_set_addr, + .get_name = ovs_netdev_get_name, + .get_addr = ovs_netdev_get_addr, + .get_kobj = ovs_netdev_get_kobj, + .get_dev_flags = ovs_netdev_get_dev_flags, + .is_running = ovs_netdev_is_running, + .get_operstate = ovs_netdev_get_operstate, + .get_ifindex = ovs_netdev_get_ifindex, + .get_mtu = ovs_netdev_get_mtu, +#endif .send = internal_dev_recv, }; @@ -231,6 +606,9 @@ return internal_dev_priv(netdev)->vport; } +#ifdef CONFIG_OPENVSWITCH_BRCOMPAT +EXPORT_SYMBOL(ovs_internal_dev_get_vport); +#endif int ovs_internal_dev_rtnl_link_register(void) {