1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <net/netdev_lock.h> 4 5 #include "netlink.h" 6 #include "common.h" 7 8 struct rss_req_info { 9 struct ethnl_req_info base; 10 u32 rss_context; 11 }; 12 13 struct rss_reply_data { 14 struct ethnl_reply_data base; 15 bool no_key_fields; 16 u32 indir_size; 17 u32 hkey_size; 18 u32 hfunc; 19 u32 input_xfrm; 20 u32 *indir_table; 21 u8 *hkey; 22 }; 23 24 #define RSS_REQINFO(__req_base) \ 25 container_of(__req_base, struct rss_req_info, base) 26 27 #define RSS_REPDATA(__reply_base) \ 28 container_of(__reply_base, struct rss_reply_data, base) 29 30 const struct nla_policy ethnl_rss_get_policy[] = { 31 [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 32 [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32 }, 33 [ETHTOOL_A_RSS_START_CONTEXT] = { .type = NLA_U32 }, 34 }; 35 36 static int 37 rss_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb, 38 struct netlink_ext_ack *extack) 39 { 40 struct rss_req_info *request = RSS_REQINFO(req_info); 41 42 if (tb[ETHTOOL_A_RSS_CONTEXT]) 43 request->rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]); 44 if (tb[ETHTOOL_A_RSS_START_CONTEXT]) { 45 NL_SET_BAD_ATTR(extack, tb[ETHTOOL_A_RSS_START_CONTEXT]); 46 return -EINVAL; 47 } 48 49 return 0; 50 } 51 52 static int 53 rss_prepare_get(const struct rss_req_info *request, struct net_device *dev, 54 struct rss_reply_data *data, const struct genl_info *info) 55 { 56 struct ethtool_rxfh_param rxfh = {}; 57 const struct ethtool_ops *ops; 58 u32 total_size, indir_bytes; 59 u8 *rss_config; 60 int ret; 61 62 ops = dev->ethtool_ops; 63 64 ret = ethnl_ops_begin(dev); 65 if (ret < 0) 66 return ret; 67 68 data->indir_size = 0; 69 data->hkey_size = 0; 70 if (ops->get_rxfh_indir_size) 71 data->indir_size = ops->get_rxfh_indir_size(dev); 72 if (ops->get_rxfh_key_size) 73 data->hkey_size = ops->get_rxfh_key_size(dev); 74 75 indir_bytes = data->indir_size * sizeof(u32); 76 total_size = indir_bytes + data->hkey_size; 77 rss_config = kzalloc(total_size, GFP_KERNEL); 78 if (!rss_config) { 79 ret = -ENOMEM; 80 goto out_ops; 81 } 82 83 if (data->indir_size) 84 data->indir_table = (u32 *)rss_config; 85 if (data->hkey_size) 86 data->hkey = rss_config + indir_bytes; 87 88 rxfh.indir_size = data->indir_size; 89 rxfh.indir = data->indir_table; 90 rxfh.key_size = data->hkey_size; 91 rxfh.key = data->hkey; 92 93 ret = ops->get_rxfh(dev, &rxfh); 94 if (ret) 95 goto out_ops; 96 97 data->hfunc = rxfh.hfunc; 98 data->input_xfrm = rxfh.input_xfrm; 99 out_ops: 100 ethnl_ops_complete(dev); 101 return ret; 102 } 103 104 static int 105 rss_prepare_ctx(const struct rss_req_info *request, struct net_device *dev, 106 struct rss_reply_data *data, const struct genl_info *info) 107 { 108 struct ethtool_rxfh_context *ctx; 109 u32 total_size, indir_bytes; 110 u8 *rss_config; 111 112 data->no_key_fields = !dev->ethtool_ops->rxfh_per_ctx_key; 113 114 ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context); 115 if (!ctx) 116 return -ENOENT; 117 118 data->indir_size = ctx->indir_size; 119 data->hkey_size = ctx->key_size; 120 data->hfunc = ctx->hfunc; 121 data->input_xfrm = ctx->input_xfrm; 122 123 indir_bytes = data->indir_size * sizeof(u32); 124 total_size = indir_bytes + data->hkey_size; 125 rss_config = kzalloc(total_size, GFP_KERNEL); 126 if (!rss_config) 127 return -ENOMEM; 128 129 data->indir_table = (u32 *)rss_config; 130 memcpy(data->indir_table, ethtool_rxfh_context_indir(ctx), indir_bytes); 131 132 if (data->hkey_size) { 133 data->hkey = rss_config + indir_bytes; 134 memcpy(data->hkey, ethtool_rxfh_context_key(ctx), 135 data->hkey_size); 136 } 137 138 return 0; 139 } 140 141 static int 142 rss_prepare(const struct rss_req_info *request, struct net_device *dev, 143 struct rss_reply_data *data, const struct genl_info *info) 144 { 145 if (request->rss_context) 146 return rss_prepare_ctx(request, dev, data, info); 147 return rss_prepare_get(request, dev, data, info); 148 } 149 150 static int 151 rss_prepare_data(const struct ethnl_req_info *req_base, 152 struct ethnl_reply_data *reply_base, 153 const struct genl_info *info) 154 { 155 struct rss_reply_data *data = RSS_REPDATA(reply_base); 156 struct rss_req_info *request = RSS_REQINFO(req_base); 157 struct net_device *dev = reply_base->dev; 158 const struct ethtool_ops *ops; 159 int ret; 160 161 ops = dev->ethtool_ops; 162 if (!ops->get_rxfh) 163 return -EOPNOTSUPP; 164 165 /* Some drivers don't handle rss_context */ 166 if (request->rss_context && 167 !ops->cap_rss_ctx_supported && !ops->create_rxfh_context) 168 return -EOPNOTSUPP; 169 170 mutex_lock(&dev->ethtool->rss_lock); 171 ret = rss_prepare(request, dev, data, info); 172 mutex_unlock(&dev->ethtool->rss_lock); 173 174 return ret; 175 } 176 177 static int 178 rss_reply_size(const struct ethnl_req_info *req_base, 179 const struct ethnl_reply_data *reply_base) 180 { 181 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 182 int len; 183 184 len = nla_total_size(sizeof(u32)) + /* _RSS_CONTEXT */ 185 nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */ 186 nla_total_size(sizeof(u32)) + /* _RSS_INPUT_XFRM */ 187 nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */ 188 nla_total_size(data->hkey_size); /* _RSS_HKEY */ 189 190 return len; 191 } 192 193 static int 194 rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base, 195 const struct ethnl_reply_data *reply_base) 196 { 197 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 198 struct rss_req_info *request = RSS_REQINFO(req_base); 199 200 if (request->rss_context && 201 nla_put_u32(skb, ETHTOOL_A_RSS_CONTEXT, request->rss_context)) 202 return -EMSGSIZE; 203 204 if ((data->indir_size && 205 nla_put(skb, ETHTOOL_A_RSS_INDIR, 206 sizeof(u32) * data->indir_size, data->indir_table))) 207 return -EMSGSIZE; 208 209 if (data->no_key_fields) 210 return 0; 211 212 if ((data->hfunc && 213 nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) || 214 (data->input_xfrm && 215 nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) || 216 (data->hkey_size && 217 nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))) 218 return -EMSGSIZE; 219 220 return 0; 221 } 222 223 static void rss_cleanup_data(struct ethnl_reply_data *reply_base) 224 { 225 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 226 227 kfree(data->indir_table); 228 } 229 230 struct rss_nl_dump_ctx { 231 unsigned long ifindex; 232 unsigned long ctx_idx; 233 234 /* User wants to only dump contexts from given ifindex */ 235 unsigned int match_ifindex; 236 unsigned int start_ctx; 237 }; 238 239 static struct rss_nl_dump_ctx *rss_dump_ctx(struct netlink_callback *cb) 240 { 241 NL_ASSERT_CTX_FITS(struct rss_nl_dump_ctx); 242 243 return (struct rss_nl_dump_ctx *)cb->ctx; 244 } 245 246 int ethnl_rss_dump_start(struct netlink_callback *cb) 247 { 248 const struct genl_info *info = genl_info_dump(cb); 249 struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb); 250 struct ethnl_req_info req_info = {}; 251 struct nlattr **tb = info->attrs; 252 int ret; 253 254 /* Filtering by context not supported */ 255 if (tb[ETHTOOL_A_RSS_CONTEXT]) { 256 NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT]); 257 return -EINVAL; 258 } 259 if (tb[ETHTOOL_A_RSS_START_CONTEXT]) { 260 ctx->start_ctx = nla_get_u32(tb[ETHTOOL_A_RSS_START_CONTEXT]); 261 ctx->ctx_idx = ctx->start_ctx; 262 } 263 264 ret = ethnl_parse_header_dev_get(&req_info, 265 tb[ETHTOOL_A_RSS_HEADER], 266 sock_net(cb->skb->sk), cb->extack, 267 false); 268 if (req_info.dev) { 269 ctx->match_ifindex = req_info.dev->ifindex; 270 ctx->ifindex = ctx->match_ifindex; 271 ethnl_parse_header_dev_put(&req_info); 272 req_info.dev = NULL; 273 } 274 275 return ret; 276 } 277 278 static int 279 rss_dump_one_ctx(struct sk_buff *skb, struct netlink_callback *cb, 280 struct net_device *dev, u32 rss_context) 281 { 282 const struct genl_info *info = genl_info_dump(cb); 283 struct rss_reply_data data = {}; 284 struct rss_req_info req = {}; 285 void *ehdr; 286 int ret; 287 288 req.rss_context = rss_context; 289 290 ehdr = ethnl_dump_put(skb, cb, ETHTOOL_MSG_RSS_GET_REPLY); 291 if (!ehdr) 292 return -EMSGSIZE; 293 294 ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_RSS_HEADER); 295 if (ret < 0) 296 goto err_cancel; 297 298 /* Context 0 is not currently storred or cached in the XArray */ 299 if (!rss_context) 300 ret = rss_prepare_get(&req, dev, &data, info); 301 else 302 ret = rss_prepare_ctx(&req, dev, &data, info); 303 if (ret) 304 goto err_cancel; 305 306 ret = rss_fill_reply(skb, &req.base, &data.base); 307 if (ret) 308 goto err_cleanup; 309 genlmsg_end(skb, ehdr); 310 311 rss_cleanup_data(&data.base); 312 return 0; 313 314 err_cleanup: 315 rss_cleanup_data(&data.base); 316 err_cancel: 317 genlmsg_cancel(skb, ehdr); 318 return ret; 319 } 320 321 static int 322 rss_dump_one_dev(struct sk_buff *skb, struct netlink_callback *cb, 323 struct net_device *dev) 324 { 325 struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb); 326 int ret; 327 328 if (!dev->ethtool_ops->get_rxfh) 329 return 0; 330 331 if (!ctx->ctx_idx) { 332 ret = rss_dump_one_ctx(skb, cb, dev, 0); 333 if (ret) 334 return ret; 335 ctx->ctx_idx++; 336 } 337 338 for (; xa_find(&dev->ethtool->rss_ctx, &ctx->ctx_idx, 339 ULONG_MAX, XA_PRESENT); ctx->ctx_idx++) { 340 ret = rss_dump_one_ctx(skb, cb, dev, ctx->ctx_idx); 341 if (ret) 342 return ret; 343 } 344 ctx->ctx_idx = ctx->start_ctx; 345 346 return 0; 347 } 348 349 int ethnl_rss_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 350 { 351 struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb); 352 struct net *net = sock_net(skb->sk); 353 struct net_device *dev; 354 int ret = 0; 355 356 rtnl_lock(); 357 for_each_netdev_dump(net, dev, ctx->ifindex) { 358 if (ctx->match_ifindex && ctx->match_ifindex != ctx->ifindex) 359 break; 360 361 netdev_lock_ops(dev); 362 ret = rss_dump_one_dev(skb, cb, dev); 363 netdev_unlock_ops(dev); 364 if (ret) 365 break; 366 } 367 rtnl_unlock(); 368 369 return ret; 370 } 371 372 /* RSS_NTF */ 373 374 void ethtool_rss_notify(struct net_device *dev, u32 rss_context) 375 { 376 struct rss_req_info req_info = { 377 .rss_context = rss_context, 378 }; 379 380 ethnl_notify(dev, ETHTOOL_MSG_RSS_NTF, &req_info.base); 381 } 382 383 const struct ethnl_request_ops ethnl_rss_request_ops = { 384 .request_cmd = ETHTOOL_MSG_RSS_GET, 385 .reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY, 386 .hdr_attr = ETHTOOL_A_RSS_HEADER, 387 .req_info_size = sizeof(struct rss_req_info), 388 .reply_data_size = sizeof(struct rss_reply_data), 389 390 .parse_request = rss_parse_request, 391 .prepare_data = rss_prepare_data, 392 .reply_size = rss_reply_size, 393 .fill_reply = rss_fill_reply, 394 .cleanup_data = rss_cleanup_data, 395 }; 396