Commit | Line | Data |
---|---|---|
800f879a AT |
1 | /* |
2 | * Copyright 2010-2017 Intel Corporation. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License, version 2, | |
6 | * as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | * | |
13 | * Disclaimer: The codes contained in these modules may be specific to | |
14 | * the Intel Software Development Platform codenamed Knights Ferry, | |
15 | * and the Intel product codenamed Knights Corner, and are not backward | |
16 | * compatible with other Intel products. Additionally, Intel will NOT | |
17 | * support the codes or instruction set in future products. | |
18 | * | |
19 | * Intel offers no warranty of any kind regarding the code. This code is | |
20 | * licensed on an "AS IS" basis and Intel is not obligated to provide | |
21 | * any support, assistance, installation, training, or other services | |
22 | * of any kind. Intel is also not obligated to provide any updates, | |
23 | * enhancements or extensions. Intel specifically disclaims any warranty | |
24 | * of merchantability, non-infringement, fitness for any particular | |
25 | * purpose, and any other warranty. | |
26 | * | |
27 | * Further, Intel disclaims all liability of any kind, including but | |
28 | * not limited to liability for infringement of any proprietary rights, | |
29 | * relating to the use of the code, even if Intel is notified of the | |
30 | * possibility of such liability. Except as expressly stated in an Intel | |
31 | * license agreement provided with this code and agreed upon with Intel, | |
32 | * no license, express or implied, by estoppel or otherwise, to any | |
33 | * intellectual property rights is granted herein. | |
34 | */ | |
35 | ||
36 | /* | |
37 | * Port reservation mechnism. | |
38 | * Since this goes with SCIF it must be available for any OS | |
39 | * and should not consume IP ports. Therefore, roll our own. | |
40 | * This is not required to be high performance, so a simple bit | |
41 | * array should do just fine. | |
42 | * | |
43 | * API specification (loosely): | |
44 | * | |
45 | * uint16_t port | |
46 | * Port number is a 16 bit unsigned integer | |
47 | * | |
48 | * uint16_t rsrv_scif_port(uint16_t) | |
49 | * reserve specified port # | |
50 | * returns port #, or 0 if port unavailable. | |
51 | * | |
52 | * uint16_t get_scif_port(void) | |
53 | * reserve any available port # | |
54 | * returns port #, or 0 if no ports available | |
55 | * | |
56 | * void put_scif_port(uint16_t) | |
57 | * release port # | |
58 | * | |
59 | * Reserved ports comes from the lower end of the allocatable range, | |
60 | * and is reserved only in the sense that get_scif_port() won't use | |
61 | * them and there is only a predefined count of them available. | |
62 | */ | |
63 | ||
64 | #include <mic/micscif.h> | |
65 | ||
66 | /* | |
67 | * Manifests | |
68 | * Port counts must be an integer multiple of 64 | |
69 | */ | |
70 | ||
71 | #define SCIF_PORT_BASE 0x0000 /* Start port (port reserved if 0) */ | |
72 | #define SCIF_PORT_COUNT 0x10000 /* Ports available */ | |
73 | ||
74 | #if SCIF_PORT_RSVD > (SCIF_PORT_COUNT/2) | |
75 | #error "No more than half of scif ports can be reserved !!" | |
76 | #endif | |
77 | #if (SCIF_PORT_BASE + SCIF_PORT_COUNT) > (2 << 16) | |
78 | #error "Scif ports cannot exceed 16 bit !!" | |
79 | #endif | |
80 | ||
81 | #include <linux/bitops.h> | |
82 | #include <linux/spinlock_types.h> | |
83 | static spinlock_t port_lock = __SPIN_LOCK_UNLOCKED(port_lock); | |
84 | ||
85 | /* | |
86 | * Data structures | |
87 | * init_array Flag for initialize (mark as init_code?) | |
88 | * port_bits 1 bit representing each possible port. | |
89 | * first_free Index into port_bits for free area | |
90 | * port_lock Lock for exclusive access | |
91 | * port_rsvd Total of successful "get/resv" calls. | |
92 | * port_free Total of successful "free" calls. | |
93 | * port_err Total of unsuccessfull calls. | |
94 | */ | |
95 | ||
96 | #define BITS_PR_PORT (8 * sizeof(uint64_t)) | |
97 | #define PORTS_ARRAY_SIZE ((SCIF_PORT_COUNT + (BITS_PR_PORT - 1)) / BITS_PR_PORT) | |
98 | ||
99 | ||
100 | static int init_array = 1; | |
101 | static uint16_t first_free; | |
102 | static uint64_t port_bits[PORTS_ARRAY_SIZE]; | |
103 | static uint64_t port_rsvd; | |
104 | static uint64_t port_free; | |
105 | static uint64_t port_err; | |
106 | ||
107 | ||
108 | /* | |
109 | * Bitfield handlers. | |
110 | * | |
111 | * Need 3 bit-fiddlers to operate on individual bits within | |
112 | * one 64 bit word in memory (always passing a pointer). | |
113 | * Individual bits are enumerated from 1, allowing for use | |
114 | * of value 0 to indicate an error condition. | |
115 | * | |
116 | * 1) __scif_ffsclr() returns index of first set bit in the | |
117 | * 64 bit word and clears it. A return value 0 means there | |
118 | * were no set bits in the word. | |
119 | * | |
120 | * 2) __scif_clrbit() clears a specified bit in the 64 bit word | |
121 | * The bit index is returned if bit was previously set and a | |
122 | * value 0 is returned if it was previously clear. | |
123 | * | |
124 | * 3) __scif_setbit() sets a specified bit in the 64 bit word. | |
125 | * | |
126 | * Two versions, one should work for you. | |
127 | */ | |
128 | ||
129 | #if 1 && (defined(__GNUC__) || defined(ICC)) | |
130 | /* | |
131 | * Use GNU style inline assembly for bit operations. | |
132 | * | |
133 | * Gcc complains about uninitialized use of variables | |
134 | * big_bit in ffsclr and avl in clrbit. Generated code | |
135 | * is correct, just haven't figured out the correct | |
136 | * contraints yet. | |
137 | * | |
138 | * gcc -O2: | |
139 | * __scif_ffsclr: 40 bytes | |
140 | * __scif_clrbit: 34 bytes | |
141 | * __scif_setbit: 17 bytes | |
142 | */ | |
143 | ||
144 | static int | |
145 | __scif_ffsclr(uint64_t *word) | |
146 | { | |
147 | uint64_t big_bit = 0; | |
148 | uint64_t field = *word; | |
149 | ||
150 | asm volatile ( | |
151 | "bsfq %1,%0\n\t" | |
152 | "jnz 1f\n\t" | |
153 | "movq $-1,%0\n" | |
154 | "jmp 2f\n\t" | |
155 | "1:\n\t" | |
156 | "btrq %2,%1\n\t" | |
157 | "2:" | |
158 | : "=r" (big_bit), "=r" (field) | |
159 | : "0" (big_bit), "1" (field) | |
160 | ); | |
161 | ||
162 | if (big_bit == -1) | |
163 | return 0; | |
164 | ||
165 | *word = field; | |
166 | return big_bit + 1; | |
167 | } | |
168 | ||
169 | static int | |
170 | __scif_clrbit(uint64_t *word, uint16_t bit) | |
171 | { | |
172 | uint64_t field = *word; | |
173 | uint64_t big_bit = bit; | |
174 | int avl = 0; | |
175 | ||
176 | big_bit--; | |
177 | asm volatile ( | |
178 | "xorl %2,%2\n\t" | |
179 | "btrq %3,%1\n\t" | |
180 | "rcll $1,%2\n\t" | |
181 | : "=Ir" (big_bit), "=r" (field), "=r" (avl) | |
182 | : "0" (big_bit), "1" (field), "2" (avl) | |
183 | ); | |
184 | ||
185 | *word = field; | |
186 | return avl ? bit : 0; | |
187 | } | |
188 | ||
189 | static void | |
190 | __scif_setbit(uint64_t *word, uint16_t bit) | |
191 | { | |
192 | uint64_t field = *word; | |
193 | uint64_t big_bit = bit; | |
194 | ||
195 | big_bit--; | |
196 | asm volatile ( | |
197 | "btsq %2,%1" | |
198 | : "=r" (field) | |
199 | : "0" (field), "Jr" (big_bit) | |
200 | ); | |
201 | ||
202 | *word = field; | |
203 | } | |
204 | #else | |
205 | /* | |
206 | * C inliners for bit operations. | |
207 | * | |
208 | * gcc -O2: | |
209 | * __scif_ffsclr: 50 bytes | |
210 | * __scif_clrbit: 45 bytes | |
211 | * __scif_setbit: 18 bytes | |
212 | * | |
213 | * WARNING: | |
214 | * 1) ffsll() may be glibc specific | |
215 | * 2) kernel ffs() use cmovz instruction that may not | |
216 | * work in uOS kernel (see arch/x86/include/asm/bitops.h) | |
217 | * | |
218 | */ | |
219 | ||
220 | ||
221 | static int | |
222 | __scif_ffsclr(uint64_t *word) | |
223 | { | |
224 | int bit; | |
225 | /* | |
226 | * ffsll() Find 1st bit in 64 bit word | |
227 | */ | |
228 | ||
229 | bit = ffsll(*word); | |
230 | if (bit) | |
231 | *word &= ~(1LL << (bit - 1)); | |
232 | ||
233 | return bit; | |
234 | } | |
235 | ||
236 | static int | |
237 | __scif_clrbit(uint64_t *word, uint16_t bit) | |
238 | { | |
239 | uint64_t msk = (1LL << (bit - 1)); | |
240 | ||
241 | if (*word & msk) { | |
242 | *word &= ~msk; | |
243 | return bit; | |
244 | } | |
245 | return 0; | |
246 | } | |
247 | ||
248 | static void | |
249 | __scif_setbit(uint64_t *word, uint16_t bit) | |
250 | { | |
251 | *word |= (1LL << (bit - 1)); | |
252 | } | |
253 | #endif | |
254 | ||
255 | ||
256 | static void | |
257 | init_scif_array(void) | |
258 | { | |
259 | spin_lock(&port_lock); | |
260 | if (init_array) { | |
261 | int i; | |
262 | for (i = 0; i < PORTS_ARRAY_SIZE; i++) | |
263 | port_bits[i] = ~0; | |
264 | first_free = SCIF_PORT_RSVD / BITS_PR_PORT; | |
265 | if (!SCIF_PORT_BASE) | |
266 | port_bits[0] ^= 1; | |
267 | port_rsvd = 0; | |
268 | port_free = 0; | |
269 | port_err = 0; | |
270 | init_array = 0; | |
271 | } | |
272 | spin_unlock(&port_lock); | |
273 | pr_debug("SCIF port array init:\n" | |
274 | " %d ports available starting at %d, %d reserved\n" | |
275 | " Array consists of %ld %ld-bit wide integers\n", | |
276 | SCIF_PORT_BASE ? SCIF_PORT_COUNT : SCIF_PORT_COUNT - 1, | |
277 | SCIF_PORT_BASE ? SCIF_PORT_BASE : 1, SCIF_PORT_RSVD, | |
278 | PORTS_ARRAY_SIZE, BITS_PR_PORT); | |
279 | } | |
280 | ||
281 | ||
282 | /* | |
283 | * Reserve a specified port for SCIF | |
284 | * TBD: doxyfy this header | |
285 | */ | |
286 | uint16_t | |
287 | rsrv_scif_port(uint16_t port) | |
288 | { | |
289 | uint16_t port_ix; | |
290 | ||
291 | if (!port) { | |
292 | pr_debug("rsrv_scif_port: invalid port %d\n", port); | |
293 | port_err++; | |
294 | return 0; | |
295 | } | |
296 | ||
297 | if (init_array) | |
298 | init_scif_array(); | |
299 | ||
300 | port -= SCIF_PORT_BASE; | |
301 | port_ix = port / BITS_PR_PORT; | |
302 | ||
303 | spin_lock(&port_lock); | |
304 | port = __scif_clrbit(port_bits + port_ix, 1 + (port % BITS_PR_PORT)); | |
305 | if (port) { | |
306 | port = port - 1 + BITS_PR_PORT * port_ix + SCIF_PORT_BASE; | |
307 | port_rsvd++; | |
308 | } else { | |
309 | port_err++; | |
310 | } | |
311 | spin_unlock(&port_lock); | |
312 | ||
313 | return port; | |
314 | } | |
315 | ||
316 | ||
317 | /* | |
318 | * Get and reserve any port # for SCIF | |
319 | * TBD: doxyfy this header | |
320 | */ | |
321 | uint16_t | |
322 | get_scif_port(void) | |
323 | { | |
324 | uint16_t port; | |
325 | ||
326 | if (init_array) | |
327 | init_scif_array(); | |
328 | ||
329 | spin_lock(&port_lock); | |
330 | if (first_free >= PORTS_ARRAY_SIZE) { /* Pool is empty */ | |
331 | port = 0; | |
332 | port_err++; | |
333 | goto out; | |
334 | } | |
335 | port = __scif_ffsclr(port_bits + first_free); | |
336 | if (port) { | |
337 | port = port - 1 + BITS_PR_PORT * first_free + SCIF_PORT_BASE; | |
338 | while ((first_free < PORTS_ARRAY_SIZE) && !port_bits[first_free]) | |
339 | first_free++; | |
340 | port_rsvd++; | |
341 | } else | |
342 | port_err++; | |
343 | out: | |
344 | spin_unlock(&port_lock); | |
345 | return port; | |
346 | } | |
347 | ||
348 | ||
349 | /* | |
350 | * Release a reserved port # for SCIF | |
351 | * For now, just ignore release on unreserved port | |
352 | * TBD: doxyfy this header | |
353 | */ | |
354 | ||
355 | void | |
356 | put_scif_port(uint16_t port) | |
357 | { | |
358 | uint16_t port_ix; | |
359 | ||
360 | if (!port) { | |
361 | pr_debug("put_scif_port: invalid port %d\n", port); | |
362 | port_err++; | |
363 | return; | |
364 | } | |
365 | ||
366 | port -= SCIF_PORT_BASE; | |
367 | port_ix = port / BITS_PR_PORT; | |
368 | ||
369 | spin_lock(&port_lock); | |
370 | __scif_setbit(port_bits + port_ix, 1 + (port % BITS_PR_PORT)); | |
371 | if (port >= SCIF_PORT_RSVD && port_ix < first_free) | |
372 | first_free = port_ix; | |
373 | port_free++; | |
374 | spin_unlock(&port_lock); | |
375 | } | |
376 |