1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include "netlink.h" 4 #include "common.h" 5 6 struct rss_req_info { 7 struct ethnl_req_info base; 8 u32 rss_context; 9 }; 10 11 struct rss_reply_data { 12 struct ethnl_reply_data base; 13 bool no_key_fields; 14 u32 indir_size; 15 u32 hkey_size; 16 u32 hfunc; 17 u32 input_xfrm; 18 u32 *indir_table; 19 u8 *hkey; 20 }; 21 22 #define RSS_REQINFO(__req_base) \ 23 container_of(__req_base, struct rss_req_info, base) 24 25 #define RSS_REPDATA(__reply_base) \ 26 container_of(__reply_base, struct rss_reply_data, base) 27 28 const struct nla_policy ethnl_rss_get_policy[] = { 29 [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 30 [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32 }, 31 }; 32 33 static int 34 rss_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb, 35 struct netlink_ext_ack *extack) 36 { 37 struct rss_req_info *request = RSS_REQINFO(req_info); 38 39 if (tb[ETHTOOL_A_RSS_CONTEXT]) 40 request->rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]); 41 42 return 0; 43 } 44 45 static int 46 rss_prepare_get(const struct rss_req_info *request, struct net_device *dev, 47 struct rss_reply_data *data, const struct genl_info *info) 48 { 49 struct ethtool_rxfh_param rxfh = {}; 50 const struct ethtool_ops *ops; 51 u32 total_size, indir_bytes; 52 u8 *rss_config; 53 int ret; 54 55 ops = dev->ethtool_ops; 56 57 ret = ethnl_ops_begin(dev); 58 if (ret < 0) 59 return ret; 60 61 data->indir_size = 0; 62 data->hkey_size = 0; 63 if (ops->get_rxfh_indir_size) 64 data->indir_size = ops->get_rxfh_indir_size(dev); 65 if (ops->get_rxfh_key_size) 66 data->hkey_size = ops->get_rxfh_key_size(dev); 67 68 indir_bytes = data->indir_size * sizeof(u32); 69 total_size = indir_bytes + data->hkey_size; 70 rss_config = kzalloc(total_size, GFP_KERNEL); 71 if (!rss_config) { 72 ret = -ENOMEM; 73 goto out_ops; 74 } 75 76 if (data->indir_size) 77 data->indir_table = (u32 *)rss_config; 78 if (data->hkey_size) 79 data->hkey = rss_config + indir_bytes; 80 81 rxfh.indir_size = data->indir_size; 82 rxfh.indir = data->indir_table; 83 rxfh.key_size = data->hkey_size; 84 rxfh.key = data->hkey; 85 86 ret = ops->get_rxfh(dev, &rxfh); 87 if (ret) 88 goto out_ops; 89 90 data->hfunc = rxfh.hfunc; 91 data->input_xfrm = rxfh.input_xfrm; 92 out_ops: 93 ethnl_ops_complete(dev); 94 return ret; 95 } 96 97 static int 98 rss_prepare_ctx(const struct rss_req_info *request, struct net_device *dev, 99 struct rss_reply_data *data, const struct genl_info *info) 100 { 101 struct ethtool_rxfh_context *ctx; 102 u32 total_size, indir_bytes; 103 u8 *rss_config; 104 105 ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context); 106 if (!ctx) 107 return -ENOENT; 108 109 data->indir_size = ctx->indir_size; 110 data->hkey_size = ctx->key_size; 111 data->hfunc = ctx->hfunc; 112 data->input_xfrm = ctx->input_xfrm; 113 114 indir_bytes = data->indir_size * sizeof(u32); 115 total_size = indir_bytes + data->hkey_size; 116 rss_config = kzalloc(total_size, GFP_KERNEL); 117 if (!rss_config) 118 return -ENOMEM; 119 120 data->indir_table = (u32 *)rss_config; 121 memcpy(data->indir_table, ethtool_rxfh_context_indir(ctx), indir_bytes); 122 123 if (data->hkey_size) { 124 data->hkey = rss_config + indir_bytes; 125 memcpy(data->hkey, ethtool_rxfh_context_key(ctx), 126 data->hkey_size); 127 } 128 129 return 0; 130 } 131 132 static int 133 rss_prepare_data(const struct ethnl_req_info *req_base, 134 struct ethnl_reply_data *reply_base, 135 const struct genl_info *info) 136 { 137 struct rss_reply_data *data = RSS_REPDATA(reply_base); 138 struct rss_req_info *request = RSS_REQINFO(req_base); 139 struct net_device *dev = reply_base->dev; 140 const struct ethtool_ops *ops; 141 142 ops = dev->ethtool_ops; 143 if (!ops->get_rxfh) 144 return -EOPNOTSUPP; 145 146 /* Some drivers don't handle rss_context */ 147 if (request->rss_context) { 148 if (!ops->cap_rss_ctx_supported && !ops->create_rxfh_context) 149 return -EOPNOTSUPP; 150 151 data->no_key_fields = !ops->rxfh_per_ctx_key; 152 return rss_prepare_ctx(request, dev, data, info); 153 } 154 155 return rss_prepare_get(request, dev, data, info); 156 } 157 158 static int 159 rss_reply_size(const struct ethnl_req_info *req_base, 160 const struct ethnl_reply_data *reply_base) 161 { 162 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 163 int len; 164 165 len = nla_total_size(sizeof(u32)) + /* _RSS_CONTEXT */ 166 nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */ 167 nla_total_size(sizeof(u32)) + /* _RSS_INPUT_XFRM */ 168 nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */ 169 nla_total_size(data->hkey_size); /* _RSS_HKEY */ 170 171 return len; 172 } 173 174 static int 175 rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base, 176 const struct ethnl_reply_data *reply_base) 177 { 178 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 179 struct rss_req_info *request = RSS_REQINFO(req_base); 180 181 if (request->rss_context && 182 nla_put_u32(skb, ETHTOOL_A_RSS_CONTEXT, request->rss_context)) 183 return -EMSGSIZE; 184 185 if ((data->indir_size && 186 nla_put(skb, ETHTOOL_A_RSS_INDIR, 187 sizeof(u32) * data->indir_size, data->indir_table))) 188 return -EMSGSIZE; 189 190 if (data->no_key_fields) 191 return 0; 192 193 if ((data->hfunc && 194 nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) || 195 (data->input_xfrm && 196 nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) || 197 (data->hkey_size && 198 nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))) 199 return -EMSGSIZE; 200 201 return 0; 202 } 203 204 static void rss_cleanup_data(struct ethnl_reply_data *reply_base) 205 { 206 const struct rss_reply_data *data = RSS_REPDATA(reply_base); 207 208 kfree(data->indir_table); 209 } 210 211 const struct ethnl_request_ops ethnl_rss_request_ops = { 212 .request_cmd = ETHTOOL_MSG_RSS_GET, 213 .reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY, 214 .hdr_attr = ETHTOOL_A_RSS_HEADER, 215 .req_info_size = sizeof(struct rss_req_info), 216 .reply_data_size = sizeof(struct rss_reply_data), 217 218 .parse_request = rss_parse_request, 219 .prepare_data = rss_prepare_data, 220 .reply_size = rss_reply_size, 221 .fill_reply = rss_fill_reply, 222 .cleanup_data = rss_cleanup_data, 223 }; 224