1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include "netlink.h" 4 #include "common.h" 5 6 struct coalesce_req_info { 7 struct ethnl_req_info base; 8 }; 9 10 struct coalesce_reply_data { 11 struct ethnl_reply_data base; 12 struct ethtool_coalesce coalesce; 13 u32 supported_params; 14 }; 15 16 #define COALESCE_REPDATA(__reply_base) \ 17 container_of(__reply_base, struct coalesce_reply_data, base) 18 19 #define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS 20 static u32 attr_to_mask(unsigned int attr_type) 21 { 22 return BIT(attr_type - __SUPPORTED_OFFSET); 23 } 24 25 /* build time check that indices in ethtool_ops::supported_coalesce_params 26 * match corresponding attribute types with an offset 27 */ 28 #define __CHECK_SUPPORTED_OFFSET(x) \ 29 static_assert((ETHTOOL_ ## x) == \ 30 BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET)) 31 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS); 32 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES); 33 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ); 34 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ); 35 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS); 36 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES); 37 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ); 38 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ); 39 __CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS); 40 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX); 41 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX); 42 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW); 43 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW); 44 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW); 45 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW); 46 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW); 47 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH); 48 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH); 49 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH); 50 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH); 51 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH); 52 __CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL); 53 54 const struct nla_policy ethnl_coalesce_get_policy[] = { 55 [ETHTOOL_A_COALESCE_HEADER] = 56 NLA_POLICY_NESTED(ethnl_header_policy), 57 }; 58 59 static int coalesce_prepare_data(const struct ethnl_req_info *req_base, 60 struct ethnl_reply_data *reply_base, 61 struct genl_info *info) 62 { 63 struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); 64 struct net_device *dev = reply_base->dev; 65 int ret; 66 67 if (!dev->ethtool_ops->get_coalesce) 68 return -EOPNOTSUPP; 69 data->supported_params = dev->ethtool_ops->supported_coalesce_params; 70 ret = ethnl_ops_begin(dev); 71 if (ret < 0) 72 return ret; 73 ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce); 74 ethnl_ops_complete(dev); 75 76 return ret; 77 } 78 79 static int coalesce_reply_size(const struct ethnl_req_info *req_base, 80 const struct ethnl_reply_data *reply_base) 81 { 82 return nla_total_size(sizeof(u32)) + /* _RX_USECS */ 83 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES */ 84 nla_total_size(sizeof(u32)) + /* _RX_USECS_IRQ */ 85 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_IRQ */ 86 nla_total_size(sizeof(u32)) + /* _TX_USECS */ 87 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES */ 88 nla_total_size(sizeof(u32)) + /* _TX_USECS_IRQ */ 89 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_IRQ */ 90 nla_total_size(sizeof(u32)) + /* _STATS_BLOCK_USECS */ 91 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_RX */ 92 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_TX */ 93 nla_total_size(sizeof(u32)) + /* _PKT_RATE_LOW */ 94 nla_total_size(sizeof(u32)) + /* _RX_USECS_LOW */ 95 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_LOW */ 96 nla_total_size(sizeof(u32)) + /* _TX_USECS_LOW */ 97 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_LOW */ 98 nla_total_size(sizeof(u32)) + /* _PKT_RATE_HIGH */ 99 nla_total_size(sizeof(u32)) + /* _RX_USECS_HIGH */ 100 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */ 101 nla_total_size(sizeof(u32)) + /* _TX_USECS_HIGH */ 102 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */ 103 nla_total_size(sizeof(u32)); /* _RATE_SAMPLE_INTERVAL */ 104 } 105 106 static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val, 107 u32 supported_params) 108 { 109 if (!val && !(supported_params & attr_to_mask(attr_type))) 110 return false; 111 return nla_put_u32(skb, attr_type, val); 112 } 113 114 static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val, 115 u32 supported_params) 116 { 117 if (!val && !(supported_params & attr_to_mask(attr_type))) 118 return false; 119 return nla_put_u8(skb, attr_type, !!val); 120 } 121 122 static int coalesce_fill_reply(struct sk_buff *skb, 123 const struct ethnl_req_info *req_base, 124 const struct ethnl_reply_data *reply_base) 125 { 126 const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); 127 const struct ethtool_coalesce *coal = &data->coalesce; 128 u32 supported = data->supported_params; 129 130 if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS, 131 coal->rx_coalesce_usecs, supported) || 132 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES, 133 coal->rx_max_coalesced_frames, supported) || 134 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ, 135 coal->rx_coalesce_usecs_irq, supported) || 136 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, 137 coal->rx_max_coalesced_frames_irq, supported) || 138 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS, 139 coal->tx_coalesce_usecs, supported) || 140 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES, 141 coal->tx_max_coalesced_frames, supported) || 142 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ, 143 coal->tx_coalesce_usecs_irq, supported) || 144 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, 145 coal->tx_max_coalesced_frames_irq, supported) || 146 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, 147 coal->stats_block_coalesce_usecs, supported) || 148 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, 149 coal->use_adaptive_rx_coalesce, supported) || 150 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, 151 coal->use_adaptive_tx_coalesce, supported) || 152 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW, 153 coal->pkt_rate_low, supported) || 154 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW, 155 coal->rx_coalesce_usecs_low, supported) || 156 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, 157 coal->rx_max_coalesced_frames_low, supported) || 158 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW, 159 coal->tx_coalesce_usecs_low, supported) || 160 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, 161 coal->tx_max_coalesced_frames_low, supported) || 162 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH, 163 coal->pkt_rate_high, supported) || 164 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH, 165 coal->rx_coalesce_usecs_high, supported) || 166 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, 167 coal->rx_max_coalesced_frames_high, supported) || 168 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH, 169 coal->tx_coalesce_usecs_high, supported) || 170 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, 171 coal->tx_max_coalesced_frames_high, supported) || 172 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, 173 coal->rate_sample_interval, supported)) 174 return -EMSGSIZE; 175 176 return 0; 177 } 178 179 const struct ethnl_request_ops ethnl_coalesce_request_ops = { 180 .request_cmd = ETHTOOL_MSG_COALESCE_GET, 181 .reply_cmd = ETHTOOL_MSG_COALESCE_GET_REPLY, 182 .hdr_attr = ETHTOOL_A_COALESCE_HEADER, 183 .req_info_size = sizeof(struct coalesce_req_info), 184 .reply_data_size = sizeof(struct coalesce_reply_data), 185 186 .prepare_data = coalesce_prepare_data, 187 .reply_size = coalesce_reply_size, 188 .fill_reply = coalesce_fill_reply, 189 }; 190 191 /* COALESCE_SET */ 192 193 const struct nla_policy ethnl_coalesce_set_policy[] = { 194 [ETHTOOL_A_COALESCE_HEADER] = 195 NLA_POLICY_NESTED(ethnl_header_policy), 196 [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_U32 }, 197 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_U32 }, 198 [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_U32 }, 199 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 200 [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_U32 }, 201 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_U32 }, 202 [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_U32 }, 203 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 204 [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_U32 }, 205 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_U8 }, 206 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_U8 }, 207 [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_U32 }, 208 [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_U32 }, 209 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 210 [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_U32 }, 211 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 212 [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_U32 }, 213 [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_U32 }, 214 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 215 [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 }, 216 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 217 [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 }, 218 }; 219 220 int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info) 221 { 222 struct ethtool_coalesce coalesce = {}; 223 struct ethnl_req_info req_info = {}; 224 struct nlattr **tb = info->attrs; 225 const struct ethtool_ops *ops; 226 struct net_device *dev; 227 u32 supported_params; 228 bool mod = false; 229 int ret; 230 u16 a; 231 232 ret = ethnl_parse_header_dev_get(&req_info, 233 tb[ETHTOOL_A_COALESCE_HEADER], 234 genl_info_net(info), info->extack, 235 true); 236 if (ret < 0) 237 return ret; 238 dev = req_info.dev; 239 ops = dev->ethtool_ops; 240 ret = -EOPNOTSUPP; 241 if (!ops->get_coalesce || !ops->set_coalesce) 242 goto out_dev; 243 244 /* make sure that only supported parameters are present */ 245 supported_params = ops->supported_coalesce_params; 246 for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++) 247 if (tb[a] && !(supported_params & attr_to_mask(a))) { 248 ret = -EINVAL; 249 NL_SET_ERR_MSG_ATTR(info->extack, tb[a], 250 "cannot modify an unsupported parameter"); 251 goto out_dev; 252 } 253 254 rtnl_lock(); 255 ret = ethnl_ops_begin(dev); 256 if (ret < 0) 257 goto out_rtnl; 258 ret = ops->get_coalesce(dev, &coalesce); 259 if (ret < 0) 260 goto out_ops; 261 262 ethnl_update_u32(&coalesce.rx_coalesce_usecs, 263 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod); 264 ethnl_update_u32(&coalesce.rx_max_coalesced_frames, 265 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod); 266 ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq, 267 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod); 268 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq, 269 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod); 270 ethnl_update_u32(&coalesce.tx_coalesce_usecs, 271 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod); 272 ethnl_update_u32(&coalesce.tx_max_coalesced_frames, 273 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod); 274 ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq, 275 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod); 276 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq, 277 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod); 278 ethnl_update_u32(&coalesce.stats_block_coalesce_usecs, 279 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod); 280 ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce, 281 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod); 282 ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce, 283 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod); 284 ethnl_update_u32(&coalesce.pkt_rate_low, 285 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod); 286 ethnl_update_u32(&coalesce.rx_coalesce_usecs_low, 287 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod); 288 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low, 289 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod); 290 ethnl_update_u32(&coalesce.tx_coalesce_usecs_low, 291 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod); 292 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low, 293 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod); 294 ethnl_update_u32(&coalesce.pkt_rate_high, 295 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod); 296 ethnl_update_u32(&coalesce.rx_coalesce_usecs_high, 297 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod); 298 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high, 299 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod); 300 ethnl_update_u32(&coalesce.tx_coalesce_usecs_high, 301 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod); 302 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high, 303 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod); 304 ethnl_update_u32(&coalesce.rate_sample_interval, 305 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod); 306 ret = 0; 307 if (!mod) 308 goto out_ops; 309 310 ret = dev->ethtool_ops->set_coalesce(dev, &coalesce); 311 if (ret < 0) 312 goto out_ops; 313 ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL); 314 315 out_ops: 316 ethnl_ops_complete(dev); 317 out_rtnl: 318 rtnl_unlock(); 319 out_dev: 320 dev_put(dev); 321 return ret; 322 } 323