xref: /linux/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c (revision fab183d632628381b466a41479489541ac0e29a0)
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  */
41 struct 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 
61 struct 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 
nfp_cpp_resource_find(struct nfp_cpp * cpp,struct nfp_resource * res)69 static 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 		if (!res->mutex)
100 			return -ENOMEM;
101 
102 		res->cpp_id = NFP_CPP_ID(entry.region.cpp_target,
103 					 entry.region.cpp_action,
104 					 entry.region.cpp_token);
105 		res->addr = (u64)entry.region.page_offset << 8;
106 		res->size = (u64)entry.region.page_size << 8;
107 
108 		return 0;
109 	}
110 
111 	return -ENOENT;
112 }
113 
114 static int
nfp_resource_try_acquire(struct nfp_cpp * cpp,struct nfp_resource * res,struct nfp_cpp_mutex * dev_mutex)115 nfp_resource_try_acquire(struct nfp_cpp *cpp, struct nfp_resource *res,
116 			 struct nfp_cpp_mutex *dev_mutex)
117 {
118 	int err;
119 
120 	if (nfp_cpp_mutex_lock(dev_mutex))
121 		return -EINVAL;
122 
123 	err = nfp_cpp_resource_find(cpp, res);
124 	if (err)
125 		goto err_unlock_dev;
126 
127 	err = nfp_cpp_mutex_trylock(res->mutex);
128 	if (err)
129 		goto err_res_mutex_free;
130 
131 	nfp_cpp_mutex_unlock(dev_mutex);
132 
133 	return 0;
134 
135 err_res_mutex_free:
136 	nfp_cpp_mutex_free(res->mutex);
137 err_unlock_dev:
138 	nfp_cpp_mutex_unlock(dev_mutex);
139 
140 	return err;
141 }
142 
143 /**
144  * nfp_resource_acquire() - Acquire a resource handle
145  * @cpp:	NFP CPP handle
146  * @name:	Name of the resource
147  *
148  * NOTE: This function locks the acquired resource
149  *
150  * Return: NFP Resource handle, or ERR_PTR()
151  */
152 struct nfp_resource *
nfp_resource_acquire(struct nfp_cpp * cpp,const char * name)153 nfp_resource_acquire(struct nfp_cpp *cpp, const char *name)
154 {
155 	unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
156 	unsigned long err_at = jiffies + NFP_MUTEX_WAIT_ERROR * HZ;
157 	struct nfp_cpp_mutex *dev_mutex;
158 	struct nfp_resource *res;
159 	int err;
160 
161 	res = kzalloc_obj(*res);
162 	if (!res)
163 		return ERR_PTR(-ENOMEM);
164 
165 	strscpy(res->name, name, sizeof(res->name));
166 
167 	dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET,
168 					NFP_RESOURCE_TBL_BASE,
169 					NFP_RESOURCE_TBL_KEY);
170 	if (!dev_mutex) {
171 		kfree(res);
172 		return ERR_PTR(-ENOMEM);
173 	}
174 
175 	for (;;) {
176 		err = nfp_resource_try_acquire(cpp, res, dev_mutex);
177 		if (!err)
178 			break;
179 		if (err != -EBUSY)
180 			goto err_free;
181 
182 		err = msleep_interruptible(1);
183 		if (err != 0) {
184 			err = -ERESTARTSYS;
185 			goto err_free;
186 		}
187 
188 		if (time_is_before_eq_jiffies(warn_at)) {
189 			warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
190 			nfp_warn(cpp, "Warning: waiting for NFP resource %s\n",
191 				 name);
192 		}
193 		if (time_is_before_eq_jiffies(err_at)) {
194 			nfp_err(cpp, "Error: resource %s timed out\n", name);
195 			err = -EBUSY;
196 			goto err_free;
197 		}
198 	}
199 
200 	nfp_cpp_mutex_free(dev_mutex);
201 
202 	return res;
203 
204 err_free:
205 	nfp_cpp_mutex_free(dev_mutex);
206 	kfree(res);
207 	return ERR_PTR(err);
208 }
209 
210 /**
211  * nfp_resource_release() - Release a NFP Resource handle
212  * @res:	NFP Resource handle
213  *
214  * NOTE: This function implictly unlocks the resource handle
215  */
nfp_resource_release(struct nfp_resource * res)216 void nfp_resource_release(struct nfp_resource *res)
217 {
218 	nfp_cpp_mutex_unlock(res->mutex);
219 	nfp_cpp_mutex_free(res->mutex);
220 	kfree(res);
221 }
222 
223 /**
224  * nfp_resource_wait() - Wait for resource to appear
225  * @cpp:	NFP CPP handle
226  * @name:	Name of the resource
227  * @secs:	Number of seconds to wait
228  *
229  * Wait for resource to appear in the resource table, grab and release
230  * its lock.  The wait is jiffies-based, don't expect fine granularity.
231  *
232  * Return: 0 on success, errno otherwise.
233  */
nfp_resource_wait(struct nfp_cpp * cpp,const char * name,unsigned int secs)234 int nfp_resource_wait(struct nfp_cpp *cpp, const char *name, unsigned int secs)
235 {
236 	unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
237 	unsigned long err_at = jiffies + secs * HZ;
238 	struct nfp_resource *res;
239 
240 	while (true) {
241 		res = nfp_resource_acquire(cpp, name);
242 		if (!IS_ERR(res)) {
243 			nfp_resource_release(res);
244 			return 0;
245 		}
246 
247 		if (PTR_ERR(res) != -ENOENT) {
248 			nfp_err(cpp, "error waiting for resource %s: %ld\n",
249 				name, PTR_ERR(res));
250 			return PTR_ERR(res);
251 		}
252 		if (time_is_before_eq_jiffies(err_at)) {
253 			nfp_err(cpp, "timeout waiting for resource %s\n", name);
254 			return -ETIMEDOUT;
255 		}
256 		if (time_is_before_eq_jiffies(warn_at)) {
257 			warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
258 			nfp_info(cpp, "waiting for NFP resource %s\n", name);
259 		}
260 		if (msleep_interruptible(10)) {
261 			nfp_err(cpp, "wait for resource %s interrupted\n",
262 				name);
263 			return -ERESTARTSYS;
264 		}
265 	}
266 }
267 
268 /**
269  * nfp_resource_cpp_id() - Return the cpp_id of a resource handle
270  * @res:	NFP Resource handle
271  *
272  * Return: NFP CPP ID
273  */
nfp_resource_cpp_id(struct nfp_resource * res)274 u32 nfp_resource_cpp_id(struct nfp_resource *res)
275 {
276 	return res->cpp_id;
277 }
278 
279 /**
280  * nfp_resource_name() - Return the name of a resource handle
281  * @res:	NFP Resource handle
282  *
283  * Return: const char pointer to the name of the resource
284  */
nfp_resource_name(struct nfp_resource * res)285 const char *nfp_resource_name(struct nfp_resource *res)
286 {
287 	return res->name;
288 }
289 
290 /**
291  * nfp_resource_address() - Return the address of a resource handle
292  * @res:	NFP Resource handle
293  *
294  * Return: Address of the resource
295  */
nfp_resource_address(struct nfp_resource * res)296 u64 nfp_resource_address(struct nfp_resource *res)
297 {
298 	return res->addr;
299 }
300 
301 /**
302  * nfp_resource_size() - Return the size in bytes of a resource handle
303  * @res:	NFP Resource handle
304  *
305  * Return: Size of the resource in bytes
306  */
nfp_resource_size(struct nfp_resource * res)307 u64 nfp_resource_size(struct nfp_resource *res)
308 {
309 	return res->size;
310 }
311 
312 /**
313  * nfp_resource_table_init() - Run initial checks on the resource table
314  * @cpp:	NFP CPP handle
315  *
316  * Start-of-day init procedure for resource table.  Must be called before
317  * any local resource table users may exist.
318  *
319  * Return: 0 on success, -errno on failure
320  */
nfp_resource_table_init(struct nfp_cpp * cpp)321 int nfp_resource_table_init(struct nfp_cpp *cpp)
322 {
323 	struct nfp_cpp_mutex *dev_mutex;
324 	int i, err;
325 
326 	err = nfp_cpp_mutex_reclaim(cpp, NFP_RESOURCE_TBL_TARGET,
327 				    NFP_RESOURCE_TBL_BASE);
328 	if (err < 0) {
329 		nfp_err(cpp, "Error: failed to reclaim resource table mutex\n");
330 		return err;
331 	}
332 	if (err)
333 		nfp_warn(cpp, "Warning: busted main resource table mutex\n");
334 
335 	dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET,
336 					NFP_RESOURCE_TBL_BASE,
337 					NFP_RESOURCE_TBL_KEY);
338 	if (!dev_mutex)
339 		return -ENOMEM;
340 
341 	if (nfp_cpp_mutex_lock(dev_mutex)) {
342 		nfp_err(cpp, "Error: failed to claim resource table mutex\n");
343 		nfp_cpp_mutex_free(dev_mutex);
344 		return -EINVAL;
345 	}
346 
347 	/* Resource 0 is the dev_mutex, start from 1 */
348 	for (i = 1; i < NFP_RESOURCE_TBL_ENTRIES; i++) {
349 		u64 addr = NFP_RESOURCE_TBL_BASE +
350 			sizeof(struct nfp_resource_entry) * i;
351 
352 		err = nfp_cpp_mutex_reclaim(cpp, NFP_RESOURCE_TBL_TARGET, addr);
353 		if (err < 0) {
354 			nfp_err(cpp,
355 				"Error: failed to reclaim resource %d mutex\n",
356 				i);
357 			goto err_unlock;
358 		}
359 		if (err)
360 			nfp_warn(cpp, "Warning: busted resource %d mutex\n", i);
361 	}
362 
363 	err = 0;
364 err_unlock:
365 	nfp_cpp_mutex_unlock(dev_mutex);
366 	nfp_cpp_mutex_free(dev_mutex);
367 
368 	return err;
369 }
370