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