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_data(const struct ethnl_req_info *req_base, 143 struct ethnl_reply_data *reply_base, 144 const struct genl_info *info) 145 { 146 struct rss_reply_data *data = RSS_REPDATA(reply_base); 147 struct rss_req_info *request = RSS_REQINFO(req_base); 148 struct net_device *dev = reply_base->dev; 149 const struct ethtool_ops *ops; 150 151 ops = dev->ethtool_ops; 152 if (!ops->get_rxfh) 153 return -EOPNOTSUPP; 154 155 /* Some drivers don't handle rss_context */ 156 if (request->rss_context) { 157 if (!ops->cap_rss_ctx_supported && !ops->create_rxfh_context) 158 return -EOPNOTSUPP; 159 160 return rss_prepare_ctx(request, dev, data, info); 161 } 162 163 return rss_prepare_get(request, dev, data, info); 164 } 165 166 static int 167 rss_reply_size(const struct ethnl_req_info *req_base, 168 const struct ethnl_reply_data *reply_base) 169 { 170 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 171 int len; 172 173 len = nla_total_size(sizeof(u32)) + /* _RSS_CONTEXT */ 174 nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */ 175 nla_total_size(sizeof(u32)) + /* _RSS_INPUT_XFRM */ 176 nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */ 177 nla_total_size(data->hkey_size); /* _RSS_HKEY */ 178 179 return len; 180 } 181 182 static int 183 rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base, 184 const struct ethnl_reply_data *reply_base) 185 { 186 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 187 struct rss_req_info *request = RSS_REQINFO(req_base); 188 189 if (request->rss_context && 190 nla_put_u32(skb, ETHTOOL_A_RSS_CONTEXT, request->rss_context)) 191 return -EMSGSIZE; 192 193 if ((data->indir_size && 194 nla_put(skb, ETHTOOL_A_RSS_INDIR, 195 sizeof(u32) * data->indir_size, data->indir_table))) 196 return -EMSGSIZE; 197 198 if (data->no_key_fields) 199 return 0; 200 201 if ((data->hfunc && 202 nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) || 203 (data->input_xfrm && 204 nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) || 205 (data->hkey_size && 206 nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))) 207 return -EMSGSIZE; 208 209 return 0; 210 } 211 212 static void rss_cleanup_data(struct ethnl_reply_data *reply_base) 213 { 214 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 215 216 kfree(data->indir_table); 217 } 218 219 struct rss_nl_dump_ctx { 220 unsigned long ifindex; 221 unsigned long ctx_idx; 222 223 /* User wants to only dump contexts from given ifindex */ 224 unsigned int match_ifindex; 225 unsigned int start_ctx; 226 }; 227 228 static struct rss_nl_dump_ctx *rss_dump_ctx(struct netlink_callback *cb) 229 { 230 NL_ASSERT_CTX_FITS(struct rss_nl_dump_ctx); 231 232 return (struct rss_nl_dump_ctx *)cb->ctx; 233 } 234 235 int ethnl_rss_dump_start(struct netlink_callback *cb) 236 { 237 const struct genl_info *info = genl_info_dump(cb); 238 struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb); 239 struct ethnl_req_info req_info = {}; 240 struct nlattr **tb = info->attrs; 241 int ret; 242 243 /* Filtering by context not supported */ 244 if (tb[ETHTOOL_A_RSS_CONTEXT]) { 245 NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT]); 246 return -EINVAL; 247 } 248 if (tb[ETHTOOL_A_RSS_START_CONTEXT]) { 249 ctx->start_ctx = nla_get_u32(tb[ETHTOOL_A_RSS_START_CONTEXT]); 250 ctx->ctx_idx = ctx->start_ctx; 251 } 252 253 ret = ethnl_parse_header_dev_get(&req_info, 254 tb[ETHTOOL_A_RSS_HEADER], 255 sock_net(cb->skb->sk), cb->extack, 256 false); 257 if (req_info.dev) { 258 ctx->match_ifindex = req_info.dev->ifindex; 259 ctx->ifindex = ctx->match_ifindex; 260 ethnl_parse_header_dev_put(&req_info); 261 req_info.dev = NULL; 262 } 263 264 return ret; 265 } 266 267 static int 268 rss_dump_one_ctx(struct sk_buff *skb, struct netlink_callback *cb, 269 struct net_device *dev, u32 rss_context) 270 { 271 const struct genl_info *info = genl_info_dump(cb); 272 struct rss_reply_data data = {}; 273 struct rss_req_info req = {}; 274 void *ehdr; 275 int ret; 276 277 req.rss_context = rss_context; 278 279 ehdr = ethnl_dump_put(skb, cb, ETHTOOL_MSG_RSS_GET_REPLY); 280 if (!ehdr) 281 return -EMSGSIZE; 282 283 ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_RSS_HEADER); 284 if (ret < 0) 285 goto err_cancel; 286 287 /* Context 0 is not currently storred or cached in the XArray */ 288 if (!rss_context) 289 ret = rss_prepare_get(&req, dev, &data, info); 290 else 291 ret = rss_prepare_ctx(&req, dev, &data, info); 292 if (ret) 293 goto err_cancel; 294 295 ret = rss_fill_reply(skb, &req.base, &data.base); 296 if (ret) 297 goto err_cleanup; 298 genlmsg_end(skb, ehdr); 299 300 rss_cleanup_data(&data.base); 301 return 0; 302 303 err_cleanup: 304 rss_cleanup_data(&data.base); 305 err_cancel: 306 genlmsg_cancel(skb, ehdr); 307 return ret; 308 } 309 310 static int 311 rss_dump_one_dev(struct sk_buff *skb, struct netlink_callback *cb, 312 struct net_device *dev) 313 { 314 struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb); 315 int ret; 316 317 if (!dev->ethtool_ops->get_rxfh) 318 return 0; 319 320 if (!ctx->ctx_idx) { 321 ret = rss_dump_one_ctx(skb, cb, dev, 0); 322 if (ret) 323 return ret; 324 ctx->ctx_idx++; 325 } 326 327 for (; xa_find(&dev->ethtool->rss_ctx, &ctx->ctx_idx, 328 ULONG_MAX, XA_PRESENT); ctx->ctx_idx++) { 329 ret = rss_dump_one_ctx(skb, cb, dev, ctx->ctx_idx); 330 if (ret) 331 return ret; 332 } 333 ctx->ctx_idx = ctx->start_ctx; 334 335 return 0; 336 } 337 338 int ethnl_rss_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 339 { 340 struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb); 341 struct net *net = sock_net(skb->sk); 342 struct net_device *dev; 343 int ret = 0; 344 345 rtnl_lock(); 346 for_each_netdev_dump(net, dev, ctx->ifindex) { 347 if (ctx->match_ifindex && ctx->match_ifindex != ctx->ifindex) 348 break; 349 350 netdev_lock_ops(dev); 351 ret = rss_dump_one_dev(skb, cb, dev); 352 netdev_unlock_ops(dev); 353 if (ret) 354 break; 355 } 356 rtnl_unlock(); 357 358 return ret; 359 } 360 361 const struct ethnl_request_ops ethnl_rss_request_ops = { 362 .request_cmd = ETHTOOL_MSG_RSS_GET, 363 .reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY, 364 .hdr_attr = ETHTOOL_A_RSS_HEADER, 365 .req_info_size = sizeof(struct rss_req_info), 366 .reply_data_size = sizeof(struct rss_reply_data), 367 368 .parse_request = rss_parse_request, 369 .prepare_data = rss_prepare_data, 370 .reply_size = rss_reply_size, 371 .fill_reply = rss_fill_reply, 372 .cleanup_data = rss_cleanup_data, 373 }; 374