0001
0002
0003 #include "netlink.h"
0004 #include "common.h"
0005
0006 struct coalesce_req_info {
0007 struct ethnl_req_info base;
0008 };
0009
0010 struct coalesce_reply_data {
0011 struct ethnl_reply_data base;
0012 struct ethtool_coalesce coalesce;
0013 struct kernel_ethtool_coalesce kernel_coalesce;
0014 u32 supported_params;
0015 };
0016
0017 #define COALESCE_REPDATA(__reply_base) \
0018 container_of(__reply_base, struct coalesce_reply_data, base)
0019
0020 #define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS
0021 static u32 attr_to_mask(unsigned int attr_type)
0022 {
0023 return BIT(attr_type - __SUPPORTED_OFFSET);
0024 }
0025
0026
0027
0028
0029 #define __CHECK_SUPPORTED_OFFSET(x) \
0030 static_assert((ETHTOOL_ ## x) == \
0031 BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET))
0032 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS);
0033 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES);
0034 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ);
0035 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ);
0036 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS);
0037 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES);
0038 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ);
0039 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ);
0040 __CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS);
0041 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX);
0042 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX);
0043 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW);
0044 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW);
0045 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW);
0046 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW);
0047 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW);
0048 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH);
0049 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH);
0050 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH);
0051 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH);
0052 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH);
0053 __CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL);
0054
0055 const struct nla_policy ethnl_coalesce_get_policy[] = {
0056 [ETHTOOL_A_COALESCE_HEADER] =
0057 NLA_POLICY_NESTED(ethnl_header_policy),
0058 };
0059
0060 static int coalesce_prepare_data(const struct ethnl_req_info *req_base,
0061 struct ethnl_reply_data *reply_base,
0062 struct genl_info *info)
0063 {
0064 struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
0065 struct netlink_ext_ack *extack = info ? info->extack : NULL;
0066 struct net_device *dev = reply_base->dev;
0067 int ret;
0068
0069 if (!dev->ethtool_ops->get_coalesce)
0070 return -EOPNOTSUPP;
0071 data->supported_params = dev->ethtool_ops->supported_coalesce_params;
0072 ret = ethnl_ops_begin(dev);
0073 if (ret < 0)
0074 return ret;
0075 ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce,
0076 &data->kernel_coalesce, extack);
0077 ethnl_ops_complete(dev);
0078
0079 return ret;
0080 }
0081
0082 static int coalesce_reply_size(const struct ethnl_req_info *req_base,
0083 const struct ethnl_reply_data *reply_base)
0084 {
0085 return nla_total_size(sizeof(u32)) +
0086 nla_total_size(sizeof(u32)) +
0087 nla_total_size(sizeof(u32)) +
0088 nla_total_size(sizeof(u32)) +
0089 nla_total_size(sizeof(u32)) +
0090 nla_total_size(sizeof(u32)) +
0091 nla_total_size(sizeof(u32)) +
0092 nla_total_size(sizeof(u32)) +
0093 nla_total_size(sizeof(u32)) +
0094 nla_total_size(sizeof(u8)) +
0095 nla_total_size(sizeof(u8)) +
0096 nla_total_size(sizeof(u32)) +
0097 nla_total_size(sizeof(u32)) +
0098 nla_total_size(sizeof(u32)) +
0099 nla_total_size(sizeof(u32)) +
0100 nla_total_size(sizeof(u32)) +
0101 nla_total_size(sizeof(u32)) +
0102 nla_total_size(sizeof(u32)) +
0103 nla_total_size(sizeof(u32)) +
0104 nla_total_size(sizeof(u32)) +
0105 nla_total_size(sizeof(u32)) +
0106 nla_total_size(sizeof(u32)) +
0107 nla_total_size(sizeof(u8)) +
0108 nla_total_size(sizeof(u8));
0109 }
0110
0111 static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val,
0112 u32 supported_params)
0113 {
0114 if (!val && !(supported_params & attr_to_mask(attr_type)))
0115 return false;
0116 return nla_put_u32(skb, attr_type, val);
0117 }
0118
0119 static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val,
0120 u32 supported_params)
0121 {
0122 if (!val && !(supported_params & attr_to_mask(attr_type)))
0123 return false;
0124 return nla_put_u8(skb, attr_type, !!val);
0125 }
0126
0127 static int coalesce_fill_reply(struct sk_buff *skb,
0128 const struct ethnl_req_info *req_base,
0129 const struct ethnl_reply_data *reply_base)
0130 {
0131 const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
0132 const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce;
0133 const struct ethtool_coalesce *coal = &data->coalesce;
0134 u32 supported = data->supported_params;
0135
0136 if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS,
0137 coal->rx_coalesce_usecs, supported) ||
0138 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES,
0139 coal->rx_max_coalesced_frames, supported) ||
0140 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ,
0141 coal->rx_coalesce_usecs_irq, supported) ||
0142 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ,
0143 coal->rx_max_coalesced_frames_irq, supported) ||
0144 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS,
0145 coal->tx_coalesce_usecs, supported) ||
0146 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES,
0147 coal->tx_max_coalesced_frames, supported) ||
0148 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ,
0149 coal->tx_coalesce_usecs_irq, supported) ||
0150 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ,
0151 coal->tx_max_coalesced_frames_irq, supported) ||
0152 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS,
0153 coal->stats_block_coalesce_usecs, supported) ||
0154 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX,
0155 coal->use_adaptive_rx_coalesce, supported) ||
0156 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX,
0157 coal->use_adaptive_tx_coalesce, supported) ||
0158 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW,
0159 coal->pkt_rate_low, supported) ||
0160 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW,
0161 coal->rx_coalesce_usecs_low, supported) ||
0162 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW,
0163 coal->rx_max_coalesced_frames_low, supported) ||
0164 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW,
0165 coal->tx_coalesce_usecs_low, supported) ||
0166 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW,
0167 coal->tx_max_coalesced_frames_low, supported) ||
0168 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH,
0169 coal->pkt_rate_high, supported) ||
0170 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH,
0171 coal->rx_coalesce_usecs_high, supported) ||
0172 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH,
0173 coal->rx_max_coalesced_frames_high, supported) ||
0174 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH,
0175 coal->tx_coalesce_usecs_high, supported) ||
0176 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH,
0177 coal->tx_max_coalesced_frames_high, supported) ||
0178 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL,
0179 coal->rate_sample_interval, supported) ||
0180 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_TX,
0181 kcoal->use_cqe_mode_tx, supported) ||
0182 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_RX,
0183 kcoal->use_cqe_mode_rx, supported))
0184 return -EMSGSIZE;
0185
0186 return 0;
0187 }
0188
0189 const struct ethnl_request_ops ethnl_coalesce_request_ops = {
0190 .request_cmd = ETHTOOL_MSG_COALESCE_GET,
0191 .reply_cmd = ETHTOOL_MSG_COALESCE_GET_REPLY,
0192 .hdr_attr = ETHTOOL_A_COALESCE_HEADER,
0193 .req_info_size = sizeof(struct coalesce_req_info),
0194 .reply_data_size = sizeof(struct coalesce_reply_data),
0195
0196 .prepare_data = coalesce_prepare_data,
0197 .reply_size = coalesce_reply_size,
0198 .fill_reply = coalesce_fill_reply,
0199 };
0200
0201
0202
0203 const struct nla_policy ethnl_coalesce_set_policy[] = {
0204 [ETHTOOL_A_COALESCE_HEADER] =
0205 NLA_POLICY_NESTED(ethnl_header_policy),
0206 [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_U32 },
0207 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_U32 },
0208 [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_U32 },
0209 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_U32 },
0210 [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_U32 },
0211 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_U32 },
0212 [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_U32 },
0213 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_U32 },
0214 [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_U32 },
0215 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_U8 },
0216 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_U8 },
0217 [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_U32 },
0218 [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_U32 },
0219 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_U32 },
0220 [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_U32 },
0221 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_U32 },
0222 [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_U32 },
0223 [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_U32 },
0224 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 },
0225 [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 },
0226 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 },
0227 [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 },
0228 [ETHTOOL_A_COALESCE_USE_CQE_MODE_TX] = NLA_POLICY_MAX(NLA_U8, 1),
0229 [ETHTOOL_A_COALESCE_USE_CQE_MODE_RX] = NLA_POLICY_MAX(NLA_U8, 1),
0230 };
0231
0232 int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info)
0233 {
0234 struct kernel_ethtool_coalesce kernel_coalesce = {};
0235 struct ethtool_coalesce coalesce = {};
0236 struct ethnl_req_info req_info = {};
0237 struct nlattr **tb = info->attrs;
0238 const struct ethtool_ops *ops;
0239 struct net_device *dev;
0240 u32 supported_params;
0241 bool mod = false;
0242 int ret;
0243 u16 a;
0244
0245 ret = ethnl_parse_header_dev_get(&req_info,
0246 tb[ETHTOOL_A_COALESCE_HEADER],
0247 genl_info_net(info), info->extack,
0248 true);
0249 if (ret < 0)
0250 return ret;
0251 dev = req_info.dev;
0252 ops = dev->ethtool_ops;
0253 ret = -EOPNOTSUPP;
0254 if (!ops->get_coalesce || !ops->set_coalesce)
0255 goto out_dev;
0256
0257
0258 supported_params = ops->supported_coalesce_params;
0259 for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++)
0260 if (tb[a] && !(supported_params & attr_to_mask(a))) {
0261 ret = -EINVAL;
0262 NL_SET_ERR_MSG_ATTR(info->extack, tb[a],
0263 "cannot modify an unsupported parameter");
0264 goto out_dev;
0265 }
0266
0267 rtnl_lock();
0268 ret = ethnl_ops_begin(dev);
0269 if (ret < 0)
0270 goto out_rtnl;
0271 ret = ops->get_coalesce(dev, &coalesce, &kernel_coalesce,
0272 info->extack);
0273 if (ret < 0)
0274 goto out_ops;
0275
0276 ethnl_update_u32(&coalesce.rx_coalesce_usecs,
0277 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod);
0278 ethnl_update_u32(&coalesce.rx_max_coalesced_frames,
0279 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod);
0280 ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq,
0281 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod);
0282 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq,
0283 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod);
0284 ethnl_update_u32(&coalesce.tx_coalesce_usecs,
0285 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod);
0286 ethnl_update_u32(&coalesce.tx_max_coalesced_frames,
0287 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod);
0288 ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq,
0289 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod);
0290 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq,
0291 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod);
0292 ethnl_update_u32(&coalesce.stats_block_coalesce_usecs,
0293 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod);
0294 ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce,
0295 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod);
0296 ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce,
0297 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod);
0298 ethnl_update_u32(&coalesce.pkt_rate_low,
0299 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod);
0300 ethnl_update_u32(&coalesce.rx_coalesce_usecs_low,
0301 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod);
0302 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low,
0303 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod);
0304 ethnl_update_u32(&coalesce.tx_coalesce_usecs_low,
0305 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod);
0306 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low,
0307 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod);
0308 ethnl_update_u32(&coalesce.pkt_rate_high,
0309 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod);
0310 ethnl_update_u32(&coalesce.rx_coalesce_usecs_high,
0311 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod);
0312 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high,
0313 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod);
0314 ethnl_update_u32(&coalesce.tx_coalesce_usecs_high,
0315 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod);
0316 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high,
0317 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod);
0318 ethnl_update_u32(&coalesce.rate_sample_interval,
0319 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod);
0320 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx,
0321 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod);
0322 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx,
0323 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &mod);
0324 ret = 0;
0325 if (!mod)
0326 goto out_ops;
0327
0328 ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce,
0329 info->extack);
0330 if (ret < 0)
0331 goto out_ops;
0332 ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL);
0333
0334 out_ops:
0335 ethnl_ops_complete(dev);
0336 out_rtnl:
0337 rtnl_unlock();
0338 out_dev:
0339 ethnl_parse_header_dev_put(&req_info);
0340 return ret;
0341 }