linux/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2015-2018 Netronome Systems, Inc. */
   3
   4/*
   5 * nfp_resource.c
   6 * Author: Jakub Kicinski <jakub.kicinski@netronome.com>
   7 *         Jason McMullan <jason.mcmullan@netronome.com>
   8 */
   9#include <linux/delay.h>
  10#include <linux/kernel.h>
  11#include <linux/slab.h>
  12
  13#include "crc32.h"
  14#include "nfp.h"
  15#include "nfp_cpp.h"
  16#include "nfp6000/nfp6000.h"
  17
  18#define NFP_RESOURCE_TBL_TARGET         NFP_CPP_TARGET_MU
  19#define NFP_RESOURCE_TBL_BASE           0x8100000000ULL
  20
  21/* NFP Resource Table self-identifier */
  22#define NFP_RESOURCE_TBL_NAME           "nfp.res"
  23#define NFP_RESOURCE_TBL_KEY            0x00000000 /* Special key for entry 0 */
  24
  25#define NFP_RESOURCE_ENTRY_NAME_SZ      8
  26
  27/**
  28 * struct nfp_resource_entry - Resource table entry
  29 * @mutex:      NFP CPP Lock
  30 * @mutex.owner:        NFP CPP Lock, interface owner
  31 * @mutex.key:          NFP CPP Lock, posix_crc32(name, 8)
  32 * @region:     Memory region descriptor
  33 * @region.name:        ASCII, zero padded name
  34 * @region.reserved:    padding
  35 * @region.cpp_action:  CPP Action
  36 * @region.cpp_token:   CPP Token
  37 * @region.cpp_target:  CPP Target ID
  38 * @region.page_offset: 256-byte page offset into target's CPP address
  39 * @region.page_size:   size, in 256-byte pages
  40 */
  41struct nfp_resource_entry {
  42        struct nfp_resource_entry_mutex {
  43                u32 owner;
  44                u32 key;
  45        } mutex;
  46        struct nfp_resource_entry_region {
  47                u8  name[NFP_RESOURCE_ENTRY_NAME_SZ];
  48                u8  reserved[5];
  49                u8  cpp_action;
  50                u8  cpp_token;
  51                u8  cpp_target;
  52                u32 page_offset;
  53                u32 page_size;
  54        } region;
  55};
  56
  57#define NFP_RESOURCE_TBL_SIZE           4096
  58#define NFP_RESOURCE_TBL_ENTRIES        (NFP_RESOURCE_TBL_SIZE /        \
  59                                         sizeof(struct nfp_resource_entry))
  60
  61struct nfp_resource {
  62        char name[NFP_RESOURCE_ENTRY_NAME_SZ + 1];
  63        u32 cpp_id;
  64        u64 addr;
  65        u64 size;
  66        struct nfp_cpp_mutex *mutex;
  67};
  68
  69static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res)
  70{
  71        struct nfp_resource_entry entry;
  72        u32 cpp_id, key;
  73        int ret, i;
  74
  75        cpp_id = NFP_CPP_ID(NFP_RESOURCE_TBL_TARGET, 3, 0);  /* Atomic read */
  76
  77        /* Search for a matching entry */
  78        if (!strcmp(res->name, NFP_RESOURCE_TBL_NAME)) {
  79                nfp_err(cpp, "Grabbing device lock not supported\n");
  80                return -EOPNOTSUPP;
  81        }
  82        key = crc32_posix(res->name, NFP_RESOURCE_ENTRY_NAME_SZ);
  83
  84        for (i = 0; i < NFP_RESOURCE_TBL_ENTRIES; i++) {
  85                u64 addr = NFP_RESOURCE_TBL_BASE +
  86                        sizeof(struct nfp_resource_entry) * i;
  87
  88                ret = nfp_cpp_read(cpp, cpp_id, addr, &entry, sizeof(entry));
  89                if (ret != sizeof(entry))
  90                        return -EIO;
  91
  92                if (entry.mutex.key != key)
  93                        continue;
  94
  95                /* Found key! */
  96                res->mutex =
  97                        nfp_cpp_mutex_alloc(cpp,
  98                                            NFP_RESOURCE_TBL_TARGET, addr, key);
  99                res->cpp_id = NFP_CPP_ID(entry.region.cpp_target,
 100                                         entry.region.cpp_action,
 101                                         entry.region.cpp_token);
 102                res->addr = (u64)entry.region.page_offset << 8;
 103                res->size = (u64)entry.region.page_size << 8;
 104
 105                return 0;
 106        }
 107
 108        return -ENOENT;
 109}
 110
 111static int
 112nfp_resource_try_acquire(struct nfp_cpp *cpp, struct nfp_resource *res,
 113                         struct nfp_cpp_mutex *dev_mutex)
 114{
 115        int err;
 116
 117        if (nfp_cpp_mutex_lock(dev_mutex))
 118                return -EINVAL;
 119
 120        err = nfp_cpp_resource_find(cpp, res);
 121        if (err)
 122                goto err_unlock_dev;
 123
 124        err = nfp_cpp_mutex_trylock(res->mutex);
 125        if (err)
 126                goto err_res_mutex_free;
 127
 128        nfp_cpp_mutex_unlock(dev_mutex);
 129
 130        return 0;
 131
 132err_res_mutex_free:
 133        nfp_cpp_mutex_free(res->mutex);
 134err_unlock_dev:
 135        nfp_cpp_mutex_unlock(dev_mutex);
 136
 137        return err;
 138}
 139
 140/**
 141 * nfp_resource_acquire() - Acquire a resource handle
 142 * @cpp:        NFP CPP handle
 143 * @name:       Name of the resource
 144 *
 145 * NOTE: This function locks the acquired resource
 146 *
 147 * Return: NFP Resource handle, or ERR_PTR()
 148 */
 149struct nfp_resource *
 150nfp_resource_acquire(struct nfp_cpp *cpp, const char *name)
 151{
 152        unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
 153        unsigned long err_at = jiffies + NFP_MUTEX_WAIT_ERROR * HZ;
 154        struct nfp_cpp_mutex *dev_mutex;
 155        struct nfp_resource *res;
 156        int err;
 157
 158        res = kzalloc(sizeof(*res), GFP_KERNEL);
 159        if (!res)
 160                return ERR_PTR(-ENOMEM);
 161
 162        strncpy(res->name, name, NFP_RESOURCE_ENTRY_NAME_SZ);
 163
 164        dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET,
 165                                        NFP_RESOURCE_TBL_BASE,
 166                                        NFP_RESOURCE_TBL_KEY);
 167        if (!dev_mutex) {
 168                kfree(res);
 169                return ERR_PTR(-ENOMEM);
 170        }
 171
 172        for (;;) {
 173                err = nfp_resource_try_acquire(cpp, res, dev_mutex);
 174                if (!err)
 175                        break;
 176                if (err != -EBUSY)
 177                        goto err_free;
 178
 179                err = msleep_interruptible(1);
 180                if (err != 0) {
 181                        err = -ERESTARTSYS;
 182                        goto err_free;
 183                }
 184
 185                if (time_is_before_eq_jiffies(warn_at)) {
 186                        warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
 187                        nfp_warn(cpp, "Warning: waiting for NFP resource %s\n",
 188                                 name);
 189                }
 190                if (time_is_before_eq_jiffies(err_at)) {
 191                        nfp_err(cpp, "Error: resource %s timed out\n", name);
 192                        err = -EBUSY;
 193                        goto err_free;
 194                }
 195        }
 196
 197        nfp_cpp_mutex_free(dev_mutex);
 198
 199        return res;
 200
 201err_free:
 202        nfp_cpp_mutex_free(dev_mutex);
 203        kfree(res);
 204        return ERR_PTR(err);
 205}
 206
 207/**
 208 * nfp_resource_release() - Release a NFP Resource handle
 209 * @res:        NFP Resource handle
 210 *
 211 * NOTE: This function implictly unlocks the resource handle
 212 */
 213void nfp_resource_release(struct nfp_resource *res)
 214{
 215        nfp_cpp_mutex_unlock(res->mutex);
 216        nfp_cpp_mutex_free(res->mutex);
 217        kfree(res);
 218}
 219
 220/**
 221 * nfp_resource_wait() - Wait for resource to appear
 222 * @cpp:        NFP CPP handle
 223 * @name:       Name of the resource
 224 * @secs:       Number of seconds to wait
 225 *
 226 * Wait for resource to appear in the resource table, grab and release
 227 * its lock.  The wait is jiffies-based, don't expect fine granularity.
 228 *
 229 * Return: 0 on success, errno otherwise.
 230 */
 231int nfp_resource_wait(struct nfp_cpp *cpp, const char *name, unsigned int secs)
 232{
 233        unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
 234        unsigned long err_at = jiffies + secs * HZ;
 235        struct nfp_resource *res;
 236
 237        while (true) {
 238                res = nfp_resource_acquire(cpp, name);
 239                if (!IS_ERR(res)) {
 240                        nfp_resource_release(res);
 241                        return 0;
 242                }
 243
 244                if (PTR_ERR(res) != -ENOENT) {
 245                        nfp_err(cpp, "error waiting for resource %s: %ld\n",
 246                                name, PTR_ERR(res));
 247                        return PTR_ERR(res);
 248                }
 249                if (time_is_before_eq_jiffies(err_at)) {
 250                        nfp_err(cpp, "timeout waiting for resource %s\n", name);
 251                        return -ETIMEDOUT;
 252                }
 253                if (time_is_before_eq_jiffies(warn_at)) {
 254                        warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
 255                        nfp_info(cpp, "waiting for NFP resource %s\n", name);
 256                }
 257                if (msleep_interruptible(10)) {
 258                        nfp_err(cpp, "wait for resource %s interrupted\n",
 259                                name);
 260                        return -ERESTARTSYS;
 261                }
 262        }
 263}
 264
 265/**
 266 * nfp_resource_cpp_id() - Return the cpp_id of a resource handle
 267 * @res:        NFP Resource handle
 268 *
 269 * Return: NFP CPP ID
 270 */
 271u32 nfp_resource_cpp_id(struct nfp_resource *res)
 272{
 273        return res->cpp_id;
 274}
 275
 276/**
 277 * nfp_resource_name() - Return the name of a resource handle
 278 * @res:        NFP Resource handle
 279 *
 280 * Return: const char pointer to the name of the resource
 281 */
 282const char *nfp_resource_name(struct nfp_resource *res)
 283{
 284        return res->name;
 285}
 286
 287/**
 288 * nfp_resource_address() - Return the address of a resource handle
 289 * @res:        NFP Resource handle
 290 *
 291 * Return: Address of the resource
 292 */
 293u64 nfp_resource_address(struct nfp_resource *res)
 294{
 295        return res->addr;
 296}
 297
 298/**
 299 * nfp_resource_size() - Return the size in bytes of a resource handle
 300 * @res:        NFP Resource handle
 301 *
 302 * Return: Size of the resource in bytes
 303 */
 304u64 nfp_resource_size(struct nfp_resource *res)
 305{
 306        return res->size;
 307}
 308
 309/**
 310 * nfp_resource_table_init() - Run initial checks on the resource table
 311 * @cpp:        NFP CPP handle
 312 *
 313 * Start-of-day init procedure for resource table.  Must be called before
 314 * any local resource table users may exist.
 315 *
 316 * Return: 0 on success, -errno on failure
 317 */
 318int nfp_resource_table_init(struct nfp_cpp *cpp)
 319{
 320        struct nfp_cpp_mutex *dev_mutex;
 321        int i, err;
 322
 323        err = nfp_cpp_mutex_reclaim(cpp, NFP_RESOURCE_TBL_TARGET,
 324                                    NFP_RESOURCE_TBL_BASE);
 325        if (err < 0) {
 326                nfp_err(cpp, "Error: failed to reclaim resource table mutex\n");
 327                return err;
 328        }
 329        if (err)
 330                nfp_warn(cpp, "Warning: busted main resource table mutex\n");
 331
 332        dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET,
 333                                        NFP_RESOURCE_TBL_BASE,
 334                                        NFP_RESOURCE_TBL_KEY);
 335        if (!dev_mutex)
 336                return -ENOMEM;
 337
 338        if (nfp_cpp_mutex_lock(dev_mutex)) {
 339                nfp_err(cpp, "Error: failed to claim resource table mutex\n");
 340                nfp_cpp_mutex_free(dev_mutex);
 341                return -EINVAL;
 342        }
 343
 344        /* Resource 0 is the dev_mutex, start from 1 */
 345        for (i = 1; i < NFP_RESOURCE_TBL_ENTRIES; i++) {
 346                u64 addr = NFP_RESOURCE_TBL_BASE +
 347                        sizeof(struct nfp_resource_entry) * i;
 348
 349                err = nfp_cpp_mutex_reclaim(cpp, NFP_RESOURCE_TBL_TARGET, addr);
 350                if (err < 0) {
 351                        nfp_err(cpp,
 352                                "Error: failed to reclaim resource %d mutex\n",
 353                                i);
 354                        goto err_unlock;
 355                }
 356                if (err)
 357                        nfp_warn(cpp, "Warning: busted resource %d mutex\n", i);
 358        }
 359
 360        err = 0;
 361err_unlock:
 362        nfp_cpp_mutex_unlock(dev_mutex);
 363        nfp_cpp_mutex_free(dev_mutex);
 364
 365        return err;
 366}
 367