0001
0002
0003 #include <linux/debugfs.h>
0004
0005 #include "netdevsim.h"
0006
0007 #define NSIM_DEV_HWSTATS_TRAFFIC_MS 100
0008
0009 static struct list_head *
0010 nsim_dev_hwstats_get_list_head(struct nsim_dev_hwstats *hwstats,
0011 enum netdev_offload_xstats_type type)
0012 {
0013 switch (type) {
0014 case NETDEV_OFFLOAD_XSTATS_TYPE_L3:
0015 return &hwstats->l3_list;
0016 }
0017
0018 WARN_ON_ONCE(1);
0019 return NULL;
0020 }
0021
0022 static void nsim_dev_hwstats_traffic_bump(struct nsim_dev_hwstats *hwstats,
0023 enum netdev_offload_xstats_type type)
0024 {
0025 struct nsim_dev_hwstats_netdev *hwsdev;
0026 struct list_head *hwsdev_list;
0027
0028 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
0029 if (WARN_ON(!hwsdev_list))
0030 return;
0031
0032 list_for_each_entry(hwsdev, hwsdev_list, list) {
0033 if (hwsdev->enabled) {
0034 hwsdev->stats.rx_packets += 1;
0035 hwsdev->stats.tx_packets += 2;
0036 hwsdev->stats.rx_bytes += 100;
0037 hwsdev->stats.tx_bytes += 300;
0038 }
0039 }
0040 }
0041
0042 static void nsim_dev_hwstats_traffic_work(struct work_struct *work)
0043 {
0044 struct nsim_dev_hwstats *hwstats;
0045
0046 hwstats = container_of(work, struct nsim_dev_hwstats, traffic_dw.work);
0047 mutex_lock(&hwstats->hwsdev_list_lock);
0048 nsim_dev_hwstats_traffic_bump(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
0049 mutex_unlock(&hwstats->hwsdev_list_lock);
0050
0051 schedule_delayed_work(&hwstats->traffic_dw,
0052 msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
0053 }
0054
0055 static struct nsim_dev_hwstats_netdev *
0056 nsim_dev_hwslist_find_hwsdev(struct list_head *hwsdev_list,
0057 int ifindex)
0058 {
0059 struct nsim_dev_hwstats_netdev *hwsdev;
0060
0061 list_for_each_entry(hwsdev, hwsdev_list, list) {
0062 if (hwsdev->netdev->ifindex == ifindex)
0063 return hwsdev;
0064 }
0065
0066 return NULL;
0067 }
0068
0069 static int nsim_dev_hwsdev_enable(struct nsim_dev_hwstats_netdev *hwsdev,
0070 struct netlink_ext_ack *extack)
0071 {
0072 if (hwsdev->fail_enable) {
0073 hwsdev->fail_enable = false;
0074 NL_SET_ERR_MSG_MOD(extack, "Stats enablement set to fail");
0075 return -ECANCELED;
0076 }
0077
0078 hwsdev->enabled = true;
0079 return 0;
0080 }
0081
0082 static void nsim_dev_hwsdev_disable(struct nsim_dev_hwstats_netdev *hwsdev)
0083 {
0084 hwsdev->enabled = false;
0085 memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
0086 }
0087
0088 static int
0089 nsim_dev_hwsdev_report_delta(struct nsim_dev_hwstats_netdev *hwsdev,
0090 struct netdev_notifier_offload_xstats_info *info)
0091 {
0092 netdev_offload_xstats_report_delta(info->report_delta, &hwsdev->stats);
0093 memset(&hwsdev->stats, 0, sizeof(hwsdev->stats));
0094 return 0;
0095 }
0096
0097 static void
0098 nsim_dev_hwsdev_report_used(struct nsim_dev_hwstats_netdev *hwsdev,
0099 struct netdev_notifier_offload_xstats_info *info)
0100 {
0101 if (hwsdev->enabled)
0102 netdev_offload_xstats_report_used(info->report_used);
0103 }
0104
0105 static int nsim_dev_hwstats_event_off_xstats(struct nsim_dev_hwstats *hwstats,
0106 struct net_device *dev,
0107 unsigned long event, void *ptr)
0108 {
0109 struct netdev_notifier_offload_xstats_info *info;
0110 struct nsim_dev_hwstats_netdev *hwsdev;
0111 struct list_head *hwsdev_list;
0112 int err = 0;
0113
0114 info = ptr;
0115 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, info->type);
0116 if (!hwsdev_list)
0117 return 0;
0118
0119 mutex_lock(&hwstats->hwsdev_list_lock);
0120
0121 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
0122 if (!hwsdev)
0123 goto out;
0124
0125 switch (event) {
0126 case NETDEV_OFFLOAD_XSTATS_ENABLE:
0127 err = nsim_dev_hwsdev_enable(hwsdev, info->info.extack);
0128 break;
0129 case NETDEV_OFFLOAD_XSTATS_DISABLE:
0130 nsim_dev_hwsdev_disable(hwsdev);
0131 break;
0132 case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
0133 nsim_dev_hwsdev_report_used(hwsdev, info);
0134 break;
0135 case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
0136 err = nsim_dev_hwsdev_report_delta(hwsdev, info);
0137 break;
0138 }
0139
0140 out:
0141 mutex_unlock(&hwstats->hwsdev_list_lock);
0142 return err;
0143 }
0144
0145 static void nsim_dev_hwsdev_fini(struct nsim_dev_hwstats_netdev *hwsdev)
0146 {
0147 dev_put(hwsdev->netdev);
0148 kfree(hwsdev);
0149 }
0150
0151 static void
0152 __nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
0153 struct net_device *dev,
0154 enum netdev_offload_xstats_type type)
0155 {
0156 struct nsim_dev_hwstats_netdev *hwsdev;
0157 struct list_head *hwsdev_list;
0158
0159 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
0160 if (WARN_ON(!hwsdev_list))
0161 return;
0162
0163 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, dev->ifindex);
0164 if (!hwsdev)
0165 return;
0166
0167 list_del(&hwsdev->list);
0168 nsim_dev_hwsdev_fini(hwsdev);
0169 }
0170
0171 static void nsim_dev_hwstats_event_unregister(struct nsim_dev_hwstats *hwstats,
0172 struct net_device *dev)
0173 {
0174 mutex_lock(&hwstats->hwsdev_list_lock);
0175 __nsim_dev_hwstats_event_unregister(hwstats, dev,
0176 NETDEV_OFFLOAD_XSTATS_TYPE_L3);
0177 mutex_unlock(&hwstats->hwsdev_list_lock);
0178 }
0179
0180 static int nsim_dev_hwstats_event(struct nsim_dev_hwstats *hwstats,
0181 struct net_device *dev,
0182 unsigned long event, void *ptr)
0183 {
0184 switch (event) {
0185 case NETDEV_OFFLOAD_XSTATS_ENABLE:
0186 case NETDEV_OFFLOAD_XSTATS_DISABLE:
0187 case NETDEV_OFFLOAD_XSTATS_REPORT_USED:
0188 case NETDEV_OFFLOAD_XSTATS_REPORT_DELTA:
0189 return nsim_dev_hwstats_event_off_xstats(hwstats, dev,
0190 event, ptr);
0191 case NETDEV_UNREGISTER:
0192 nsim_dev_hwstats_event_unregister(hwstats, dev);
0193 break;
0194 }
0195
0196 return 0;
0197 }
0198
0199 static int nsim_dev_netdevice_event(struct notifier_block *nb,
0200 unsigned long event, void *ptr)
0201 {
0202 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
0203 struct nsim_dev_hwstats *hwstats;
0204 int err = 0;
0205
0206 hwstats = container_of(nb, struct nsim_dev_hwstats, netdevice_nb);
0207 err = nsim_dev_hwstats_event(hwstats, dev, event, ptr);
0208 if (err)
0209 return notifier_from_errno(err);
0210
0211 return NOTIFY_OK;
0212 }
0213
0214 static int
0215 nsim_dev_hwstats_enable_ifindex(struct nsim_dev_hwstats *hwstats,
0216 int ifindex,
0217 enum netdev_offload_xstats_type type,
0218 struct list_head *hwsdev_list)
0219 {
0220 struct nsim_dev_hwstats_netdev *hwsdev;
0221 struct nsim_dev *nsim_dev;
0222 struct net_device *netdev;
0223 bool notify = false;
0224 struct net *net;
0225 int err = 0;
0226
0227 nsim_dev = container_of(hwstats, struct nsim_dev, hwstats);
0228 net = nsim_dev_net(nsim_dev);
0229
0230 rtnl_lock();
0231 mutex_lock(&hwstats->hwsdev_list_lock);
0232 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
0233 if (hwsdev)
0234 goto out_unlock_list;
0235
0236 netdev = dev_get_by_index(net, ifindex);
0237 if (!netdev) {
0238 err = -ENODEV;
0239 goto out_unlock_list;
0240 }
0241
0242 hwsdev = kzalloc(sizeof(*hwsdev), GFP_KERNEL);
0243 if (!hwsdev) {
0244 err = -ENOMEM;
0245 goto out_put_netdev;
0246 }
0247
0248 hwsdev->netdev = netdev;
0249 list_add_tail(&hwsdev->list, hwsdev_list);
0250 mutex_unlock(&hwstats->hwsdev_list_lock);
0251
0252 if (netdev_offload_xstats_enabled(netdev, type)) {
0253 nsim_dev_hwsdev_enable(hwsdev, NULL);
0254 notify = true;
0255 }
0256
0257 if (notify)
0258 rtnl_offload_xstats_notify(netdev);
0259 rtnl_unlock();
0260 return err;
0261
0262 out_put_netdev:
0263 dev_put(netdev);
0264 out_unlock_list:
0265 mutex_unlock(&hwstats->hwsdev_list_lock);
0266 rtnl_unlock();
0267 return err;
0268 }
0269
0270 static int
0271 nsim_dev_hwstats_disable_ifindex(struct nsim_dev_hwstats *hwstats,
0272 int ifindex,
0273 enum netdev_offload_xstats_type type,
0274 struct list_head *hwsdev_list)
0275 {
0276 struct nsim_dev_hwstats_netdev *hwsdev;
0277 int err = 0;
0278
0279 rtnl_lock();
0280 mutex_lock(&hwstats->hwsdev_list_lock);
0281 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
0282 if (hwsdev)
0283 list_del(&hwsdev->list);
0284 mutex_unlock(&hwstats->hwsdev_list_lock);
0285
0286 if (!hwsdev) {
0287 err = -ENOENT;
0288 goto unlock_out;
0289 }
0290
0291 if (netdev_offload_xstats_enabled(hwsdev->netdev, type)) {
0292 netdev_offload_xstats_push_delta(hwsdev->netdev, type,
0293 &hwsdev->stats);
0294 rtnl_offload_xstats_notify(hwsdev->netdev);
0295 }
0296 nsim_dev_hwsdev_fini(hwsdev);
0297
0298 unlock_out:
0299 rtnl_unlock();
0300 return err;
0301 }
0302
0303 static int
0304 nsim_dev_hwstats_fail_ifindex(struct nsim_dev_hwstats *hwstats,
0305 int ifindex,
0306 enum netdev_offload_xstats_type type,
0307 struct list_head *hwsdev_list)
0308 {
0309 struct nsim_dev_hwstats_netdev *hwsdev;
0310 int err = 0;
0311
0312 mutex_lock(&hwstats->hwsdev_list_lock);
0313
0314 hwsdev = nsim_dev_hwslist_find_hwsdev(hwsdev_list, ifindex);
0315 if (!hwsdev) {
0316 err = -ENOENT;
0317 goto err_hwsdev_list_unlock;
0318 }
0319
0320 hwsdev->fail_enable = true;
0321
0322 err_hwsdev_list_unlock:
0323 mutex_unlock(&hwstats->hwsdev_list_lock);
0324 return err;
0325 }
0326
0327 enum nsim_dev_hwstats_do {
0328 NSIM_DEV_HWSTATS_DO_DISABLE,
0329 NSIM_DEV_HWSTATS_DO_ENABLE,
0330 NSIM_DEV_HWSTATS_DO_FAIL,
0331 };
0332
0333 struct nsim_dev_hwstats_fops {
0334 const struct file_operations fops;
0335 enum nsim_dev_hwstats_do action;
0336 enum netdev_offload_xstats_type type;
0337 };
0338
0339 static ssize_t
0340 nsim_dev_hwstats_do_write(struct file *file,
0341 const char __user *data,
0342 size_t count, loff_t *ppos)
0343 {
0344 struct nsim_dev_hwstats *hwstats = file->private_data;
0345 struct nsim_dev_hwstats_fops *hwsfops;
0346 struct list_head *hwsdev_list;
0347 int ifindex;
0348 int err;
0349
0350 hwsfops = container_of(debugfs_real_fops(file),
0351 struct nsim_dev_hwstats_fops, fops);
0352
0353 err = kstrtoint_from_user(data, count, 0, &ifindex);
0354 if (err)
0355 return err;
0356
0357 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, hwsfops->type);
0358 if (WARN_ON(!hwsdev_list))
0359 return -EINVAL;
0360
0361 switch (hwsfops->action) {
0362 case NSIM_DEV_HWSTATS_DO_DISABLE:
0363 err = nsim_dev_hwstats_disable_ifindex(hwstats, ifindex,
0364 hwsfops->type,
0365 hwsdev_list);
0366 break;
0367 case NSIM_DEV_HWSTATS_DO_ENABLE:
0368 err = nsim_dev_hwstats_enable_ifindex(hwstats, ifindex,
0369 hwsfops->type,
0370 hwsdev_list);
0371 break;
0372 case NSIM_DEV_HWSTATS_DO_FAIL:
0373 err = nsim_dev_hwstats_fail_ifindex(hwstats, ifindex,
0374 hwsfops->type,
0375 hwsdev_list);
0376 break;
0377 }
0378 if (err)
0379 return err;
0380
0381 return count;
0382 }
0383
0384 #define NSIM_DEV_HWSTATS_FOPS(ACTION, TYPE) \
0385 { \
0386 .fops = { \
0387 .open = simple_open, \
0388 .write = nsim_dev_hwstats_do_write, \
0389 .llseek = generic_file_llseek, \
0390 .owner = THIS_MODULE, \
0391 }, \
0392 .action = ACTION, \
0393 .type = TYPE, \
0394 }
0395
0396 static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_disable_fops =
0397 NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_DISABLE,
0398 NETDEV_OFFLOAD_XSTATS_TYPE_L3);
0399
0400 static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_enable_fops =
0401 NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_ENABLE,
0402 NETDEV_OFFLOAD_XSTATS_TYPE_L3);
0403
0404 static const struct nsim_dev_hwstats_fops nsim_dev_hwstats_l3_fail_fops =
0405 NSIM_DEV_HWSTATS_FOPS(NSIM_DEV_HWSTATS_DO_FAIL,
0406 NETDEV_OFFLOAD_XSTATS_TYPE_L3);
0407
0408 #undef NSIM_DEV_HWSTATS_FOPS
0409
0410 int nsim_dev_hwstats_init(struct nsim_dev *nsim_dev)
0411 {
0412 struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
0413 struct net *net = nsim_dev_net(nsim_dev);
0414 int err;
0415
0416 mutex_init(&hwstats->hwsdev_list_lock);
0417 INIT_LIST_HEAD(&hwstats->l3_list);
0418
0419 hwstats->netdevice_nb.notifier_call = nsim_dev_netdevice_event;
0420 err = register_netdevice_notifier_net(net, &hwstats->netdevice_nb);
0421 if (err)
0422 goto err_mutex_destroy;
0423
0424 hwstats->ddir = debugfs_create_dir("hwstats", nsim_dev->ddir);
0425 if (IS_ERR(hwstats->ddir)) {
0426 err = PTR_ERR(hwstats->ddir);
0427 goto err_unregister_notifier;
0428 }
0429
0430 hwstats->l3_ddir = debugfs_create_dir("l3", hwstats->ddir);
0431 if (IS_ERR(hwstats->l3_ddir)) {
0432 err = PTR_ERR(hwstats->l3_ddir);
0433 goto err_remove_hwstats_recursive;
0434 }
0435
0436 debugfs_create_file("enable_ifindex", 0200, hwstats->l3_ddir, hwstats,
0437 &nsim_dev_hwstats_l3_enable_fops.fops);
0438 debugfs_create_file("disable_ifindex", 0200, hwstats->l3_ddir, hwstats,
0439 &nsim_dev_hwstats_l3_disable_fops.fops);
0440 debugfs_create_file("fail_next_enable", 0200, hwstats->l3_ddir, hwstats,
0441 &nsim_dev_hwstats_l3_fail_fops.fops);
0442
0443 INIT_DELAYED_WORK(&hwstats->traffic_dw,
0444 &nsim_dev_hwstats_traffic_work);
0445 schedule_delayed_work(&hwstats->traffic_dw,
0446 msecs_to_jiffies(NSIM_DEV_HWSTATS_TRAFFIC_MS));
0447 return 0;
0448
0449 err_remove_hwstats_recursive:
0450 debugfs_remove_recursive(hwstats->ddir);
0451 err_unregister_notifier:
0452 unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
0453 err_mutex_destroy:
0454 mutex_destroy(&hwstats->hwsdev_list_lock);
0455 return err;
0456 }
0457
0458 static void nsim_dev_hwsdev_list_wipe(struct nsim_dev_hwstats *hwstats,
0459 enum netdev_offload_xstats_type type)
0460 {
0461 struct nsim_dev_hwstats_netdev *hwsdev, *tmp;
0462 struct list_head *hwsdev_list;
0463
0464 hwsdev_list = nsim_dev_hwstats_get_list_head(hwstats, type);
0465 if (WARN_ON(!hwsdev_list))
0466 return;
0467
0468 mutex_lock(&hwstats->hwsdev_list_lock);
0469 list_for_each_entry_safe(hwsdev, tmp, hwsdev_list, list) {
0470 list_del(&hwsdev->list);
0471 nsim_dev_hwsdev_fini(hwsdev);
0472 }
0473 mutex_unlock(&hwstats->hwsdev_list_lock);
0474 }
0475
0476 void nsim_dev_hwstats_exit(struct nsim_dev *nsim_dev)
0477 {
0478 struct nsim_dev_hwstats *hwstats = &nsim_dev->hwstats;
0479 struct net *net = nsim_dev_net(nsim_dev);
0480
0481 cancel_delayed_work_sync(&hwstats->traffic_dw);
0482 debugfs_remove_recursive(hwstats->ddir);
0483 unregister_netdevice_notifier_net(net, &hwstats->netdevice_nb);
0484 nsim_dev_hwsdev_list_wipe(hwstats, NETDEV_OFFLOAD_XSTATS_TYPE_L3);
0485 mutex_destroy(&hwstats->hwsdev_list_lock);
0486 }