/*
* Copyright (c) 2018 Cumulus Networks. All rights reserved.
* Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
* Copyright (c) 2019 Mellanox Technologies. All rights reserved.
*
* This software is licensed under the GNU General License Version 2,
* June 1991 as shown in the file COPYING in the top-level directory of this
* source tree.
*
* THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
* OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
* THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/inet.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/rtnetlink.h>
#include <linux/workqueue.h>
#include <net/devlink.h>
#include <net/ip.h>
#include <net/flow_offload.h>
#include <uapi/linux/devlink.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/udp.h>
#include "netdevsim.h"
static struct dentry *nsim_dev_ddir;
#define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32)
static int
nsim_dev_take_snapshot(struct devlink *devlink, struct netlink_ext_ack *extack,
u8 **data)
{
void *dummy_data;
dummy_data = kmalloc(NSIM_DEV_DUMMY_REGION_SIZE, GFP_KERNEL);
if (!dummy_data)
return -ENOMEM;
get_random_bytes(dummy_data, NSIM_DEV_DUMMY_REGION_SIZE);
*data = dummy_data;
return 0;
}
static ssize_t nsim_dev_take_snapshot_write(struct file *file,
const char __user *data,
size_t count, loff_t *ppos)
{
struct nsim_dev *nsim_dev = file->private_data;
struct devlink *devlink;
u8 *dummy_data;
int err;
u32 id;
devlink = priv_to_devlink(nsim_dev);
err = nsim_dev_take_snapshot(devlink, NULL, &dummy_data);
if (err)
return err;
err = devlink_region_snapshot_id_get(devlink, &id);
if (err) {
pr_err("Failed to get snapshot id\n");
kfree(dummy_data);
return err;
}
err = devlink_region_snapshot_create(nsim_dev->dummy_region,
dummy_data, id);
devlink_region_snapshot_id_put(devlink, id);
if (err) {
pr_err("Failed to create region snapshot\n");
kfree(dummy_data);
return err;
}
return count;
}
static const struct file_operations nsim_dev_take_snapshot_fops = {
.open = simple_open,
.write = nsim_dev_take_snapshot_write,
.llseek = generic_file_llseek,
};
static ssize_t nsim_dev_trap_fa_cookie_read(struct file *file,
char __user *data,
size_t count, loff_t *ppos)
{
struct nsim_dev *nsim_dev = file->private_data;
struct flow_action_cookie *fa_cookie;
unsigned int buf_len;
ssize_t ret;
char *buf;
spin_lock(&nsim_dev->fa_cookie_lock);
fa_cookie = nsim_dev->fa_cookie;
if (!fa_cookie) {
ret = -EINVAL;
goto errout;
}
buf_len = fa_cookie->cookie_len * 2;
buf = kmalloc(buf_len, GFP_ATOMIC);
if (!buf) {
ret = -ENOMEM;
goto errout;
}
bin2hex(buf, fa_cookie->cookie, fa_cookie->cookie_len);
spin_unlock(&nsim_dev->fa_cookie_lock);
ret = simple_read_from_buffer(data, count, ppos, buf, buf_len);
kfree(buf);
return ret;
errout:
spin_unlock(&nsim_dev->fa_cookie_lock);
return ret;
}
static ssize_t nsim_dev_trap_fa_cookie_write(struct file *file,
const char __user *data,
size_t count, loff_t *ppos)
{
struct nsim_dev *nsim_dev = fil