Added a basic README to the project.
[xeon-phi-kernel-module] / micscif / micscif_ports.c
CommitLineData
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>
83static 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
100static int init_array = 1;
101static uint16_t first_free;
102static uint64_t port_bits[PORTS_ARRAY_SIZE];
103static uint64_t port_rsvd;
104static uint64_t port_free;
105static 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
144static 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
169static 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
189static 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
221static 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
236static 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
248static void
249__scif_setbit(uint64_t *word, uint16_t bit)
250{
251 *word |= (1LL << (bit - 1));
252}
253#endif
254
255
256static void
257init_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 */
286uint16_t
287rsrv_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 */
321uint16_t
322get_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++;
343out:
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
355void
356put_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