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 * @mutex: NFP CPP Lock 60 * @mutex.owner: NFP CPP Lock, interface owner 61 * @mutex.key: NFP CPP Lock, posix_crc32(name, 8) 62 * @region: Memory region descriptor 63 * @region.name: ASCII, zero padded name 64 * @region.reserved: padding 65 * @region.cpp_action: CPP Action 66 * @region.cpp_token: CPP Token 67 * @region.cpp_target: CPP Target ID 68 * @region.page_offset: 256-byte page offset into target's CPP address 69 * @region.page_size: size, in 256-byte pages 70 */ 71 struct nfp_resource_entry { 72 struct nfp_resource_entry_mutex { 73 u32 owner; 74 u32 key; 75 } mutex; 76 struct nfp_resource_entry_region { 77 u8 name[NFP_RESOURCE_ENTRY_NAME_SZ]; 78 u8 reserved[5]; 79 u8 cpp_action; 80 u8 cpp_token; 81 u8 cpp_target; 82 u32 page_offset; 83 u32 page_size; 84 } region; 85 }; 86 87 #define NFP_RESOURCE_TBL_SIZE 4096 88 #define NFP_RESOURCE_TBL_ENTRIES (NFP_RESOURCE_TBL_SIZE / \ 89 sizeof(struct nfp_resource_entry)) 90 91 struct nfp_resource { 92 char name[NFP_RESOURCE_ENTRY_NAME_SZ + 1]; 93 u32 cpp_id; 94 u64 addr; 95 u64 size; 96 struct nfp_cpp_mutex *mutex; 97 }; 98 99 static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res) 100 { 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 /* Search for a matching entry */ 108 if (!strcmp(res->name, NFP_RESOURCE_TBL_NAME)) { 109 nfp_err(cpp, "Grabbing device lock not supported\n"); 110 return -EOPNOTSUPP; 111 } 112 key = crc32_posix(res->name, NFP_RESOURCE_ENTRY_NAME_SZ); 113 114 for (i = 0; i < NFP_RESOURCE_TBL_ENTRIES; i++) { 115 u64 addr = NFP_RESOURCE_TBL_BASE + 116 sizeof(struct nfp_resource_entry) * i; 117 118 ret = nfp_cpp_read(cpp, cpp_id, addr, &entry, sizeof(entry)); 119 if (ret != sizeof(entry)) 120 return -EIO; 121 122 if (entry.mutex.key != key) 123 continue; 124 125 /* Found key! */ 126 res->mutex = 127 nfp_cpp_mutex_alloc(cpp, 128 NFP_RESOURCE_TBL_TARGET, addr, key); 129 res->cpp_id = NFP_CPP_ID(entry.region.cpp_target, 130 entry.region.cpp_action, 131 entry.region.cpp_token); 132 res->addr = (u64)entry.region.page_offset << 8; 133 res->size = (u64)entry.region.page_size << 8; 134 135 return 0; 136 } 137 138 return -ENOENT; 139 } 140 141 static int 142 nfp_resource_try_acquire(struct nfp_cpp *cpp, struct nfp_resource *res, 143 struct nfp_cpp_mutex *dev_mutex) 144 { 145 int err; 146 147 if (nfp_cpp_mutex_lock(dev_mutex)) 148 return -EINVAL; 149 150 err = nfp_cpp_resource_find(cpp, res); 151 if (err) 152 goto err_unlock_dev; 153 154 err = nfp_cpp_mutex_trylock(res->mutex); 155 if (err) 156 goto err_res_mutex_free; 157 158 nfp_cpp_mutex_unlock(dev_mutex); 159 160 return 0; 161 162 err_res_mutex_free: 163 nfp_cpp_mutex_free(res->mutex); 164 err_unlock_dev: 165 nfp_cpp_mutex_unlock(dev_mutex); 166 167 return err; 168 } 169 170 /** 171 * nfp_resource_acquire() - Acquire a resource handle 172 * @cpp: NFP CPP handle 173 * @name: Name of the resource 174 * 175 * NOTE: This function locks the acquired resource 176 * 177 * Return: NFP Resource handle, or ERR_PTR() 178 */ 179 struct nfp_resource * 180 nfp_resource_acquire(struct nfp_cpp *cpp, const char *name) 181 { 182 unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ; 183 unsigned long err_at = jiffies + NFP_MUTEX_WAIT_ERROR * HZ; 184 struct nfp_cpp_mutex *dev_mutex; 185 struct nfp_resource *res; 186 int err; 187 188 res = kzalloc(sizeof(*res), GFP_KERNEL); 189 if (!res) 190 return ERR_PTR(-ENOMEM); 191 192 strncpy(res->name, name, NFP_RESOURCE_ENTRY_NAME_SZ); 193 194 dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET, 195 NFP_RESOURCE_TBL_BASE, 196 NFP_RESOURCE_TBL_KEY); 197 if (!dev_mutex) { 198 kfree(res); 199 return ERR_PTR(-ENOMEM); 200 } 201 202 for (;;) { 203 err = nfp_resource_try_acquire(cpp, res, dev_mutex); 204 if (!err) 205 break; 206 if (err != -EBUSY) 207 goto err_free; 208 209 err = msleep_interruptible(1); 210 if (err != 0) { 211 err = -ERESTARTSYS; 212 goto err_free; 213 } 214 215 if (time_is_before_eq_jiffies(warn_at)) { 216 warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ; 217 nfp_warn(cpp, "Warning: waiting for NFP resource %s\n", 218 name); 219 } 220 if (time_is_before_eq_jiffies(err_at)) { 221 nfp_err(cpp, "Error: resource %s timed out\n", name); 222 err = -EBUSY; 223 goto err_free; 224 } 225 } 226 227 nfp_cpp_mutex_free(dev_mutex); 228 229 return res; 230 231 err_free: 232 nfp_cpp_mutex_free(dev_mutex); 233 kfree(res); 234 return ERR_PTR(err); 235 } 236 237 /** 238 * nfp_resource_release() - Release a NFP Resource handle 239 * @res: NFP Resource handle 240 * 241 * NOTE: This function implictly unlocks the resource handle 242 */ 243 void nfp_resource_release(struct nfp_resource *res) 244 { 245 nfp_cpp_mutex_unlock(res->mutex); 246 nfp_cpp_mutex_free(res->mutex); 247 kfree(res); 248 } 249 250 /** 251 * nfp_resource_wait() - Wait for resource to appear 252 * @cpp: NFP CPP handle 253 * @name: Name of the resource 254 * @secs: Number of seconds to wait 255 * 256 * Wait for resource to appear in the resource table, grab and release 257 * its lock. The wait is jiffies-based, don't expect fine granularity. 258 * 259 * Return: 0 on success, errno otherwise. 260 */ 261 int nfp_resource_wait(struct nfp_cpp *cpp, const char *name, unsigned int secs) 262 { 263 unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ; 264 unsigned long err_at = jiffies + secs * HZ; 265 struct nfp_resource *res; 266 267 while (true) { 268 res = nfp_resource_acquire(cpp, name); 269 if (!IS_ERR(res)) { 270 nfp_resource_release(res); 271 return 0; 272 } 273 274 if (PTR_ERR(res) != -ENOENT) { 275 nfp_err(cpp, "error waiting for resource %s: %ld\n", 276 name, PTR_ERR(res)); 277 return PTR_ERR(res); 278 } 279 if (time_is_before_eq_jiffies(err_at)) { 280 nfp_err(cpp, "timeout waiting for resource %s\n", name); 281 return -ETIMEDOUT; 282 } 283 if (time_is_before_eq_jiffies(warn_at)) { 284 warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ; 285 nfp_info(cpp, "waiting for NFP resource %s\n", name); 286 } 287 if (msleep_interruptible(10)) { 288 nfp_err(cpp, "wait for resource %s interrupted\n", 289 name); 290 return -ERESTARTSYS; 291 } 292 } 293 } 294 295 /** 296 * nfp_resource_cpp_id() - Return the cpp_id of a resource handle 297 * @res: NFP Resource handle 298 * 299 * Return: NFP CPP ID 300 */ 301 u32 nfp_resource_cpp_id(struct nfp_resource *res) 302 { 303 return res->cpp_id; 304 } 305 306 /** 307 * nfp_resource_name() - Return the name of a resource handle 308 * @res: NFP Resource handle 309 * 310 * Return: const char pointer to the name of the resource 311 */ 312 const char *nfp_resource_name(struct nfp_resource *res) 313 { 314 return res->name; 315 } 316 317 /** 318 * nfp_resource_address() - Return the address of a resource handle 319 * @res: NFP Resource handle 320 * 321 * Return: Address of the resource 322 */ 323 u64 nfp_resource_address(struct nfp_resource *res) 324 { 325 return res->addr; 326 } 327 328 /** 329 * nfp_resource_size() - Return the size in bytes of a resource handle 330 * @res: NFP Resource handle 331 * 332 * Return: Size of the resource in bytes 333 */ 334 u64 nfp_resource_size(struct nfp_resource *res) 335 { 336 return res->size; 337 } 338 339 /** 340 * nfp_resource_table_init() - Run initial checks on the resource table 341 * @cpp: NFP CPP handle 342 * 343 * Start-of-day init procedure for resource table. Must be called before 344 * any local resource table users may exist. 345 * 346 * Return: 0 on success, -errno on failure 347 */ 348 int nfp_resource_table_init(struct nfp_cpp *cpp) 349 { 350 struct nfp_cpp_mutex *dev_mutex; 351 int i, err; 352 353 err = nfp_cpp_mutex_reclaim(cpp, NFP_RESOURCE_TBL_TARGET, 354 NFP_RESOURCE_TBL_BASE); 355 if (err < 0) { 356 nfp_err(cpp, "Error: failed to reclaim resource table mutex\n"); 357 return err; 358 } 359 if (err) 360 nfp_warn(cpp, "Warning: busted main resource table mutex\n"); 361 362 dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET, 363 NFP_RESOURCE_TBL_BASE, 364 NFP_RESOURCE_TBL_KEY); 365 if (!dev_mutex) 366 return -ENOMEM; 367 368 if (nfp_cpp_mutex_lock(dev_mutex)) { 369 nfp_err(cpp, "Error: failed to claim resource table mutex\n"); 370 nfp_cpp_mutex_free(dev_mutex); 371 return -EINVAL; 372 } 373 374 /* Resource 0 is the dev_mutex, start from 1 */ 375 for (i = 1; i < NFP_RESOURCE_TBL_ENTRIES; i++) { 376 u64 addr = NFP_RESOURCE_TBL_BASE + 377 sizeof(struct nfp_resource_entry) * i; 378 379 err = nfp_cpp_mutex_reclaim(cpp, NFP_RESOURCE_TBL_TARGET, addr); 380 if (err < 0) { 381 nfp_err(cpp, 382 "Error: failed to reclaim resource %d mutex\n", 383 i); 384 goto err_unlock; 385 } 386 if (err) 387 nfp_warn(cpp, "Warning: busted resource %d mutex\n", i); 388 } 389 390 err = 0; 391 err_unlock: 392 nfp_cpp_mutex_unlock(dev_mutex); 393 nfp_cpp_mutex_free(dev_mutex); 394 395 return err; 396 } 397