140 lines
3.1 KiB
C
140 lines
3.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/xarray.h>
|
|
#include <net/net_namespace.h>
|
|
#include <net/psp.h>
|
|
|
|
#include "psp.h"
|
|
#include "psp-nl-gen.h"
|
|
|
|
DEFINE_XARRAY_ALLOC1(psp_devs);
|
|
struct mutex psp_devs_lock;
|
|
|
|
/**
|
|
* DOC: PSP locking
|
|
*
|
|
* psp_devs_lock protects the psp_devs xarray.
|
|
* Ordering is take the psp_devs_lock and then the instance lock.
|
|
* Each instance is protected by RCU, and has a refcount.
|
|
* When driver unregisters the instance gets flushed, but struct sticks around.
|
|
*/
|
|
|
|
/**
|
|
* psp_dev_check_access() - check if user in a given net ns can access PSP dev
|
|
* @psd: PSP device structure user is trying to access
|
|
* @net: net namespace user is in
|
|
*
|
|
* Return: 0 if PSP device should be visible in @net, errno otherwise.
|
|
*/
|
|
int psp_dev_check_access(struct psp_dev *psd, struct net *net)
|
|
{
|
|
if (dev_net(psd->main_netdev) == net)
|
|
return 0;
|
|
return -ENOENT;
|
|
}
|
|
|
|
/**
|
|
* psp_dev_create() - create and register PSP device
|
|
* @netdev: main netdevice
|
|
* @psd_ops: driver callbacks
|
|
* @psd_caps: device capabilities
|
|
* @priv_ptr: back-pointer to driver private data
|
|
*
|
|
* Return: pointer to allocated PSP device, or ERR_PTR.
|
|
*/
|
|
struct psp_dev *
|
|
psp_dev_create(struct net_device *netdev,
|
|
struct psp_dev_ops *psd_ops, struct psp_dev_caps *psd_caps,
|
|
void *priv_ptr)
|
|
{
|
|
struct psp_dev *psd;
|
|
static u32 last_id;
|
|
int err;
|
|
|
|
if (WARN_ON(!psd_caps->versions ||
|
|
!psd_ops->set_config))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
psd = kzalloc(sizeof(*psd), GFP_KERNEL);
|
|
if (!psd)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
psd->main_netdev = netdev;
|
|
psd->ops = psd_ops;
|
|
psd->caps = psd_caps;
|
|
psd->drv_priv = priv_ptr;
|
|
|
|
mutex_init(&psd->lock);
|
|
refcount_set(&psd->refcnt, 1);
|
|
|
|
mutex_lock(&psp_devs_lock);
|
|
err = xa_alloc_cyclic(&psp_devs, &psd->id, psd, xa_limit_16b,
|
|
&last_id, GFP_KERNEL);
|
|
if (err) {
|
|
mutex_unlock(&psp_devs_lock);
|
|
kfree(psd);
|
|
return ERR_PTR(err);
|
|
}
|
|
mutex_lock(&psd->lock);
|
|
mutex_unlock(&psp_devs_lock);
|
|
|
|
psp_nl_notify_dev(psd, PSP_CMD_DEV_ADD_NTF);
|
|
|
|
rcu_assign_pointer(netdev->psp_dev, psd);
|
|
|
|
mutex_unlock(&psd->lock);
|
|
|
|
return psd;
|
|
}
|
|
EXPORT_SYMBOL(psp_dev_create);
|
|
|
|
void psp_dev_destroy(struct psp_dev *psd)
|
|
{
|
|
mutex_lock(&psp_devs_lock);
|
|
xa_erase(&psp_devs, psd->id);
|
|
mutex_unlock(&psp_devs_lock);
|
|
|
|
mutex_destroy(&psd->lock);
|
|
kfree_rcu(psd, rcu);
|
|
}
|
|
|
|
/**
|
|
* psp_dev_unregister() - unregister PSP device
|
|
* @psd: PSP device structure
|
|
*/
|
|
void psp_dev_unregister(struct psp_dev *psd)
|
|
{
|
|
mutex_lock(&psp_devs_lock);
|
|
mutex_lock(&psd->lock);
|
|
|
|
psp_nl_notify_dev(psd, PSP_CMD_DEV_DEL_NTF);
|
|
|
|
/* Wait until psp_dev_destroy() to call xa_erase() to prevent a
|
|
* different psd from being added to the xarray with this id, while
|
|
* there are still references to this psd being held.
|
|
*/
|
|
xa_store(&psp_devs, psd->id, NULL, GFP_KERNEL);
|
|
mutex_unlock(&psp_devs_lock);
|
|
|
|
rcu_assign_pointer(psd->main_netdev->psp_dev, NULL);
|
|
|
|
psd->ops = NULL;
|
|
psd->drv_priv = NULL;
|
|
|
|
mutex_unlock(&psd->lock);
|
|
|
|
psp_dev_put(psd);
|
|
}
|
|
EXPORT_SYMBOL(psp_dev_unregister);
|
|
|
|
static int __init psp_init(void)
|
|
{
|
|
mutex_init(&psp_devs_lock);
|
|
|
|
return genl_register_family(&psp_nl_family);
|
|
}
|
|
|
|
subsys_initcall(psp_init);
|