Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 
0003 #include <linux/ethtool.h>
0004 
0005 #include "netlink.h"
0006 #include "common.h"
0007 #include "bitset.h"
0008 
0009 struct module_req_info {
0010     struct ethnl_req_info base;
0011 };
0012 
0013 struct module_reply_data {
0014     struct ethnl_reply_data base;
0015     struct ethtool_module_power_mode_params power;
0016 };
0017 
0018 #define MODULE_REPDATA(__reply_base) \
0019     container_of(__reply_base, struct module_reply_data, base)
0020 
0021 /* MODULE_GET */
0022 
0023 const struct nla_policy ethnl_module_get_policy[ETHTOOL_A_MODULE_HEADER + 1] = {
0024     [ETHTOOL_A_MODULE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
0025 };
0026 
0027 static int module_get_power_mode(struct net_device *dev,
0028                  struct module_reply_data *data,
0029                  struct netlink_ext_ack *extack)
0030 {
0031     const struct ethtool_ops *ops = dev->ethtool_ops;
0032 
0033     if (!ops->get_module_power_mode)
0034         return 0;
0035 
0036     return ops->get_module_power_mode(dev, &data->power, extack);
0037 }
0038 
0039 static int module_prepare_data(const struct ethnl_req_info *req_base,
0040                    struct ethnl_reply_data *reply_base,
0041                    struct genl_info *info)
0042 {
0043     struct module_reply_data *data = MODULE_REPDATA(reply_base);
0044     struct netlink_ext_ack *extack = info ? info->extack : NULL;
0045     struct net_device *dev = reply_base->dev;
0046     int ret;
0047 
0048     ret = ethnl_ops_begin(dev);
0049     if (ret < 0)
0050         return ret;
0051 
0052     ret = module_get_power_mode(dev, data, extack);
0053     if (ret < 0)
0054         goto out_complete;
0055 
0056 out_complete:
0057     ethnl_ops_complete(dev);
0058     return ret;
0059 }
0060 
0061 static int module_reply_size(const struct ethnl_req_info *req_base,
0062                  const struct ethnl_reply_data *reply_base)
0063 {
0064     struct module_reply_data *data = MODULE_REPDATA(reply_base);
0065     int len = 0;
0066 
0067     if (data->power.policy)
0068         len += nla_total_size(sizeof(u8));  /* _MODULE_POWER_MODE_POLICY */
0069 
0070     if (data->power.mode)
0071         len += nla_total_size(sizeof(u8));  /* _MODULE_POWER_MODE */
0072 
0073     return len;
0074 }
0075 
0076 static int module_fill_reply(struct sk_buff *skb,
0077                  const struct ethnl_req_info *req_base,
0078                  const struct ethnl_reply_data *reply_base)
0079 {
0080     const struct module_reply_data *data = MODULE_REPDATA(reply_base);
0081 
0082     if (data->power.policy &&
0083         nla_put_u8(skb, ETHTOOL_A_MODULE_POWER_MODE_POLICY,
0084                data->power.policy))
0085         return -EMSGSIZE;
0086 
0087     if (data->power.mode &&
0088         nla_put_u8(skb, ETHTOOL_A_MODULE_POWER_MODE, data->power.mode))
0089         return -EMSGSIZE;
0090 
0091     return 0;
0092 }
0093 
0094 const struct ethnl_request_ops ethnl_module_request_ops = {
0095     .request_cmd        = ETHTOOL_MSG_MODULE_GET,
0096     .reply_cmd      = ETHTOOL_MSG_MODULE_GET_REPLY,
0097     .hdr_attr       = ETHTOOL_A_MODULE_HEADER,
0098     .req_info_size      = sizeof(struct module_req_info),
0099     .reply_data_size    = sizeof(struct module_reply_data),
0100 
0101     .prepare_data       = module_prepare_data,
0102     .reply_size     = module_reply_size,
0103     .fill_reply     = module_fill_reply,
0104 };
0105 
0106 /* MODULE_SET */
0107 
0108 const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MODE_POLICY + 1] = {
0109     [ETHTOOL_A_MODULE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
0110     [ETHTOOL_A_MODULE_POWER_MODE_POLICY] =
0111         NLA_POLICY_RANGE(NLA_U8, ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH,
0112                  ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO),
0113 };
0114 
0115 static int module_set_power_mode(struct net_device *dev, struct nlattr **tb,
0116                  bool *p_mod, struct netlink_ext_ack *extack)
0117 {
0118     struct ethtool_module_power_mode_params power = {};
0119     struct ethtool_module_power_mode_params power_new;
0120     const struct ethtool_ops *ops = dev->ethtool_ops;
0121     int ret;
0122 
0123     if (!tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY])
0124         return 0;
0125 
0126     if (!ops->get_module_power_mode || !ops->set_module_power_mode) {
0127         NL_SET_ERR_MSG_ATTR(extack,
0128                     tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY],
0129                     "Setting power mode policy is not supported by this device");
0130         return -EOPNOTSUPP;
0131     }
0132 
0133     power_new.policy = nla_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]);
0134     ret = ops->get_module_power_mode(dev, &power, extack);
0135     if (ret < 0)
0136         return ret;
0137 
0138     if (power_new.policy == power.policy)
0139         return 0;
0140     *p_mod = true;
0141 
0142     return ops->set_module_power_mode(dev, &power_new, extack);
0143 }
0144 
0145 int ethnl_set_module(struct sk_buff *skb, struct genl_info *info)
0146 {
0147     struct ethnl_req_info req_info = {};
0148     struct nlattr **tb = info->attrs;
0149     struct net_device *dev;
0150     bool mod = false;
0151     int ret;
0152 
0153     ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_MODULE_HEADER],
0154                      genl_info_net(info), info->extack,
0155                      true);
0156     if (ret < 0)
0157         return ret;
0158     dev = req_info.dev;
0159 
0160     rtnl_lock();
0161     ret = ethnl_ops_begin(dev);
0162     if (ret < 0)
0163         goto out_rtnl;
0164 
0165     ret = module_set_power_mode(dev, tb, &mod, info->extack);
0166     if (ret < 0)
0167         goto out_ops;
0168 
0169     if (!mod)
0170         goto out_ops;
0171 
0172     ethtool_notify(dev, ETHTOOL_MSG_MODULE_NTF, NULL);
0173 
0174 out_ops:
0175     ethnl_ops_complete(dev);
0176 out_rtnl:
0177     rtnl_unlock();
0178     ethnl_parse_header_dev_put(&req_info);
0179     return ret;
0180 }