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  * @owner:		NFP CPP Lock, interface owner
60  * @key:		NFP CPP Lock, posix_crc32(name, 8)
61  * @region:		Memory region descriptor
62  * @name:		ASCII, zero padded name
63  * @reserved
64  * @cpp_action:		CPP Action
65  * @cpp_token:		CPP Token
66  * @cpp_target:		CPP Target ID
67  * @page_offset:	256-byte page offset into target's CPP address
68  * @page_size:		size, in 256-byte pages
69  */
70 struct nfp_resource_entry {
71 	struct nfp_resource_entry_mutex {
72 		u32 owner;
73 		u32 key;
74 	} mutex;
75 	struct nfp_resource_entry_region {
76 		u8  name[NFP_RESOURCE_ENTRY_NAME_SZ];
77 		u8  reserved[5];
78 		u8  cpp_action;
79 		u8  cpp_token;
80 		u8  cpp_target;
81 		u32 page_offset;
82 		u32 page_size;
83 	} region;
84 };
85 
86 #define NFP_RESOURCE_TBL_SIZE		4096
87 #define NFP_RESOURCE_TBL_ENTRIES	(NFP_RESOURCE_TBL_SIZE /	\
88 					 sizeof(struct nfp_resource_entry))
89 
90 struct nfp_resource {
91 	char name[NFP_RESOURCE_ENTRY_NAME_SZ + 1];
92 	u32 cpp_id;
93 	u64 addr;
94 	u64 size;
95 	struct nfp_cpp_mutex *mutex;
96 };
97 
98 static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res)
99 {
100 	char name_pad[NFP_RESOURCE_ENTRY_NAME_SZ] = {};
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 	strncpy(name_pad, res->name, sizeof(name_pad));
108 
109 	/* Search for a matching entry */
110 	if (!memcmp(name_pad, NFP_RESOURCE_TBL_NAME "\0\0\0\0\0\0\0\0", 8)) {
111 		nfp_err(cpp, "Grabbing device lock not supported\n");
112 		return -EOPNOTSUPP;
113 	}
114 	key = crc32_posix(name_pad, sizeof(name_pad));
115 
116 	for (i = 0; i < NFP_RESOURCE_TBL_ENTRIES; i++) {
117 		u64 addr = NFP_RESOURCE_TBL_BASE +
118 			sizeof(struct nfp_resource_entry) * i;
119 
120 		ret = nfp_cpp_read(cpp, cpp_id, addr, &entry, sizeof(entry));
121 		if (ret != sizeof(entry))
122 			return -EIO;
123 
124 		if (entry.mutex.key != key)
125 			continue;
126 
127 		/* Found key! */
128 		res->mutex =
129 			nfp_cpp_mutex_alloc(cpp,
130 					    NFP_RESOURCE_TBL_TARGET, addr, key);
131 		res->cpp_id = NFP_CPP_ID(entry.region.cpp_target,
132 					 entry.region.cpp_action,
133 					 entry.region.cpp_token);
134 		res->addr = (u64)entry.region.page_offset << 8;
135 		res->size = (u64)entry.region.page_size << 8;
136 
137 		return 0;
138 	}
139 
140 	return -ENOENT;
141 }
142 
143 static int
144 nfp_resource_try_acquire(struct nfp_cpp *cpp, struct nfp_resource *res,
145 			 struct nfp_cpp_mutex *dev_mutex)
146 {
147 	int err;
148 
149 	if (nfp_cpp_mutex_lock(dev_mutex))
150 		return -EINVAL;
151 
152 	err = nfp_cpp_resource_find(cpp, res);
153 	if (err)
154 		goto err_unlock_dev;
155 
156 	err = nfp_cpp_mutex_trylock(res->mutex);
157 	if (err)
158 		goto err_res_mutex_free;
159 
160 	nfp_cpp_mutex_unlock(dev_mutex);
161 
162 	return 0;
163 
164 err_res_mutex_free:
165 	nfp_cpp_mutex_free(res->mutex);
166 err_unlock_dev:
167 	nfp_cpp_mutex_unlock(dev_mutex);
168 
169 	return err;
170 }
171 
172 /**
173  * nfp_resource_acquire() - Acquire a resource handle
174  * @cpp:	NFP CPP handle
175  * @name:	Name of the resource
176  *
177  * NOTE: This function locks the acquired resource
178  *
179  * Return: NFP Resource handle, or ERR_PTR()
180  */
181 struct nfp_resource *
182 nfp_resource_acquire(struct nfp_cpp *cpp, const char *name)
183 {
184 	unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
185 	unsigned long err_at = jiffies + NFP_MUTEX_WAIT_ERROR * HZ;
186 	struct nfp_cpp_mutex *dev_mutex;
187 	struct nfp_resource *res;
188 	int err;
189 
190 	res = kzalloc(sizeof(*res), GFP_KERNEL);
191 	if (!res)
192 		return ERR_PTR(-ENOMEM);
193 
194 	strncpy(res->name, name, NFP_RESOURCE_ENTRY_NAME_SZ);
195 
196 	dev_mutex = nfp_cpp_mutex_alloc(cpp, NFP_RESOURCE_TBL_TARGET,
197 					NFP_RESOURCE_TBL_BASE,
198 					NFP_RESOURCE_TBL_KEY);
199 	if (!dev_mutex) {
200 		kfree(res);
201 		return ERR_PTR(-ENOMEM);
202 	}
203 
204 	for (;;) {
205 		err = nfp_resource_try_acquire(cpp, res, dev_mutex);
206 		if (!err)
207 			break;
208 		if (err != -EBUSY)
209 			goto err_free;
210 
211 		err = msleep_interruptible(1);
212 		if (err != 0) {
213 			err = -ERESTARTSYS;
214 			goto err_free;
215 		}
216 
217 		if (time_is_before_eq_jiffies(warn_at)) {
218 			warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
219 			nfp_warn(cpp, "Warning: waiting for NFP resource %s\n",
220 				 name);
221 		}
222 		if (time_is_before_eq_jiffies(err_at)) {
223 			nfp_err(cpp, "Error: resource %s timed out\n", name);
224 			err = -EBUSY;
225 			goto err_free;
226 		}
227 	}
228 
229 	nfp_cpp_mutex_free(dev_mutex);
230 
231 	return res;
232 
233 err_free:
234 	nfp_cpp_mutex_free(dev_mutex);
235 	kfree(res);
236 	return ERR_PTR(err);
237 }
238 
239 /**
240  * nfp_resource_release() - Release a NFP Resource handle
241  * @res:	NFP Resource handle
242  *
243  * NOTE: This function implictly unlocks the resource handle
244  */
245 void nfp_resource_release(struct nfp_resource *res)
246 {
247 	nfp_cpp_mutex_unlock(res->mutex);
248 	nfp_cpp_mutex_free(res->mutex);
249 	kfree(res);
250 }
251 
252 /**
253  * nfp_resource_cpp_id() - Return the cpp_id of a resource handle
254  * @res:	NFP Resource handle
255  *
256  * Return: NFP CPP ID
257  */
258 u32 nfp_resource_cpp_id(struct nfp_resource *res)
259 {
260 	return res->cpp_id;
261 }
262 
263 /**
264  * nfp_resource_name() - Return the name of a resource handle
265  * @res:	NFP Resource handle
266  *
267  * Return: const char pointer to the name of the resource
268  */
269 const char *nfp_resource_name(struct nfp_resource *res)
270 {
271 	return res->name;
272 }
273 
274 /**
275  * nfp_resource_address() - Return the address of a resource handle
276  * @res:	NFP Resource handle
277  *
278  * Return: Address of the resource
279  */
280 u64 nfp_resource_address(struct nfp_resource *res)
281 {
282 	return res->addr;
283 }
284 
285 /**
286  * nfp_resource_size() - Return the size in bytes of a resource handle
287  * @res:	NFP Resource handle
288  *
289  * Return: Size of the resource in bytes
290  */
291 u64 nfp_resource_size(struct nfp_resource *res)
292 {
293 	return res->size;
294 }
295