// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
/* Copyright (C) 2015-2018 Netronome Systems, Inc. */
|
|
/*
|
* nfp_resource.c
|
* Author: Jakub Kicinski <jakub.kicinski@netronome.com>
|
* Jason McMullan <jason.mcmullan@netronome.com>
|
*/
|
#include <linux/delay.h>
|
#include <linux/kernel.h>
|
#include <linux/slab.h>
|
|
#include "crc32.h"
|
#include "nfp.h"
|
#include "nfp_cpp.h"
|
#include "nfp6000/nfp6000.h"
|
|
#define NFP_RESOURCE_TBL_TARGET NFP_CPP_TARGET_MU
|
#define NFP_RESOURCE_TBL_BASE 0x8100000000ULL
|
|
/* NFP Resource Table self-identifier */
|
#define NFP_RESOURCE_TBL_NAME "nfp.res"
|
#define NFP_RESOURCE_TBL_KEY 0x00000000 /* Special key for entry 0 */
|
|
#define NFP_RESOURCE_ENTRY_NAME_SZ 8
|
|
/**
|
* struct nfp_resource_entry - Resource table entry
|
* @mutex: NFP CPP Lock
|
* @mutex.owner: NFP CPP Lock, interface owner
|
* @mutex.key: NFP CPP Lock, posix_crc32(name, 8)
|
* @region: Memory region descriptor
|
* @region.name: ASCII, zero padded name
|
* @region.reserved: padding
|
* @region.cpp_action: CPP Action
|
* @region.cpp_token: CPP Token
|
* @region.cpp_target: CPP Target ID
|
* @region.page_offset: 256-byte page offset into target's CPP address
|
* @region.page_size: size, in 256-byte pages
|
*/
|
struct nfp_resource_entry {
|
struct nfp_resource_entry_mutex {
|
u32 owner;
|
u32 key;
|
} mutex;
|
struct nfp_resource_entry_region {
|
u8 name[NFP_RESOURCE_ENTRY_NAME_SZ];
|
u8 reserved[5];
|
u8 cpp_action;
|
u8 cpp_token;
|
u8 cpp_target;
|
u32 page_offset;
|
u32 page_size;
|
} region;
|
};
|
|
#define NFP_RESOURCE_TBL_SIZE 4096
|
#define NFP_RESOURCE_TBL_ENTRIES (NFP_RESOURCE_TBL_SIZE / \
|
sizeof(struct nfp_resource_entry))
|
|
struct nfp_resource {
|
char name[NFP_RESOURCE_ENTRY_NAME_SZ + 1];
|
u32 cpp_id;
|
u64 addr;
|
u64 size;
|
struct nfp_cpp_mutex *mutex;
|
};
|
|
static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res)
|
{
|
struct nfp_resource_entry entry;
|
u32 cpp_id, key;
|
int ret, i;
|
|
cpp_id = NFP_CPP_ID(NFP_RESOURCE_TBL_TARGET, 3, 0); /* Atomic read */
|
|
/* Search for a matching entry */
|
if (!strcmp(res->name, NFP_RESOURCE_TBL_NAME)) {
|
nfp_err(cpp, "Grabbing device lock not supported\n");
|
return -EOPNOTSUPP;
|
}
|
key = crc32_posix(res->name, NFP_RESOURCE_ENTRY_NAME_SZ);
|
|
for (i = 0; i < NFP_RESOURCE_TBL_ENTRIES; i++) {
|
u64 addr = NFP_RESOURCE_TBL_BASE +
|
sizeof(struct nfp_resource_entry) * i;
|
|
ret = nfp_cpp_read(cpp, cpp_id, addr, &entry, sizeof(entry));
|
if (ret != sizeof(entry))
|
return -EIO;
|
|
if (entry.mutex.key != key)
|
continue;
|
|
/* Found key! */
|
res->mutex =
|
nfp_cpp_mutex_alloc(cpp,
|
NFP_RESOURCE_TBL_TARGET, addr, key);
|
res->cpp_id = NFP_CPP_ID(entry.region.cpp_target,
|
entry.region.cpp_action,
|
entry.region.cpp_token);
|
res->addr = (u64)entry.region.page_offset << 8;
|
res->size = (u64)entry.region.page_size << 8;
|
|
return 0;
|
}
|
|
return -ENOENT;
|
}
|
|
static int
|
nfp_resource_try_acquire(struct nfp_cpp *cpp, struct nfp_resource *res,
|
struct nfp_cpp_mutex *dev_mutex)
|
{
|
int err;
|
|
if (nfp_cpp_mutex_lock(dev_mutex))
|
return -EINVAL;
|
|
err = nfp_cpp_resource_find(cpp, res);
|
if (err)
|
goto err_unlock_dev;
|
|
err = nfp_cpp_mutex_trylock(res->mutex);
|
if (err)
|
goto err_res_mutex_free;
|
|
nfp_cpp_mutex_unlock(dev_mutex);
|
|
return 0;
|
|
err_res_mutex_free:
|
nfp_cpp_mutex_free(res->mutex);
|
err_unlock_dev:
|
nfp_cpp_mutex_unlock(dev_mutex);
|
|
return err;
|
}
|
|
/**
|
* nfp_resource_acquire() - Acquire a resource handle
|
* @cpp: NFP CPP handle
|
* @name: Name of the resource
|
*
|
* NOTE: This function locks the acquired resource
|
*
|
* Return: NFP Resource handle, or ERR_PTR()
|
*/
|
struct nfp_resource *
|
nfp_resource_acquire(struct nfp_cpp *cpp, const char *name)
|
{
|
unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
|
unsigned long err_at = jiffies + NFP_MUTEX_WAIT_ERROR * HZ;
|
struct nfp_cpp_mutex *dev_mutex;
|
struct nfp_resource *res;
|
int err;
|
|
res = kzalloc(sizeof(*res), GFP_KERNEL);
|
if (!res)
|
return ERR_PTR(-ENOMEM);
|
|
strncpy(res->name, name, NFP_RESOURCE_ENTRY_NAME_SZ);
|
|
dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET,
|
NFP_RESOURCE_TBL_BASE,
|
NFP_RESOURCE_TBL_KEY);
|
if (!dev_mutex) {
|
kfree(res);
|
return ERR_PTR(-ENOMEM);
|
}
|
|
for (;;) {
|
err = nfp_resource_try_acquire(cpp, res, dev_mutex);
|
if (!err)
|
break;
|
if (err != -EBUSY)
|
goto err_free;
|
|
err = msleep_interruptible(1);
|
if (err != 0) {
|
err = -ERESTARTSYS;
|
goto err_free;
|
}
|
|
if (time_is_before_eq_jiffies(warn_at)) {
|
warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
|
nfp_warn(cpp, "Warning: waiting for NFP resource %s\n",
|
name);
|
}
|
if (time_is_before_eq_jiffies(err_at)) {
|
nfp_err(cpp, "Error: resource %s timed out\n", name);
|
err = -EBUSY;
|
goto err_free;
|
}
|
}
|
|
nfp_cpp_mutex_free(dev_mutex);
|
|
return res;
|
|
err_free:
|
nfp_cpp_mutex_free(dev_mutex);
|
kfree(res);
|
return ERR_PTR(err);
|
}
|
|
/**
|
* nfp_resource_release() - Release a NFP Resource handle
|
* @res: NFP Resource handle
|
*
|
* NOTE: This function implictly unlocks the resource handle
|
*/
|
void nfp_resource_release(struct nfp_resource *res)
|
{
|
nfp_cpp_mutex_unlock(res->mutex);
|
nfp_cpp_mutex_free(res->mutex);
|
kfree(res);
|
}
|
|
/**
|
* nfp_resource_wait() - Wait for resource to appear
|
* @cpp: NFP CPP handle
|
* @name: Name of the resource
|
* @secs: Number of seconds to wait
|
*
|
* Wait for resource to appear in the resource table, grab and release
|
* its lock. The wait is jiffies-based, don't expect fine granularity.
|
*
|
* Return: 0 on success, errno otherwise.
|
*/
|
int nfp_resource_wait(struct nfp_cpp *cpp, const char *name, unsigned int secs)
|
{
|
unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
|
unsigned long err_at = jiffies + secs * HZ;
|
struct nfp_resource *res;
|
|
while (true) {
|
res = nfp_resource_acquire(cpp, name);
|
if (!IS_ERR(res)) {
|
nfp_resource_release(res);
|
return 0;
|
}
|
|
if (PTR_ERR(res) != -ENOENT) {
|
nfp_err(cpp, "error waiting for resource %s: %ld\n",
|
name, PTR_ERR(res));
|
return PTR_ERR(res);
|
}
|
if (time_is_before_eq_jiffies(err_at)) {
|
nfp_err(cpp, "timeout waiting for resource %s\n", name);
|
return -ETIMEDOUT;
|
}
|
if (time_is_before_eq_jiffies(warn_at)) {
|
warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
|
nfp_info(cpp, "waiting for NFP resource %s\n", name);
|
}
|
if (msleep_interruptible(10)) {
|
nfp_err(cpp, "wait for resource %s interrupted\n",
|
name);
|
return -ERESTARTSYS;
|
}
|
}
|
}
|
|
/**
|
* nfp_resource_cpp_id() - Return the cpp_id of a resource handle
|
* @res: NFP Resource handle
|
*
|
* Return: NFP CPP ID
|
*/
|
u32 nfp_resource_cpp_id(struct nfp_resource *res)
|
{
|
return res->cpp_id;
|
}
|
|
/**
|
* nfp_resource_name() - Return the name of a resource handle
|
* @res: NFP Resource handle
|
*
|
* Return: const char pointer to the name of the resource
|
*/
|
const char *nfp_resource_name(struct nfp_resource *res)
|
{
|
return res->name;
|
}
|
|
/**
|
* nfp_resource_address() - Return the address of a resource handle
|
* @res: NFP Resource handle
|
*
|
* Return: Address of the resource
|
*/
|
u64 nfp_resource_address(struct nfp_resource *res)
|
{
|
return res->addr;
|
}
|
|
/**
|
* nfp_resource_size() - Return the size in bytes of a resource handle
|
* @res: NFP Resource handle
|
*
|
* Return: Size of the resource in bytes
|
*/
|
u64 nfp_resource_size(struct nfp_resource *res)
|
{
|
return res->size;
|
}
|
|
/**
|
* nfp_resource_table_init() - Run initial checks on the resource table
|
* @cpp: NFP CPP handle
|
*
|
* Start-of-day init procedure for resource table. Must be called before
|
* any local resource table users may exist.
|
*
|
* Return: 0 on success, -errno on failure
|
*/
|
int nfp_resource_table_init(struct nfp_cpp *cpp)
|
{
|
struct nfp_cpp_mutex *dev_mutex;
|
int i, err;
|
|
err = nfp_cpp_mutex_reclaim(cpp, NFP_RESOURCE_TBL_TARGET,
|
NFP_RESOURCE_TBL_BASE);
|
if (err < 0) {
|
nfp_err(cpp, "Error: failed to reclaim resource table mutex\n");
|
return err;
|
}
|
if (err)
|
nfp_warn(cpp, "Warning: busted main resource table mutex\n");
|
|
dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET,
|
NFP_RESOURCE_TBL_BASE,
|
NFP_RESOURCE_TBL_KEY);
|
if (!dev_mutex)
|
return -ENOMEM;
|
|
if (nfp_cpp_mutex_lock(dev_mutex)) {
|
nfp_err(cpp, "Error: failed to claim resource table mutex\n");
|
nfp_cpp_mutex_free(dev_mutex);
|
return -EINVAL;
|
}
|
|
/* Resource 0 is the dev_mutex, start from 1 */
|
for (i = 1; i < NFP_RESOURCE_TBL_ENTRIES; i++) {
|
u64 addr = NFP_RESOURCE_TBL_BASE +
|
sizeof(struct nfp_resource_entry) * i;
|
|
err = nfp_cpp_mutex_reclaim(cpp, NFP_RESOURCE_TBL_TARGET, addr);
|
if (err < 0) {
|
nfp_err(cpp,
|
"Error: failed to reclaim resource %d mutex\n",
|
i);
|
goto err_unlock;
|
}
|
if (err)
|
nfp_warn(cpp, "Warning: busted resource %d mutex\n", i);
|
}
|
|
err = 0;
|
err_unlock:
|
nfp_cpp_mutex_unlock(dev_mutex);
|
nfp_cpp_mutex_free(dev_mutex);
|
|
return err;
|
}
|