// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2015-2018 Netronome Systems, Inc. */
/*
* nfp_resource.c
* Author: Jakub Kicinski <[email protected]>
* Jason McMullan <[email protected]>
*/
#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);
strscpy(res->name, name, sizeof(res->name));
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;
}