| @@ -1622,7 +1622,8 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd) · 1621 unmodified lines |
| 1622 | 1622 | N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN) |
| 1623 | 1623 | N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO) |
| 1624 | 1624 | N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) |
| 1625 | | − N(PRE_CHANGEADDR) |
| 1625 | + N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE) |
| 1626 | + N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA) |
| 1626 | 1627 | } |
| 1627 | 1628 | #undef N |
| 1628 | 1629 | return "UNKNOWN_NETDEV_EVENT"; |
| @@ -1939,6 +1940,32 @@ static int call_netdevice_notifiers_info(unsigned long val, · 310 unmodified lines |
| 1939 | 1940 | return raw_notifier_call_chain(&netdev_chain, val, info); |
| 1940 | 1941 | } |
| 1941 | 1942 | |
| 1943 | +/** |
| 1944 | + * call_netdevice_notifiers_info_robust - call per-netns notifier blocks |
| 1945 | + * for and rollback on error |
| 1946 | + * @val_up: value passed unmodified to notifier function |
| 1947 | + * @val_down: value passed unmodified to the notifier function when |
| 1948 | + * recovering from an error on @val_up |
| 1949 | + * @info: notifier information data |
| 1950 | + * |
| 1951 | + * Call all per-netns network notifier blocks, but not notifier blocks on |
| 1952 | + * the global notifier chain. Parameters and return value are as for |
| 1953 | + * raw_notifier_call_chain_robust(). |
| 1954 | + */ |
| 1955 | + |
| 1956 | +static int |
| 1957 | +call_netdevice_notifiers_info_robust(unsigned long val_up, |
| 1958 | + unsigned long val_down, |
| 1959 | + struct netdev_notifier_info *info) |
| 1960 | +{ |
| 1961 | + struct net *net = dev_net(info->dev); |
| 1962 | + |
| 1963 | + ASSERT_RTNL(); |
| 1964 | + |
| 1965 | + return raw_notifier_call_chain_robust(&net->netdev_chain, |
| 1966 | + val_up, val_down, info); |
| 1967 | +} |
| 1968 | + |
| 1942 | 1969 | static int call_netdevice_notifiers_extack(unsigned long val, |
| 1943 | 1970 | struct net_device *dev, |
| 1944 | 1971 | struct netlink_ext_ack *extack) |
| @@ -7728,6 +7755,242 @@ void netdev_bonding_info_change(struct net_device *dev, · 5783 unmodified lines |
| 7728 | 7755 | } |
| 7729 | 7756 | EXPORT_SYMBOL(netdev_bonding_info_change); |
| 7730 | 7757 | |
| 7758 | +static int netdev_offload_xstats_enable_l3(struct net_device *dev, |
| 7759 | + struct netlink_ext_ack *extack) |
| 7760 | +{ |
| 7761 | + struct netdev_notifier_offload_xstats_info info = { |
| 7762 | + .info.dev = dev, |
| 7763 | + .info.extack = extack, |
| 7764 | + .type = NETDEV_OFFLOAD_XSTATS_TYPE_L3, |
| 7765 | + }; |
| 7766 | + int err; |
| 7767 | + int rc; |
| 7768 | + |
| 7769 | + dev->offload_xstats_l3 = kzalloc(sizeof(*dev->offload_xstats_l3), |
| 7770 | + GFP_KERNEL); |
| 7771 | + if (!dev->offload_xstats_l3) |
| 7772 | + return -ENOMEM; |
| 7773 | + |
| 7774 | + rc = call_netdevice_notifiers_info_robust(NETDEV_OFFLOAD_XSTATS_ENABLE, |
| 7775 | + NETDEV_OFFLOAD_XSTATS_DISABLE, |
| 7776 | + &info.info); |
| 7777 | + err = notifier_to_errno(rc); |
| 7778 | + if (err) |
| 7779 | + goto free_stats; |
| 7780 | + |
| 7781 | + return 0; |
| 7782 | + |
| 7783 | +free_stats: |
| 7784 | + kfree(dev->offload_xstats_l3); |
| 7785 | + dev->offload_xstats_l3 = NULL; |
| 7786 | + return err; |
| 7787 | +} |
| 7788 | + |
| 7789 | +int netdev_offload_xstats_enable(struct net_device *dev, |
| 7790 | + enum netdev_offload_xstats_type type, |
| 7791 | + struct netlink_ext_ack *extack) |
| 7792 | +{ |
| 7793 | + ASSERT_RTNL(); |
| 7794 | + |
| 7795 | + if (netdev_offload_xstats_enabled(dev, type)) |
| 7796 | + return -EALREADY; |
| 7797 | + |
| 7798 | + switch (type) { |
| 7799 | + case NETDEV_OFFLOAD_XSTATS_TYPE_L3: |
| 7800 | + return netdev_offload_xstats_enable_l3(dev, extack); |
| 7801 | + } |
| 7802 | + |
| 7803 | + WARN_ON(1); |
| 7804 | + return -EINVAL; |
| 7805 | +} |
| 7806 | +EXPORT_SYMBOL(netdev_offload_xstats_enable); |
| 7807 | + |
| 7808 | +static void netdev_offload_xstats_disable_l3(struct net_device *dev) |
| 7809 | +{ |
| 7810 | + struct netdev_notifier_offload_xstats_info info = { |
| 7811 | + .info.dev = dev, |
| 7812 | + .type = NETDEV_OFFLOAD_XSTATS_TYPE_L3, |
| 7813 | + }; |
| 7814 | + |
| 7815 | + call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_DISABLE, |
| 7816 | + &info.info); |
| 7817 | + kfree(dev->offload_xstats_l3); |
| 7818 | + dev->offload_xstats_l3 = NULL; |
| 7819 | +} |
| 7820 | + |
| 7821 | +int netdev_offload_xstats_disable(struct net_device *dev, |
| 7822 | + enum netdev_offload_xstats_type type) |
| 7823 | +{ |
| 7824 | + ASSERT_RTNL(); |
| 7825 | + |
| 7826 | + if (!netdev_offload_xstats_enabled(dev, type)) |
| 7827 | + return -EALREADY; |
| 7828 | + |
| 7829 | + switch (type) { |
| 7830 | + case NETDEV_OFFLOAD_XSTATS_TYPE_L3: |
| 7831 | + netdev_offload_xstats_disable_l3(dev); |
| 7832 | + return 0; |
| 7833 | + } |
| 7834 | + |
| 7835 | + WARN_ON(1); |
| 7836 | + return -EINVAL; |
| 7837 | +} |
| 7838 | +EXPORT_SYMBOL(netdev_offload_xstats_disable); |
| 7839 | + |
| 7840 | +static void netdev_offload_xstats_disable_all(struct net_device *dev) |
| 7841 | +{ |
| 7842 | + netdev_offload_xstats_disable(dev, NETDEV_OFFLOAD_XSTATS_TYPE_L3); |
| 7843 | +} |
| 7844 | + |
| 7845 | +static struct rtnl_hw_stats64 * |
| 7846 | +netdev_offload_xstats_get_ptr(const struct net_device *dev, |
| 7847 | + enum netdev_offload_xstats_type type) |
| 7848 | +{ |
| 7849 | + switch (type) { |
| 7850 | + case NETDEV_OFFLOAD_XSTATS_TYPE_L3: |
| 7851 | + return dev->offload_xstats_l3; |
| 7852 | + } |
| 7853 | + |
| 7854 | + WARN_ON(1); |
| 7855 | + return NULL; |
| 7856 | +} |
| 7857 | + |
| 7858 | +bool netdev_offload_xstats_enabled(const struct net_device *dev, |
| 7859 | + enum netdev_offload_xstats_type type) |
| 7860 | +{ |
| 7861 | + ASSERT_RTNL(); |
| 7862 | + |
| 7863 | + return netdev_offload_xstats_get_ptr(dev, type); |
| 7864 | +} |
| 7865 | +EXPORT_SYMBOL(netdev_offload_xstats_enabled); |
| 7866 | + |
| 7867 | +struct netdev_notifier_offload_xstats_ru { |
| 7868 | + bool used; |
| 7869 | +}; |
| 7870 | + |
| 7871 | +struct netdev_notifier_offload_xstats_rd { |
| 7872 | + struct rtnl_hw_stats64 stats; |
| 7873 | + bool used; |
| 7874 | +}; |
| 7875 | + |
| 7876 | +static void netdev_hw_stats64_add(struct rtnl_hw_stats64 *dest, |
| 7877 | + const struct rtnl_hw_stats64 *src) |
| 7878 | +{ |
| 7879 | + dest->rx_packets += src->rx_packets; |
| 7880 | + dest->tx_packets += src->tx_packets; |
| 7881 | + dest->rx_bytes += src->rx_bytes; |
| 7882 | + dest->tx_bytes += src->tx_bytes; |
| 7883 | + dest->rx_errors += src->rx_errors; |
| 7884 | + dest->tx_errors += src->tx_errors; |
| 7885 | + dest->rx_dropped += src->rx_dropped; |
| 7886 | + dest->tx_dropped += src->tx_dropped; |
| 7887 | + dest->multicast += src->multicast; |
| 7888 | +} |
| 7889 | + |
| 7890 | +static int netdev_offload_xstats_get_used(struct net_device *dev, |
| 7891 | + enum netdev_offload_xstats_type type, |
| 7892 | + bool *p_used, |
| 7893 | + struct netlink_ext_ack *extack) |
| 7894 | +{ |
| 7895 | + struct netdev_notifier_offload_xstats_ru report_used = {}; |
| 7896 | + struct netdev_notifier_offload_xstats_info info = { |
| 7897 | + .info.dev = dev, |
| 7898 | + .info.extack = extack, |
| 7899 | + .type = type, |
| 7900 | + .report_used = &report_used, |
| 7901 | + }; |
| 7902 | + int rc; |
| 7903 | + |
| 7904 | + WARN_ON(!netdev_offload_xstats_enabled(dev, type)); |
| 7905 | + rc = call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_REPORT_USED, |
| 7906 | + &info.info); |
| 7907 | + *p_used = report_used.used; |
| 7908 | + return notifier_to_errno(rc); |
| 7909 | +} |
| 7910 | + |
| 7911 | +static int netdev_offload_xstats_get_stats(struct net_device *dev, |
| 7912 | + enum netdev_offload_xstats_type type, |
| 7913 | + struct rtnl_hw_stats64 *p_stats, |
| 7914 | + bool *p_used, |
| 7915 | + struct netlink_ext_ack *extack) |
| 7916 | +{ |
| 7917 | + struct netdev_notifier_offload_xstats_rd report_delta = {}; |
| 7918 | + struct netdev_notifier_offload_xstats_info info = { |
| 7919 | + .info.dev = dev, |
| 7920 | + .info.extack = extack, |
| 7921 | + .type = type, |
| 7922 | + .report_delta = &report_delta, |
| 7923 | + }; |
| 7924 | + struct rtnl_hw_stats64 *stats; |
| 7925 | + int rc; |
| 7926 | + |
| 7927 | + stats = netdev_offload_xstats_get_ptr(dev, type); |
| 7928 | + if (WARN_ON(!stats)) |
| 7929 | + return -EINVAL; |
| 7930 | + |
| 7931 | + rc = call_netdevice_notifiers_info(NETDEV_OFFLOAD_XSTATS_REPORT_DELTA, |
| 7932 | + &info.info); |
| 7933 | + |
| 7934 | + /* Cache whatever we got, even if there was an error, otherwise the |
| 7935 | + * successful stats retrievals would get lost. |
| 7936 | + */ |
| 7937 | + netdev_hw_stats64_add(stats, &report_delta.stats); |
| 7938 | + |
| 7939 | + if (p_stats) |
| 7940 | + *p_stats = *stats; |
| 7941 | + *p_used = report_delta.used; |
| 7942 | + |
| 7943 | + return notifier_to_errno(rc); |
| 7944 | +} |
| 7945 | + |
| 7946 | +int netdev_offload_xstats_get(struct net_device *dev, |
| 7947 | + enum netdev_offload_xstats_type type, |
| 7948 | + struct rtnl_hw_stats64 *p_stats, bool *p_used, |
| 7949 | + struct netlink_ext_ack *extack) |
| 7950 | +{ |
| 7951 | + ASSERT_RTNL(); |
| 7952 | + |
| 7953 | + if (p_stats) |
| 7954 | + return netdev_offload_xstats_get_stats(dev, type, p_stats, |
| 7955 | + p_used, extack); |
| 7956 | + else |
| 7957 | + return netdev_offload_xstats_get_used(dev, type, p_used, |
| 7958 | + extack); |
| 7959 | +} |
| 7960 | +EXPORT_SYMBOL(netdev_offload_xstats_get); |
| 7961 | + |
| 7962 | +void |
| 7963 | +netdev_offload_xstats_report_delta(struct netdev_notifier_offload_xstats_rd *report_delta, |
| 7964 | + const struct rtnl_hw_stats64 *stats) |
| 7965 | +{ |
| 7966 | + report_delta->used = true; |
| 7967 | + netdev_hw_stats64_add(&report_delta->stats, stats); |
| 7968 | +} |
| 7969 | +EXPORT_SYMBOL(netdev_offload_xstats_report_delta); |
| 7970 | + |
| 7971 | +void |
| 7972 | +netdev_offload_xstats_report_used(struct netdev_notifier_offload_xstats_ru *report_used) |
| 7973 | +{ |
| 7974 | + report_used->used = true; |
| 7975 | +} |
| 7976 | +EXPORT_SYMBOL(netdev_offload_xstats_report_used); |
| 7977 | + |
| 7978 | +void netdev_offload_xstats_push_delta(struct net_device *dev, |
| 7979 | + enum netdev_offload_xstats_type type, |
| 7980 | + const struct rtnl_hw_stats64 *p_stats) |
| 7981 | +{ |
| 7982 | + struct rtnl_hw_stats64 *stats; |
| 7983 | + |
| 7984 | + ASSERT_RTNL(); |
| 7985 | + |
| 7986 | + stats = netdev_offload_xstats_get_ptr(dev, type); |
| 7987 | + if (WARN_ON(!stats)) |
| 7988 | + return; |
| 7989 | + |
| 7990 | + netdev_hw_stats64_add(stats, p_stats); |
| 7991 | +} |
| 7992 | +EXPORT_SYMBOL(netdev_offload_xstats_push_delta); |
| 7993 | + |
| 7731 | 7994 | /** |
| 7732 | 7995 | * netdev_get_xmit_slave - Get the xmit slave of master device |
| 7733 | 7996 | * @dev: device |
| @@ -10417,6 +10680,8 @@ void unregister_netdevice_many(struct list_head *head) · 2683 unmodified lines |
| 10417 | 10680 | |
| 10418 | 10681 | dev_xdp_uninstall(dev); |
| 10419 | 10682 | |
| 10683 | + netdev_offload_xstats_disable_all(dev); |
| 10684 | + |
| 10420 | 10685 | /* Notify protocols, that we are about to destroy |
| 10421 | 10686 | * this device. They should clean all the things. |
| 10422 | 10687 | */ |