linux/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015-2017 Netronome Systems, Inc.
   3 *
   4 * This software is dual licensed under the GNU General License Version 2,
   5 * June 1991 as shown in the file COPYING in the top-level directory of this
   6 * source tree or the BSD 2-Clause License provided below.  You have the
   7 * option to license this software under the complete terms of either license.
   8 *
   9 * The BSD 2-Clause License:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      1. Redistributions of source code must retain the above
  16 *         copyright notice, this list of conditions and the following
  17 *         disclaimer.
  18 *
  19 *      2. Redistributions in binary form must reproduce the above
  20 *         copyright notice, this list of conditions and the following
  21 *         disclaimer in the documentation and/or other materials
  22 *         provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34/*
  35 * nfp_resource.c
  36 * Author: Jakub Kicinski <jakub.kicinski@netronome.com>
  37 *         Jason McMullan <jason.mcmullan@netronome.com>
  38 */
  39#include <linux/delay.h>
  40#include <linux/kernel.h>
  41#include <linux/slab.h>
  42
  43#include "crc32.h"
  44#include "nfp.h"
  45#include "nfp_cpp.h"
  46#include "nfp6000/nfp6000.h"
  47
  48#define NFP_RESOURCE_TBL_TARGET         NFP_CPP_TARGET_MU
  49#define NFP_RESOURCE_TBL_BASE           0x8100000000ULL
  50
  51/* NFP Resource Table self-identifier */
  52#define NFP_RESOURCE_TBL_NAME           "nfp.res"
  53#define NFP_RESOURCE_TBL_KEY            0x00000000 /* Special key for entry 0 */
  54
  55#define NFP_RESOURCE_ENTRY_NAME_SZ      8
  56
  57/**
  58 * struct nfp_resource_entry - Resource table entry
  59 * @owner:              NFP CPP Lock, interface owner
  60 * @key:                NFP CPP Lock, posix_crc32(name, 8)
  61 * @region:             Memory region descriptor
  62 * @name:               ASCII, zero padded name
  63 * @reserved
  64 * @cpp_action:         CPP Action
  65 * @cpp_token:          CPP Token
  66 * @cpp_target:         CPP Target ID
  67 * @page_offset:        256-byte page offset into target's CPP address
  68 * @page_size:          size, in 256-byte pages
  69 */
  70struct nfp_resource_entry {
  71        struct nfp_resource_entry_mutex {
  72                u32 owner;
  73                u32 key;
  74        } mutex;
  75        struct nfp_resource_entry_region {
  76                u8  name[NFP_RESOURCE_ENTRY_NAME_SZ];
  77                u8  reserved[5];
  78                u8  cpp_action;
  79                u8  cpp_token;
  80                u8  cpp_target;
  81                u32 page_offset;
  82                u32 page_size;
  83        } region;
  84};
  85
  86#define NFP_RESOURCE_TBL_SIZE           4096
  87#define NFP_RESOURCE_TBL_ENTRIES        (NFP_RESOURCE_TBL_SIZE /        \
  88                                         sizeof(struct nfp_resource_entry))
  89
  90struct nfp_resource {
  91        char name[NFP_RESOURCE_ENTRY_NAME_SZ + 1];
  92        u32 cpp_id;
  93        u64 addr;
  94        u64 size;
  95        struct nfp_cpp_mutex *mutex;
  96};
  97
  98static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res)
  99{
 100        char name_pad[NFP_RESOURCE_ENTRY_NAME_SZ] = {};
 101        struct nfp_resource_entry entry;
 102        u32 cpp_id, key;
 103        int ret, i;
 104
 105        cpp_id = NFP_CPP_ID(NFP_RESOURCE_TBL_TARGET, 3, 0);  /* Atomic read */
 106
 107        strncpy(name_pad, res->name, sizeof(name_pad));
 108
 109        /* Search for a matching entry */
 110        if (!memcmp(name_pad, NFP_RESOURCE_TBL_NAME "\0\0\0\0\0\0\0\0", 8)) {
 111                nfp_err(cpp, "Grabbing device lock not supported\n");
 112                return -EOPNOTSUPP;
 113        }
 114        key = crc32_posix(name_pad, sizeof(name_pad));
 115
 116        for (i = 0; i < NFP_RESOURCE_TBL_ENTRIES; i++) {
 117                u64 addr = NFP_RESOURCE_TBL_BASE +
 118                        sizeof(struct nfp_resource_entry) * i;
 119
 120                ret = nfp_cpp_read(cpp, cpp_id, addr, &entry, sizeof(entry));
 121                if (ret != sizeof(entry))
 122                        return -EIO;
 123
 124                if (entry.mutex.key != key)
 125                        continue;
 126
 127                /* Found key! */
 128                res->mutex =
 129                        nfp_cpp_mutex_alloc(cpp,
 130                                            NFP_RESOURCE_TBL_TARGET, addr, key);
 131                res->cpp_id = NFP_CPP_ID(entry.region.cpp_target,
 132                                         entry.region.cpp_action,
 133                                         entry.region.cpp_token);
 134                res->addr = (u64)entry.region.page_offset << 8;
 135                res->size = (u64)entry.region.page_size << 8;
 136
 137                return 0;
 138        }
 139
 140        return -ENOENT;
 141}
 142
 143static int
 144nfp_resource_try_acquire(struct nfp_cpp *cpp, struct nfp_resource *res,
 145                         struct nfp_cpp_mutex *dev_mutex)
 146{
 147        int err;
 148
 149        if (nfp_cpp_mutex_lock(dev_mutex))
 150                return -EINVAL;
 151
 152        err = nfp_cpp_resource_find(cpp, res);
 153        if (err)
 154                goto err_unlock_dev;
 155
 156        err = nfp_cpp_mutex_trylock(res->mutex);
 157        if (err)
 158                goto err_res_mutex_free;
 159
 160        nfp_cpp_mutex_unlock(dev_mutex);
 161
 162        return 0;
 163
 164err_res_mutex_free:
 165        nfp_cpp_mutex_free(res->mutex);
 166err_unlock_dev:
 167        nfp_cpp_mutex_unlock(dev_mutex);
 168
 169        return err;
 170}
 171
 172/**
 173 * nfp_resource_acquire() - Acquire a resource handle
 174 * @cpp:        NFP CPP handle
 175 * @name:       Name of the resource
 176 *
 177 * NOTE: This function locks the acquired resource
 178 *
 179 * Return: NFP Resource handle, or ERR_PTR()
 180 */
 181struct nfp_resource *
 182nfp_resource_acquire(struct nfp_cpp *cpp, const char *name)
 183{
 184        unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
 185        unsigned long err_at = jiffies + NFP_MUTEX_WAIT_ERROR * HZ;
 186        struct nfp_cpp_mutex *dev_mutex;
 187        struct nfp_resource *res;
 188        int err;
 189
 190        res = kzalloc(sizeof(*res), GFP_KERNEL);
 191        if (!res)
 192                return ERR_PTR(-ENOMEM);
 193
 194        strncpy(res->name, name, NFP_RESOURCE_ENTRY_NAME_SZ);
 195
 196        dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET,
 197                                        NFP_RESOURCE_TBL_BASE,
 198                                        NFP_RESOURCE_TBL_KEY);
 199        if (!dev_mutex) {
 200                kfree(res);
 201                return ERR_PTR(-ENOMEM);
 202        }
 203
 204        for (;;) {
 205                err = nfp_resource_try_acquire(cpp, res, dev_mutex);
 206                if (!err)
 207                        break;
 208                if (err != -EBUSY)
 209                        goto err_free;
 210
 211                err = msleep_interruptible(1);
 212                if (err != 0) {
 213                        err = -ERESTARTSYS;
 214                        goto err_free;
 215                }
 216
 217                if (time_is_before_eq_jiffies(warn_at)) {
 218                        warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
 219                        nfp_warn(cpp, "Warning: waiting for NFP resource %s\n",
 220                                 name);
 221                }
 222                if (time_is_before_eq_jiffies(err_at)) {
 223                        nfp_err(cpp, "Error: resource %s timed out\n", name);
 224                        err = -EBUSY;
 225                        goto err_free;
 226                }
 227        }
 228
 229        nfp_cpp_mutex_free(dev_mutex);
 230
 231        return res;
 232
 233err_free:
 234        nfp_cpp_mutex_free(dev_mutex);
 235        kfree(res);
 236        return ERR_PTR(err);
 237}
 238
 239/**
 240 * nfp_resource_release() - Release a NFP Resource handle
 241 * @res:        NFP Resource handle
 242 *
 243 * NOTE: This function implictly unlocks the resource handle
 244 */
 245void nfp_resource_release(struct nfp_resource *res)
 246{
 247        nfp_cpp_mutex_unlock(res->mutex);
 248        nfp_cpp_mutex_free(res->mutex);
 249        kfree(res);
 250}
 251
 252/**
 253 * nfp_resource_wait() - Wait for resource to appear
 254 * @cpp:        NFP CPP handle
 255 * @name:       Name of the resource
 256 * @secs:       Number of seconds to wait
 257 *
 258 * Wait for resource to appear in the resource table, grab and release
 259 * its lock.  The wait is jiffies-based, don't expect fine granularity.
 260 *
 261 * Return: 0 on success, errno otherwise.
 262 */
 263int nfp_resource_wait(struct nfp_cpp *cpp, const char *name, unsigned int secs)
 264{
 265        unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
 266        unsigned long err_at = jiffies + secs * HZ;
 267        struct nfp_resource *res;
 268
 269        while (true) {
 270                res = nfp_resource_acquire(cpp, name);
 271                if (!IS_ERR(res)) {
 272                        nfp_resource_release(res);
 273                        return 0;
 274                }
 275
 276                if (PTR_ERR(res) != -ENOENT) {
 277                        nfp_err(cpp, "error waiting for resource %s: %ld\n",
 278                                name, PTR_ERR(res));
 279                        return PTR_ERR(res);
 280                }
 281                if (time_is_before_eq_jiffies(err_at)) {
 282                        nfp_err(cpp, "timeout waiting for resource %s\n", name);
 283                        return -ETIMEDOUT;
 284                }
 285                if (time_is_before_eq_jiffies(warn_at)) {
 286                        warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
 287                        nfp_info(cpp, "waiting for NFP resource %s\n", name);
 288                }
 289                if (msleep_interruptible(10)) {
 290                        nfp_err(cpp, "wait for resource %s interrupted\n",
 291                                name);
 292                        return -ERESTARTSYS;
 293                }
 294        }
 295}
 296
 297/**
 298 * nfp_resource_cpp_id() - Return the cpp_id of a resource handle
 299 * @res:        NFP Resource handle
 300 *
 301 * Return: NFP CPP ID
 302 */
 303u32 nfp_resource_cpp_id(struct nfp_resource *res)
 304{
 305        return res->cpp_id;
 306}
 307
 308/**
 309 * nfp_resource_name() - Return the name of a resource handle
 310 * @res:        NFP Resource handle
 311 *
 312 * Return: const char pointer to the name of the resource
 313 */
 314const char *nfp_resource_name(struct nfp_resource *res)
 315{
 316        return res->name;
 317}
 318
 319/**
 320 * nfp_resource_address() - Return the address of a resource handle
 321 * @res:        NFP Resource handle
 322 *
 323 * Return: Address of the resource
 324 */
 325u64 nfp_resource_address(struct nfp_resource *res)
 326{
 327        return res->addr;
 328}
 329
 330/**
 331 * nfp_resource_size() - Return the size in bytes of a resource handle
 332 * @res:        NFP Resource handle
 333 *
 334 * Return: Size of the resource in bytes
 335 */
 336u64 nfp_resource_size(struct nfp_resource *res)
 337{
 338        return res->size;
 339}
 340