/*
* 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 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;
void *dummy_data;
int err;
u32 id;
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);
id = devlink_region_snapshot_id_get(priv_to_devlink(nsim_dev));
err = devlink_region_snapshot_create(nsim_dev->dummy_region,
dummy_data, 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 = file->private_data;
struct flow_action_cookie *fa_cookie;
size_t cookie_len;
ssize_t ret;
char *buf;
if (*ppos != 0)
return -EINVAL;
cookie_len = (count - 1) / 2;
if ((count - 1) % 2)
return -EINVAL;
buf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN);
if (!buf)
return -ENOMEM;
ret = simple_write_to_buffer(buf, count, ppos, data, count);
if (ret < 0)
goto free_buf;
fa_cookie =