Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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 }