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	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
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}
367