^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) #include "netlink.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) #include "common.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) struct pause_req_info {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) struct ethnl_req_info base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) struct pause_reply_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) struct ethnl_reply_data base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) struct ethtool_pauseparam pauseparam;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) struct ethtool_pause_stats pausestat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #define PAUSE_REPDATA(__reply_base) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) container_of(__reply_base, struct pause_reply_data, base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) const struct nla_policy ethnl_pause_get_policy[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) [ETHTOOL_A_PAUSE_HEADER] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) NLA_POLICY_NESTED(ethnl_header_policy_stats),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static void ethtool_stats_init(u64 *stats, unsigned int n)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) while (n--)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) stats[n] = ETHTOOL_STAT_NOT_SET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) static int pause_prepare_data(const struct ethnl_req_info *req_base,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) struct ethnl_reply_data *reply_base,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct genl_info *info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct pause_reply_data *data = PAUSE_REPDATA(reply_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct net_device *dev = reply_base->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) if (!dev->ethtool_ops->get_pauseparam)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) return -EOPNOTSUPP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) ethtool_stats_init((u64 *)&data->pausestat,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) sizeof(data->pausestat) / 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) ret = ethnl_ops_begin(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) dev->ethtool_ops->get_pauseparam(dev, &data->pauseparam);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) if (req_base->flags & ETHTOOL_FLAG_STATS &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) dev->ethtool_ops->get_pause_stats)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) dev->ethtool_ops->get_pause_stats(dev, &data->pausestat);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) ethnl_ops_complete(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) static int pause_reply_size(const struct ethnl_req_info *req_base,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) const struct ethnl_reply_data *reply_base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) int n = nla_total_size(sizeof(u8)) + /* _PAUSE_AUTONEG */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) nla_total_size(sizeof(u8)) + /* _PAUSE_RX */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) nla_total_size(sizeof(u8)); /* _PAUSE_TX */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) if (req_base->flags & ETHTOOL_FLAG_STATS)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) n += nla_total_size(0) + /* _PAUSE_STATS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) nla_total_size_64bit(sizeof(u64)) * ETHTOOL_PAUSE_STAT_CNT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) return n;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) static int ethtool_put_stat(struct sk_buff *skb, u64 val, u16 attrtype,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) u16 padtype)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) if (val == ETHTOOL_STAT_NOT_SET)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) if (nla_put_u64_64bit(skb, attrtype, val, padtype))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) return -EMSGSIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) static int pause_put_stats(struct sk_buff *skb,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) const struct ethtool_pause_stats *pause_stats)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) const u16 pad = ETHTOOL_A_PAUSE_STAT_PAD;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) struct nlattr *nest;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) nest = nla_nest_start(skb, ETHTOOL_A_PAUSE_STATS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) if (!nest)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) return -EMSGSIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) if (ethtool_put_stat(skb, pause_stats->tx_pause_frames,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) ETHTOOL_A_PAUSE_STAT_TX_FRAMES, pad) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) ethtool_put_stat(skb, pause_stats->rx_pause_frames,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) ETHTOOL_A_PAUSE_STAT_RX_FRAMES, pad))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) goto err_cancel;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) nla_nest_end(skb, nest);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) err_cancel:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) nla_nest_cancel(skb, nest);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) return -EMSGSIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) static int pause_fill_reply(struct sk_buff *skb,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) const struct ethnl_req_info *req_base,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) const struct ethnl_reply_data *reply_base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) const struct pause_reply_data *data = PAUSE_REPDATA(reply_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) const struct ethtool_pauseparam *pauseparam = &data->pauseparam;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) if (nla_put_u8(skb, ETHTOOL_A_PAUSE_AUTONEG, !!pauseparam->autoneg) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) nla_put_u8(skb, ETHTOOL_A_PAUSE_RX, !!pauseparam->rx_pause) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) nla_put_u8(skb, ETHTOOL_A_PAUSE_TX, !!pauseparam->tx_pause))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return -EMSGSIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) if (req_base->flags & ETHTOOL_FLAG_STATS &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) pause_put_stats(skb, &data->pausestat))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) return -EMSGSIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) const struct ethnl_request_ops ethnl_pause_request_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) .request_cmd = ETHTOOL_MSG_PAUSE_GET,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) .reply_cmd = ETHTOOL_MSG_PAUSE_GET_REPLY,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) .hdr_attr = ETHTOOL_A_PAUSE_HEADER,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) .req_info_size = sizeof(struct pause_req_info),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) .reply_data_size = sizeof(struct pause_reply_data),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) .prepare_data = pause_prepare_data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) .reply_size = pause_reply_size,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) .fill_reply = pause_fill_reply,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) /* PAUSE_SET */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) const struct nla_policy ethnl_pause_set_policy[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) [ETHTOOL_A_PAUSE_HEADER] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) NLA_POLICY_NESTED(ethnl_header_policy),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) [ETHTOOL_A_PAUSE_AUTONEG] = { .type = NLA_U8 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) [ETHTOOL_A_PAUSE_RX] = { .type = NLA_U8 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) [ETHTOOL_A_PAUSE_TX] = { .type = NLA_U8 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) struct ethtool_pauseparam params = {};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) struct ethnl_req_info req_info = {};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) struct nlattr **tb = info->attrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) const struct ethtool_ops *ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) struct net_device *dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) bool mod = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) ret = ethnl_parse_header_dev_get(&req_info,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) tb[ETHTOOL_A_PAUSE_HEADER],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) genl_info_net(info), info->extack,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) dev = req_info.dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) ops = dev->ethtool_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) ret = -EOPNOTSUPP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) if (!ops->get_pauseparam || !ops->set_pauseparam)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) goto out_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) rtnl_lock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) ret = ethnl_ops_begin(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) goto out_rtnl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) ops->get_pauseparam(dev, ¶ms);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) ethnl_update_bool32(¶ms.autoneg, tb[ETHTOOL_A_PAUSE_AUTONEG], &mod);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) ethnl_update_bool32(¶ms.rx_pause, tb[ETHTOOL_A_PAUSE_RX], &mod);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) ethnl_update_bool32(¶ms.tx_pause, tb[ETHTOOL_A_PAUSE_TX], &mod);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) if (!mod)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) goto out_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) ret = dev->ethtool_ops->set_pauseparam(dev, ¶ms);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) goto out_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) ethtool_notify(dev, ETHTOOL_MSG_PAUSE_NTF, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) out_ops:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) ethnl_ops_complete(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) out_rtnl:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) rtnl_unlock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) out_dev:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) dev_put(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) }