GNU Linux-libre 4.14.313-gnu1
[releases.git] / drivers / net / ethernet / netronome / nfp / nfpcore / nfp_resource.c
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_wait() - Wait for resource to appear
254  * @cpp:        NFP CPP handle
255  * @name:       Name of the resource
256  * @secs:       Number of seconds to wait
257  *
258  * Wait for resource to appear in the resource table, grab and release
259  * its lock.  The wait is jiffies-based, don't expect fine granularity.
260  *
261  * Return: 0 on success, errno otherwise.
262  */
263 int nfp_resource_wait(struct nfp_cpp *cpp, const char *name, unsigned int secs)
264 {
265         unsigned long warn_at = jiffies + NFP_MUTEX_WAIT_FIRST_WARN * HZ;
266         unsigned long err_at = jiffies + secs * HZ;
267         struct nfp_resource *res;
268
269         while (true) {
270                 res = nfp_resource_acquire(cpp, name);
271                 if (!IS_ERR(res)) {
272                         nfp_resource_release(res);
273                         return 0;
274                 }
275
276                 if (PTR_ERR(res) != -ENOENT) {
277                         nfp_err(cpp, "error waiting for resource %s: %ld\n",
278                                 name, PTR_ERR(res));
279                         return PTR_ERR(res);
280                 }
281                 if (time_is_before_eq_jiffies(err_at)) {
282                         nfp_err(cpp, "timeout waiting for resource %s\n", name);
283                         return -ETIMEDOUT;
284                 }
285                 if (time_is_before_eq_jiffies(warn_at)) {
286                         warn_at = jiffies + NFP_MUTEX_WAIT_NEXT_WARN * HZ;
287                         nfp_info(cpp, "waiting for NFP resource %s\n", name);
288                 }
289                 if (msleep_interruptible(10)) {
290                         nfp_err(cpp, "wait for resource %s interrupted\n",
291                                 name);
292                         return -ERESTARTSYS;
293                 }
294         }
295 }
296
297 /**
298  * nfp_resource_cpp_id() - Return the cpp_id of a resource handle
299  * @res:        NFP Resource handle
300  *
301  * Return: NFP CPP ID
302  */
303 u32 nfp_resource_cpp_id(struct nfp_resource *res)
304 {
305         return res->cpp_id;
306 }
307
308 /**
309  * nfp_resource_name() - Return the name of a resource handle
310  * @res:        NFP Resource handle
311  *
312  * Return: const char pointer to the name of the resource
313  */
314 const char *nfp_resource_name(struct nfp_resource *res)
315 {
316         return res->name;
317 }
318
319 /**
320  * nfp_resource_address() - Return the address of a resource handle
321  * @res:        NFP Resource handle
322  *
323  * Return: Address of the resource
324  */
325 u64 nfp_resource_address(struct nfp_resource *res)
326 {
327         return res->addr;
328 }
329
330 /**
331  * nfp_resource_size() - Return the size in bytes of a resource handle
332  * @res:        NFP Resource handle
333  *
334  * Return: Size of the resource in bytes
335  */
336 u64 nfp_resource_size(struct nfp_resource *res)
337 {
338         return res->size;
339 }