at v5.5-rc3 305 lines 7.3 kB view raw
1/* 2 * Copyright (c) 2018 Cumulus Networks. All rights reserved. 3 * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com> 4 * 5 * This software is licensed under the GNU General License Version 2, 6 * June 1991 as shown in the file COPYING in the top-level directory of this 7 * source tree. 8 * 9 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" 10 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 11 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 12 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 13 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 14 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 15 */ 16 17#include <net/fib_notifier.h> 18#include <net/ip_fib.h> 19#include <net/ip6_fib.h> 20#include <net/fib_rules.h> 21#include <net/net_namespace.h> 22 23#include "netdevsim.h" 24 25struct nsim_fib_entry { 26 u64 max; 27 u64 num; 28}; 29 30struct nsim_per_fib_data { 31 struct nsim_fib_entry fib; 32 struct nsim_fib_entry rules; 33}; 34 35struct nsim_fib_data { 36 struct notifier_block fib_nb; 37 struct nsim_per_fib_data ipv4; 38 struct nsim_per_fib_data ipv6; 39}; 40 41u64 nsim_fib_get_val(struct nsim_fib_data *fib_data, 42 enum nsim_resource_id res_id, bool max) 43{ 44 struct nsim_fib_entry *entry; 45 46 switch (res_id) { 47 case NSIM_RESOURCE_IPV4_FIB: 48 entry = &fib_data->ipv4.fib; 49 break; 50 case NSIM_RESOURCE_IPV4_FIB_RULES: 51 entry = &fib_data->ipv4.rules; 52 break; 53 case NSIM_RESOURCE_IPV6_FIB: 54 entry = &fib_data->ipv6.fib; 55 break; 56 case NSIM_RESOURCE_IPV6_FIB_RULES: 57 entry = &fib_data->ipv6.rules; 58 break; 59 default: 60 return 0; 61 } 62 63 return max ? entry->max : entry->num; 64} 65 66static void nsim_fib_set_max(struct nsim_fib_data *fib_data, 67 enum nsim_resource_id res_id, u64 val) 68{ 69 struct nsim_fib_entry *entry; 70 71 switch (res_id) { 72 case NSIM_RESOURCE_IPV4_FIB: 73 entry = &fib_data->ipv4.fib; 74 break; 75 case NSIM_RESOURCE_IPV4_FIB_RULES: 76 entry = &fib_data->ipv4.rules; 77 break; 78 case NSIM_RESOURCE_IPV6_FIB: 79 entry = &fib_data->ipv6.fib; 80 break; 81 case NSIM_RESOURCE_IPV6_FIB_RULES: 82 entry = &fib_data->ipv6.rules; 83 break; 84 default: 85 WARN_ON(1); 86 return; 87 } 88 entry->max = val; 89} 90 91static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add, 92 struct netlink_ext_ack *extack) 93{ 94 int err = 0; 95 96 if (add) { 97 if (entry->num < entry->max) { 98 entry->num++; 99 } else { 100 err = -ENOSPC; 101 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries"); 102 } 103 } else { 104 entry->num--; 105 } 106 107 return err; 108} 109 110static int nsim_fib_rule_event(struct nsim_fib_data *data, 111 struct fib_notifier_info *info, bool add) 112{ 113 struct netlink_ext_ack *extack = info->extack; 114 int err = 0; 115 116 switch (info->family) { 117 case AF_INET: 118 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack); 119 break; 120 case AF_INET6: 121 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack); 122 break; 123 } 124 125 return err; 126} 127 128static int nsim_fib_account(struct nsim_fib_entry *entry, bool add, 129 struct netlink_ext_ack *extack) 130{ 131 int err = 0; 132 133 if (add) { 134 if (entry->num < entry->max) { 135 entry->num++; 136 } else { 137 err = -ENOSPC; 138 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries"); 139 } 140 } else { 141 entry->num--; 142 } 143 144 return err; 145} 146 147static int nsim_fib_event(struct nsim_fib_data *data, 148 struct fib_notifier_info *info, bool add) 149{ 150 struct netlink_ext_ack *extack = info->extack; 151 int err = 0; 152 153 switch (info->family) { 154 case AF_INET: 155 err = nsim_fib_account(&data->ipv4.fib, add, extack); 156 break; 157 case AF_INET6: 158 err = nsim_fib_account(&data->ipv6.fib, add, extack); 159 break; 160 } 161 162 return err; 163} 164 165static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event, 166 void *ptr) 167{ 168 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, 169 fib_nb); 170 struct fib_notifier_info *info = ptr; 171 int err = 0; 172 173 switch (event) { 174 case FIB_EVENT_RULE_ADD: /* fall through */ 175 case FIB_EVENT_RULE_DEL: 176 err = nsim_fib_rule_event(data, info, 177 event == FIB_EVENT_RULE_ADD); 178 break; 179 180 case FIB_EVENT_ENTRY_ADD: /* fall through */ 181 case FIB_EVENT_ENTRY_DEL: 182 err = nsim_fib_event(data, info, 183 event == FIB_EVENT_ENTRY_ADD); 184 break; 185 } 186 187 return notifier_from_errno(err); 188} 189 190/* inconsistent dump, trying again */ 191static void nsim_fib_dump_inconsistent(struct notifier_block *nb) 192{ 193 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, 194 fib_nb); 195 196 data->ipv4.fib.num = 0ULL; 197 data->ipv4.rules.num = 0ULL; 198 data->ipv6.fib.num = 0ULL; 199 data->ipv6.rules.num = 0ULL; 200} 201 202static u64 nsim_fib_ipv4_resource_occ_get(void *priv) 203{ 204 struct nsim_fib_data *data = priv; 205 206 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false); 207} 208 209static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv) 210{ 211 struct nsim_fib_data *data = priv; 212 213 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false); 214} 215 216static u64 nsim_fib_ipv6_resource_occ_get(void *priv) 217{ 218 struct nsim_fib_data *data = priv; 219 220 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false); 221} 222 223static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv) 224{ 225 struct nsim_fib_data *data = priv; 226 227 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false); 228} 229 230static void nsim_fib_set_max_all(struct nsim_fib_data *data, 231 struct devlink *devlink) 232{ 233 enum nsim_resource_id res_ids[] = { 234 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES, 235 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES 236 }; 237 int i; 238 239 for (i = 0; i < ARRAY_SIZE(res_ids); i++) { 240 int err; 241 u64 val; 242 243 err = devlink_resource_size_get(devlink, res_ids[i], &val); 244 if (err) 245 val = (u64) -1; 246 nsim_fib_set_max(data, res_ids[i], val); 247 } 248} 249 250struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, 251 struct netlink_ext_ack *extack) 252{ 253 struct nsim_fib_data *data; 254 int err; 255 256 data = kzalloc(sizeof(*data), GFP_KERNEL); 257 if (!data) 258 return ERR_PTR(-ENOMEM); 259 260 nsim_fib_set_max_all(data, devlink); 261 262 data->fib_nb.notifier_call = nsim_fib_event_nb; 263 err = register_fib_notifier(devlink_net(devlink), &data->fib_nb, 264 nsim_fib_dump_inconsistent, extack); 265 if (err) { 266 pr_err("Failed to register fib notifier\n"); 267 goto err_out; 268 } 269 270 devlink_resource_occ_get_register(devlink, 271 NSIM_RESOURCE_IPV4_FIB, 272 nsim_fib_ipv4_resource_occ_get, 273 data); 274 devlink_resource_occ_get_register(devlink, 275 NSIM_RESOURCE_IPV4_FIB_RULES, 276 nsim_fib_ipv4_rules_res_occ_get, 277 data); 278 devlink_resource_occ_get_register(devlink, 279 NSIM_RESOURCE_IPV6_FIB, 280 nsim_fib_ipv6_resource_occ_get, 281 data); 282 devlink_resource_occ_get_register(devlink, 283 NSIM_RESOURCE_IPV6_FIB_RULES, 284 nsim_fib_ipv6_rules_res_occ_get, 285 data); 286 return data; 287 288err_out: 289 kfree(data); 290 return ERR_PTR(err); 291} 292 293void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data) 294{ 295 devlink_resource_occ_get_unregister(devlink, 296 NSIM_RESOURCE_IPV6_FIB_RULES); 297 devlink_resource_occ_get_unregister(devlink, 298 NSIM_RESOURCE_IPV6_FIB); 299 devlink_resource_occ_get_unregister(devlink, 300 NSIM_RESOURCE_IPV4_FIB_RULES); 301 devlink_resource_occ_get_unregister(devlink, 302 NSIM_RESOURCE_IPV4_FIB); 303 unregister_fib_notifier(devlink_net(devlink), &data->fib_nb); 304 kfree(data); 305}