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 | |
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(buff: 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, address: addr, kernel_vaddr: &entry, length: 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, address: addr, key_id: 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 |
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(mutex: 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(mutex: res->mutex); |
125 | if (err) |
126 | goto err_res_mutex_free; |
127 | |
128 | nfp_cpp_mutex_unlock(mutex: dev_mutex); |
129 | |
130 | return 0; |
131 | |
132 | err_res_mutex_free: |
133 | nfp_cpp_mutex_free(mutex: res->mutex); |
134 | err_unlock_dev: |
135 | nfp_cpp_mutex_unlock(mutex: 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 * |
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(size: sizeof(*res), GFP_KERNEL); |
159 | if (!res) |
160 | return ERR_PTR(error: -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(objp: res); |
169 | return ERR_PTR(error: -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(msecs: 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(mutex: dev_mutex); |
198 | |
199 | return res; |
200 | |
201 | err_free: |
202 | nfp_cpp_mutex_free(mutex: dev_mutex); |
203 | kfree(objp: res); |
204 | return ERR_PTR(error: 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 | */ |
213 | void nfp_resource_release(struct nfp_resource *res) |
214 | { |
215 | nfp_cpp_mutex_unlock(mutex: res->mutex); |
216 | nfp_cpp_mutex_free(mutex: res->mutex); |
217 | kfree(objp: 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 | */ |
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(ptr: res)) { |
240 | nfp_resource_release(res); |
241 | return 0; |
242 | } |
243 | |
244 | if (PTR_ERR(ptr: res) != -ENOENT) { |
245 | nfp_err(cpp, "error waiting for resource %s: %ld\n" , |
246 | name, PTR_ERR(res)); |
247 | return PTR_ERR(ptr: 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(msecs: 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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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(mutex: dev_mutex)) { |
339 | nfp_err(cpp, "Error: failed to claim resource table mutex\n" ); |
340 | nfp_cpp_mutex_free(mutex: 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, address: 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(mutex: dev_mutex); |
363 | nfp_cpp_mutex_free(mutex: dev_mutex); |
364 | |
365 | return err; |
366 | } |
367 | |