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 		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 
111 static int
nfp_resource_try_acquire(struct nfp_cpp * cpp,struct nfp_resource * res,struct nfp_cpp_mutex * dev_mutex)112 nfp_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 
132 err_res_mutex_free:
133 	nfp_cpp_mutex_free(res->mutex);
134 err_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  */
149 struct nfp_resource *
nfp_resource_acquire(struct nfp_cpp * cpp,const char * name)150 nfp_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 	strscpy(res->name, name, sizeof(res->name));
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 
201 err_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  */
nfp_resource_release(struct nfp_resource * res)213 void 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  */
nfp_resource_wait(struct nfp_cpp * cpp,const char * name,unsigned int secs)231 int 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  */
nfp_resource_cpp_id(struct nfp_resource * res)271 u32 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  */
nfp_resource_name(struct nfp_resource * res)282 const 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  */
nfp_resource_address(struct nfp_resource * res)293 u64 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  */
nfp_resource_size(struct nfp_resource * res)304 u64 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  */
nfp_resource_table_init(struct nfp_cpp * cpp)318 int 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;
361 err_unlock:
362 	nfp_cpp_mutex_unlock(dev_mutex);
363 	nfp_cpp_mutex_free(dev_mutex);
364 
365 	return err;
366 }
367