nfp_resource.c (8803B)
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}