Whamcloud - gitweb
c80f9d42552bf5b43b0e7146b4122b268c5cd2f2
[fs/lustre-release.git] / libcfs / libcfs / libcfs_cpu.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * GPL HEADER END
17  */
18 /*
19  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
20  * Copyright (c) 2012, 2017, Intel Corporation.
21  */
22 /*
23  * This file is part of Lustre, http://www.lustre.org/
24  * Lustre is a trademark of Sun Microsystems, Inc.
25  *
26  * Please see comments in libcfs/include/libcfs/libcfs_cpu.h for introduction
27  *
28  * Author: liang@whamcloud.com
29  */
30
31 #define DEBUG_SUBSYSTEM S_LNET
32
33 #include <linux/cpu.h>
34 #include <linux/sched.h>
35 #include <libcfs/libcfs_cpu.h>
36 #include <libcfs/libcfs.h>
37
38 /** Global CPU partition table */
39 struct cfs_cpt_table *cfs_cpt_table __read_mostly;
40 EXPORT_SYMBOL(cfs_cpt_table);
41
42 /**
43  * modparam for setting number of partitions
44  *
45  *  0 : estimate best value based on cores or NUMA nodes
46  *  1 : disable multiple partitions
47  * >1 : specify number of partitions
48  */
49 static int cpu_npartitions;
50 module_param(cpu_npartitions, int, 0444);
51 MODULE_PARM_DESC(cpu_npartitions, "# of CPU partitions");
52
53 /**
54  * modparam for setting CPU partitions patterns:
55  *
56  * i.e: "0[0,1,2,3] 1[4,5,6,7]", number before bracket is CPU partition ID,
57  *      number in bracket is processor ID (core or HT)
58  *
59  * i.e: "N 0[0,1] 1[2,3]" the first character 'N' means numbers in bracket
60  *       are NUMA node ID, number before bracket is CPU partition ID.
61  *
62  * i.e: "N", shortcut expression to create CPT from NUMA & CPU topology
63  *
64  * NB: If user specified cpu_pattern, cpu_npartitions will be ignored
65  */
66 static char *cpu_pattern = "N";
67 module_param(cpu_pattern, charp, 0444);
68 MODULE_PARM_DESC(cpu_pattern, "CPU partitions pattern");
69
70 #ifdef CONFIG_SMP
71 struct cfs_cpt_table *cfs_cpt_table_alloc(int ncpt)
72 {
73         struct cfs_cpt_table *cptab;
74         int i;
75
76         LIBCFS_ALLOC(cptab, sizeof(*cptab));
77         if (!cptab)
78                 return NULL;
79
80         cptab->ctb_nparts = ncpt;
81
82         LIBCFS_ALLOC(cptab->ctb_cpumask, cpumask_size());
83         if (!cptab->ctb_cpumask)
84                 goto failed_alloc_cpumask;
85
86         LIBCFS_ALLOC(cptab->ctb_nodemask, sizeof(*cptab->ctb_nodemask));
87         if (!cptab->ctb_nodemask)
88                 goto failed_alloc_nodemask;
89
90         LIBCFS_ALLOC(cptab->ctb_cpu2cpt,
91                      nr_cpu_ids * sizeof(cptab->ctb_cpu2cpt[0]));
92         if (!cptab->ctb_cpu2cpt)
93                 goto failed_alloc_cpu2cpt;
94
95         memset(cptab->ctb_cpu2cpt, -1,
96                nr_cpu_ids * sizeof(cptab->ctb_cpu2cpt[0]));
97
98         LIBCFS_ALLOC(cptab->ctb_node2cpt,
99                      nr_node_ids * sizeof(cptab->ctb_node2cpt[0]));
100         if (!cptab->ctb_node2cpt)
101                 goto failed_alloc_node2cpt;
102
103         memset(cptab->ctb_node2cpt, -1,
104                nr_node_ids * sizeof(cptab->ctb_node2cpt[0]));
105
106         LIBCFS_ALLOC(cptab->ctb_parts, ncpt * sizeof(cptab->ctb_parts[0]));
107         if (!cptab->ctb_parts)
108                 goto failed_alloc_ctb_parts;
109
110         memset(cptab->ctb_parts, -1, ncpt * sizeof(cptab->ctb_parts[0]));
111
112         for (i = 0; i < ncpt; i++) {
113                 struct cfs_cpu_partition *part = &cptab->ctb_parts[i];
114
115                 LIBCFS_ALLOC(part->cpt_cpumask, cpumask_size());
116                 if (!part->cpt_cpumask)
117                         goto failed_setting_ctb_parts;
118
119                 LIBCFS_ALLOC(part->cpt_nodemask, sizeof(*part->cpt_nodemask));
120                 if (!part->cpt_nodemask)
121                         goto failed_setting_ctb_parts;
122
123                 LIBCFS_ALLOC(part->cpt_distance,
124                              cptab->ctb_nparts * sizeof(part->cpt_distance[0]));
125                 if (!part->cpt_distance)
126                         goto failed_setting_ctb_parts;
127
128                 memset(part->cpt_distance, -1,
129                        cptab->ctb_nparts * sizeof(part->cpt_distance[0]));
130         }
131
132         return cptab;
133
134 failed_setting_ctb_parts:
135         while (i-- >= 0) {
136                 struct cfs_cpu_partition *part = &cptab->ctb_parts[i];
137
138                 if (part->cpt_nodemask) {
139                         LIBCFS_FREE(part->cpt_nodemask,
140                                     sizeof(*part->cpt_nodemask));
141                 }
142
143                 if (part->cpt_cpumask)
144                         LIBCFS_FREE(part->cpt_cpumask, cpumask_size());
145
146                 if (part->cpt_distance) {
147                         LIBCFS_FREE(part->cpt_distance,
148                                 cptab->ctb_nparts *
149                                         sizeof(part->cpt_distance[0]));
150                 }
151         }
152
153         if (cptab->ctb_parts) {
154                 LIBCFS_FREE(cptab->ctb_parts,
155                             cptab->ctb_nparts * sizeof(cptab->ctb_parts[0]));
156         }
157 failed_alloc_ctb_parts:
158         if (cptab->ctb_node2cpt) {
159                 LIBCFS_FREE(cptab->ctb_node2cpt,
160                             nr_node_ids * sizeof(cptab->ctb_node2cpt[0]));
161         }
162 failed_alloc_node2cpt:
163         if (cptab->ctb_cpu2cpt) {
164                 LIBCFS_FREE(cptab->ctb_cpu2cpt,
165                             nr_cpu_ids * sizeof(cptab->ctb_cpu2cpt[0]));
166         }
167 failed_alloc_cpu2cpt:
168         if (cptab->ctb_nodemask)
169                 LIBCFS_FREE(cptab->ctb_nodemask, sizeof(*cptab->ctb_nodemask));
170 failed_alloc_nodemask:
171         if (cptab->ctb_cpumask)
172                 LIBCFS_FREE(cptab->ctb_cpumask, cpumask_size());
173 failed_alloc_cpumask:
174         LIBCFS_FREE(cptab, sizeof(*cptab));
175         return NULL;
176 }
177 EXPORT_SYMBOL(cfs_cpt_table_alloc);
178
179 void cfs_cpt_table_free(struct cfs_cpt_table *cptab)
180 {
181         int i;
182
183         if (cptab->ctb_cpu2cpt) {
184                 LIBCFS_FREE(cptab->ctb_cpu2cpt,
185                             nr_cpu_ids * sizeof(cptab->ctb_cpu2cpt[0]));
186         }
187
188         if (cptab->ctb_node2cpt) {
189                 LIBCFS_FREE(cptab->ctb_node2cpt,
190                             nr_node_ids * sizeof(cptab->ctb_node2cpt[0]));
191         }
192
193         for (i = 0; cptab->ctb_parts && i < cptab->ctb_nparts; i++) {
194                 struct cfs_cpu_partition *part = &cptab->ctb_parts[i];
195
196                 if (part->cpt_nodemask) {
197                         LIBCFS_FREE(part->cpt_nodemask,
198                                     sizeof(*part->cpt_nodemask));
199                 }
200
201                 if (part->cpt_cpumask)
202                         LIBCFS_FREE(part->cpt_cpumask, cpumask_size());
203
204                 if (part->cpt_distance) {
205                         LIBCFS_FREE(part->cpt_distance,
206                                 cptab->ctb_nparts *
207                                         sizeof(part->cpt_distance[0]));
208                 }
209         }
210
211         if (cptab->ctb_parts) {
212                 LIBCFS_FREE(cptab->ctb_parts,
213                             cptab->ctb_nparts * sizeof(cptab->ctb_parts[0]));
214         }
215
216         if (cptab->ctb_nodemask)
217                 LIBCFS_FREE(cptab->ctb_nodemask, sizeof(*cptab->ctb_nodemask));
218         if (cptab->ctb_cpumask)
219                 LIBCFS_FREE(cptab->ctb_cpumask, cpumask_size());
220
221         LIBCFS_FREE(cptab, sizeof(*cptab));
222 }
223 EXPORT_SYMBOL(cfs_cpt_table_free);
224
225 int cfs_cpt_table_print(struct cfs_cpt_table *cptab, char *buf, int len)
226 {
227         char *tmp = buf;
228         int rc;
229         int i;
230         int j;
231
232         for (i = 0; i < cptab->ctb_nparts; i++) {
233                 if (len <= 0)
234                         goto err;
235
236                 rc = snprintf(tmp, len, "%d\t:", i);
237                 len -= rc;
238
239                 if (len <= 0)
240                         goto err;
241
242                 tmp += rc;
243                 for_each_cpu(j, cptab->ctb_parts[i].cpt_cpumask) {
244                         rc = snprintf(tmp, len, " %d", j);
245                         len -= rc;
246                         if (len <= 0)
247                                 goto err;
248                         tmp += rc;
249                 }
250
251                 *tmp = '\n';
252                 tmp++;
253                 len--;
254         }
255
256         return tmp - buf;
257 err:
258         return -E2BIG;
259 }
260 EXPORT_SYMBOL(cfs_cpt_table_print);
261
262 int cfs_cpt_distance_print(struct cfs_cpt_table *cptab, char *buf, int len)
263 {
264         char *tmp = buf;
265         int rc;
266         int i;
267         int j;
268
269         for (i = 0; i < cptab->ctb_nparts; i++) {
270                 if (len <= 0)
271                         goto err;
272
273                 rc = snprintf(tmp, len, "%d\t:", i);
274                 len -= rc;
275
276                 if (len <= 0)
277                         goto err;
278
279                 tmp += rc;
280                 for (j = 0; j < cptab->ctb_nparts; j++) {
281                         rc = snprintf(tmp, len, " %d:%d", j,
282                                       cptab->ctb_parts[i].cpt_distance[j]);
283                         len -= rc;
284                         if (len <= 0)
285                                 goto err;
286                         tmp += rc;
287                 }
288
289                 *tmp = '\n';
290                 tmp++;
291                 len--;
292         }
293
294         return tmp - buf;
295 err:
296         return -E2BIG;
297 }
298 EXPORT_SYMBOL(cfs_cpt_distance_print);
299
300 int cfs_cpt_number(struct cfs_cpt_table *cptab)
301 {
302         return cptab->ctb_nparts;
303 }
304 EXPORT_SYMBOL(cfs_cpt_number);
305
306 int cfs_cpt_weight(struct cfs_cpt_table *cptab, int cpt)
307 {
308         LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
309
310         return cpt == CFS_CPT_ANY ?
311                cpumask_weight(cptab->ctb_cpumask) :
312                cpumask_weight(cptab->ctb_parts[cpt].cpt_cpumask);
313 }
314 EXPORT_SYMBOL(cfs_cpt_weight);
315
316 int cfs_cpt_online(struct cfs_cpt_table *cptab, int cpt)
317 {
318         LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
319
320         return cpt == CFS_CPT_ANY ?
321                cpumask_any_and(cptab->ctb_cpumask,
322                                cpu_online_mask) < nr_cpu_ids :
323                cpumask_any_and(cptab->ctb_parts[cpt].cpt_cpumask,
324                                cpu_online_mask) < nr_cpu_ids;
325 }
326 EXPORT_SYMBOL(cfs_cpt_online);
327
328 cpumask_t *cfs_cpt_cpumask(struct cfs_cpt_table *cptab, int cpt)
329 {
330         LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
331
332         return cpt == CFS_CPT_ANY ?
333                cptab->ctb_cpumask : cptab->ctb_parts[cpt].cpt_cpumask;
334 }
335 EXPORT_SYMBOL(cfs_cpt_cpumask);
336
337 nodemask_t *cfs_cpt_nodemask(struct cfs_cpt_table *cptab, int cpt)
338 {
339         LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
340
341         return cpt == CFS_CPT_ANY ?
342                cptab->ctb_nodemask : cptab->ctb_parts[cpt].cpt_nodemask;
343 }
344 EXPORT_SYMBOL(cfs_cpt_nodemask);
345
346 unsigned int cfs_cpt_distance(struct cfs_cpt_table *cptab, int cpt1, int cpt2)
347 {
348         LASSERT(cpt1 == CFS_CPT_ANY || (cpt1 >= 0 && cpt1 < cptab->ctb_nparts));
349         LASSERT(cpt2 == CFS_CPT_ANY || (cpt2 >= 0 && cpt2 < cptab->ctb_nparts));
350
351         if (cpt1 == CFS_CPT_ANY || cpt2 == CFS_CPT_ANY)
352                 return cptab->ctb_distance;
353
354         return cptab->ctb_parts[cpt1].cpt_distance[cpt2];
355 }
356 EXPORT_SYMBOL(cfs_cpt_distance);
357
358 /*
359  * Calculate the maximum NUMA distance between all nodes in the
360  * from_mask and all nodes in the to_mask.
361  */
362 static unsigned int cfs_cpt_distance_calculate(nodemask_t *from_mask,
363                                                nodemask_t *to_mask)
364 {
365         unsigned int maximum;
366         unsigned int distance;
367         int from;
368         int to;
369
370         maximum = 0;
371         for_each_node_mask(from, *from_mask) {
372                 for_each_node_mask(to, *to_mask) {
373                         distance = node_distance(from, to);
374                         if (maximum < distance)
375                                 maximum = distance;
376                 }
377         }
378         return maximum;
379 }
380
381 static void cfs_cpt_add_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu)
382 {
383         cptab->ctb_cpu2cpt[cpu] = cpt;
384
385         cpumask_set_cpu(cpu, cptab->ctb_cpumask);
386         cpumask_set_cpu(cpu, cptab->ctb_parts[cpt].cpt_cpumask);
387 }
388
389 static void cfs_cpt_del_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu)
390 {
391         cpumask_clear_cpu(cpu, cptab->ctb_parts[cpt].cpt_cpumask);
392         cpumask_clear_cpu(cpu, cptab->ctb_cpumask);
393
394         cptab->ctb_cpu2cpt[cpu] = -1;
395 }
396
397 static void cfs_cpt_add_node(struct cfs_cpt_table *cptab, int cpt, int node)
398 {
399         struct cfs_cpu_partition *part;
400
401         if (!node_isset(node, *cptab->ctb_nodemask)) {
402                 unsigned int dist;
403
404                 /* first time node is added to the CPT table */
405                 node_set(node, *cptab->ctb_nodemask);
406                 cptab->ctb_node2cpt[node] = cpt;
407
408                 dist = cfs_cpt_distance_calculate(cptab->ctb_nodemask,
409                                                   cptab->ctb_nodemask);
410                 cptab->ctb_distance = dist;
411         }
412
413         part = &cptab->ctb_parts[cpt];
414         if (!node_isset(node, *part->cpt_nodemask)) {
415                 int cpt2;
416
417                 /* first time node is added to this CPT */
418                 node_set(node, *part->cpt_nodemask);
419                 for (cpt2 = 0; cpt2 < cptab->ctb_nparts; cpt2++) {
420                         struct cfs_cpu_partition *part2;
421                         unsigned int dist;
422
423                         part2 = &cptab->ctb_parts[cpt2];
424                         dist = cfs_cpt_distance_calculate(part->cpt_nodemask,
425                                                           part2->cpt_nodemask);
426                         part->cpt_distance[cpt2] = dist;
427                         dist = cfs_cpt_distance_calculate(part2->cpt_nodemask,
428                                                           part->cpt_nodemask);
429                         part2->cpt_distance[cpt] = dist;
430                 }
431         }
432 }
433
434 static void cfs_cpt_del_node(struct cfs_cpt_table *cptab, int cpt, int node)
435 {
436         struct cfs_cpu_partition *part = &cptab->ctb_parts[cpt];
437         int cpu;
438
439         for_each_cpu(cpu, part->cpt_cpumask) {
440                 /* this CPT has other CPU belonging to this node? */
441                 if (cpu_to_node(cpu) == node)
442                         break;
443         }
444
445         if (cpu >= nr_cpu_ids && node_isset(node,  *part->cpt_nodemask)) {
446                 int cpt2;
447
448                 /* No more CPUs in the node for this CPT. */
449                 node_clear(node, *part->cpt_nodemask);
450                 for (cpt2 = 0; cpt2 < cptab->ctb_nparts; cpt2++) {
451                         struct cfs_cpu_partition *part2;
452                         unsigned int dist;
453
454                         part2 = &cptab->ctb_parts[cpt2];
455                         if (node_isset(node, *part2->cpt_nodemask))
456                                 cptab->ctb_node2cpt[node] = cpt2;
457
458                         dist = cfs_cpt_distance_calculate(part->cpt_nodemask,
459                                                           part2->cpt_nodemask);
460                         part->cpt_distance[cpt2] = dist;
461                         dist = cfs_cpt_distance_calculate(part2->cpt_nodemask,
462                                                           part->cpt_nodemask);
463                         part2->cpt_distance[cpt] = dist;
464                 }
465         }
466
467         for_each_cpu(cpu, cptab->ctb_cpumask) {
468                 /* this CPT-table has other CPUs belonging to this node? */
469                 if (cpu_to_node(cpu) == node)
470                         break;
471         }
472
473         if (cpu >= nr_cpu_ids && node_isset(node, *cptab->ctb_nodemask)) {
474                 /* No more CPUs in the table for this node. */
475                 node_clear(node, *cptab->ctb_nodemask);
476                 cptab->ctb_node2cpt[node] = -1;
477                 cptab->ctb_distance =
478                         cfs_cpt_distance_calculate(cptab->ctb_nodemask,
479                                                    cptab->ctb_nodemask);
480         }
481 }
482
483 int cfs_cpt_set_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu)
484 {
485         LASSERT(cpt >= 0 && cpt < cptab->ctb_nparts);
486
487         if (cpu < 0 || cpu >= nr_cpu_ids || !cpu_online(cpu)) {
488                 CDEBUG(D_INFO, "CPU %d is invalid or it's offline\n", cpu);
489                 return 0;
490         }
491
492         if (cptab->ctb_cpu2cpt[cpu] != -1) {
493                 CDEBUG(D_INFO, "CPU %d is already in partition %d\n",
494                        cpu, cptab->ctb_cpu2cpt[cpu]);
495                 return 0;
496         }
497
498         if (cpumask_test_cpu(cpu, cptab->ctb_cpumask)) {
499                 CDEBUG(D_INFO, "CPU %d is already in cpumask\n", cpu);
500                 return 0;
501         }
502
503         if (cpumask_test_cpu(cpu, cptab->ctb_parts[cpt].cpt_cpumask)) {
504                 CDEBUG(D_INFO, "CPU %d is already in partition %d cpumask\n",
505                        cpu, cptab->ctb_cpu2cpt[cpu]);
506                 return 0;
507         }
508
509         cfs_cpt_add_cpu(cptab, cpt, cpu);
510         cfs_cpt_add_node(cptab, cpt, cpu_to_node(cpu));
511
512         return 1;
513 }
514 EXPORT_SYMBOL(cfs_cpt_set_cpu);
515
516 void cfs_cpt_unset_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu)
517 {
518         LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
519
520         if (cpu < 0 || cpu >= nr_cpu_ids) {
521                 CDEBUG(D_INFO, "Invalid CPU id %d\n", cpu);
522                 return;
523         }
524
525         if (cpt == CFS_CPT_ANY) {
526                 /* caller doesn't know the partition ID */
527                 cpt = cptab->ctb_cpu2cpt[cpu];
528                 if (cpt < 0) { /* not set in this CPT-table */
529                         CDEBUG(D_INFO,
530                                "Try to unset cpu %d which is not in CPT-table %p\n",
531                                cpt, cptab);
532                         return;
533                 }
534
535         } else if (cpt != cptab->ctb_cpu2cpt[cpu]) {
536                 CDEBUG(D_INFO,
537                        "CPU %d is not in CPU partition %d\n", cpu, cpt);
538                 return;
539         }
540
541         LASSERT(cpumask_test_cpu(cpu, cptab->ctb_parts[cpt].cpt_cpumask));
542         LASSERT(cpumask_test_cpu(cpu, cptab->ctb_cpumask));
543
544         cfs_cpt_del_cpu(cptab, cpt, cpu);
545         cfs_cpt_del_node(cptab, cpt, cpu_to_node(cpu));
546 }
547 EXPORT_SYMBOL(cfs_cpt_unset_cpu);
548
549 int cfs_cpt_set_cpumask(struct cfs_cpt_table *cptab, int cpt,
550                         const cpumask_t *mask)
551 {
552         int cpu;
553
554         if (!cpumask_weight(mask) ||
555             cpumask_any_and(mask, cpu_online_mask) >= nr_cpu_ids) {
556                 CDEBUG(D_INFO,
557                        "No online CPU is found in the CPU mask for CPU partition %d\n",
558                        cpt);
559                 return 0;
560         }
561
562         for_each_cpu(cpu, mask) {
563                 cfs_cpt_add_cpu(cptab, cpt, cpu);
564                 cfs_cpt_add_node(cptab, cpt, cpu_to_node(cpu));
565         }
566
567         return 1;
568 }
569 EXPORT_SYMBOL(cfs_cpt_set_cpumask);
570
571 void cfs_cpt_unset_cpumask(struct cfs_cpt_table *cptab, int cpt,
572                            const cpumask_t *mask)
573 {
574         int cpu;
575
576         for_each_cpu(cpu, mask) {
577                 cfs_cpt_del_cpu(cptab, cpt, cpu);
578                 cfs_cpt_del_node(cptab, cpt, cpu_to_node(cpu));
579         }
580 }
581 EXPORT_SYMBOL(cfs_cpt_unset_cpumask);
582
583 int cfs_cpt_set_node(struct cfs_cpt_table *cptab, int cpt, int node)
584 {
585         const cpumask_t *mask;
586         int cpu;
587
588         if (node < 0 || node >= nr_node_ids) {
589                 CDEBUG(D_INFO,
590                        "Invalid NUMA id %d for CPU partition %d\n", node, cpt);
591                 return 0;
592         }
593
594         mask = cpumask_of_node(node);
595
596         for_each_cpu(cpu, mask)
597                 cfs_cpt_add_cpu(cptab, cpt, cpu);
598
599         cfs_cpt_add_node(cptab, cpt, node);
600
601         return 1;
602 }
603 EXPORT_SYMBOL(cfs_cpt_set_node);
604
605 void cfs_cpt_unset_node(struct cfs_cpt_table *cptab, int cpt, int node)
606 {
607         const cpumask_t *mask;
608         int cpu;
609
610         if (node < 0 || node >= nr_node_ids) {
611                 CDEBUG(D_INFO,
612                        "Invalid NUMA id %d for CPU partition %d\n", node, cpt);
613                 return;
614         }
615
616         mask = cpumask_of_node(node);
617
618         for_each_cpu(cpu, mask)
619                 cfs_cpt_del_cpu(cptab, cpt, cpu);
620
621         cfs_cpt_del_node(cptab, cpt, node);
622 }
623 EXPORT_SYMBOL(cfs_cpt_unset_node);
624
625 int cfs_cpt_set_nodemask(struct cfs_cpt_table *cptab, int cpt,
626                          const nodemask_t *mask)
627 {
628         int node;
629
630         for_each_node_mask(node, *mask)
631                 cfs_cpt_set_node(cptab, cpt, node);
632
633         return 1;
634 }
635 EXPORT_SYMBOL(cfs_cpt_set_nodemask);
636
637 void cfs_cpt_unset_nodemask(struct cfs_cpt_table *cptab, int cpt,
638                             const nodemask_t *mask)
639 {
640         int node;
641
642         for_each_node_mask(node, *mask)
643                 cfs_cpt_unset_node(cptab, cpt, node);
644 }
645 EXPORT_SYMBOL(cfs_cpt_unset_nodemask);
646
647 int cfs_cpt_spread_node(struct cfs_cpt_table *cptab, int cpt)
648 {
649         nodemask_t *mask;
650         int weight;
651         int rotor;
652         int node = 0;
653
654         /* convert CPU partition ID to HW node id */
655
656         if (cpt < 0 || cpt >= cptab->ctb_nparts) {
657                 mask = cptab->ctb_nodemask;
658                 rotor = cptab->ctb_spread_rotor++;
659         } else {
660                 mask = cptab->ctb_parts[cpt].cpt_nodemask;
661                 rotor = cptab->ctb_parts[cpt].cpt_spread_rotor++;
662                 node  = cptab->ctb_parts[cpt].cpt_node;
663         }
664
665         weight = nodes_weight(*mask);
666         if (weight > 0) {
667                 rotor %= weight;
668
669                 for_each_node_mask(node, *mask) {
670                         if (!rotor--)
671                                 return node;
672                 }
673         }
674
675         return node;
676 }
677 EXPORT_SYMBOL(cfs_cpt_spread_node);
678
679 int cfs_cpt_current(struct cfs_cpt_table *cptab, int remap)
680 {
681         int cpu;
682         int cpt;
683
684         preempt_disable();
685         cpu = smp_processor_id();
686         cpt = cptab->ctb_cpu2cpt[cpu];
687
688         if (cpt < 0 && remap) {
689                 /* don't return negative value for safety of upper layer,
690                  * instead we shadow the unknown cpu to a valid partition ID
691                  */
692                 cpt = cpu % cptab->ctb_nparts;
693         }
694         preempt_enable();
695         return cpt;
696 }
697 EXPORT_SYMBOL(cfs_cpt_current);
698
699 int cfs_cpt_of_cpu(struct cfs_cpt_table *cptab, int cpu)
700 {
701         LASSERT(cpu >= 0 && cpu < nr_cpu_ids);
702
703         return cptab->ctb_cpu2cpt[cpu];
704 }
705 EXPORT_SYMBOL(cfs_cpt_of_cpu);
706
707 int cfs_cpt_of_node(struct cfs_cpt_table *cptab, int node)
708 {
709         if (node < 0 || node > nr_node_ids)
710                 return CFS_CPT_ANY;
711
712         return cptab->ctb_node2cpt[node];
713 }
714 EXPORT_SYMBOL(cfs_cpt_of_node);
715
716 int cfs_cpt_bind(struct cfs_cpt_table *cptab, int cpt)
717 {
718         nodemask_t *nodemask;
719         cpumask_t *cpumask;
720         int cpu;
721         int rc;
722
723         LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
724
725         if (cpt == CFS_CPT_ANY) {
726                 cpumask = cptab->ctb_cpumask;
727                 nodemask = cptab->ctb_nodemask;
728         } else {
729                 cpumask = cptab->ctb_parts[cpt].cpt_cpumask;
730                 nodemask = cptab->ctb_parts[cpt].cpt_nodemask;
731         }
732
733         if (!cpumask_intersects(cpumask, cpu_online_mask)) {
734                 CDEBUG(D_INFO,
735                        "No online CPU found in CPU partition %d, did someone do CPU hotplug on system? You might need to reload Lustre modules to keep system working well.\n",
736                         cpt);
737                 return -ENODEV;
738         }
739
740         for_each_online_cpu(cpu) {
741                 if (cpumask_test_cpu(cpu, cpumask))
742                         continue;
743
744                 rc = set_cpus_allowed_ptr(current, cpumask);
745                 set_mems_allowed(*nodemask);
746                 if (!rc)
747                         schedule(); /* switch to allowed CPU */
748
749                 return rc;
750         }
751
752         /* don't need to set affinity because all online CPUs are covered */
753         return 0;
754 }
755 EXPORT_SYMBOL(cfs_cpt_bind);
756
757 /**
758  * Choose max to \a number CPUs from \a node and set them in \a cpt.
759  * We always prefer to choose CPU in the same core/socket.
760  */
761 static int cfs_cpt_choose_ncpus(struct cfs_cpt_table *cptab, int cpt,
762                                 cpumask_t *node_mask, int number)
763 {
764         cpumask_t *socket_mask = NULL;
765         cpumask_t *core_mask = NULL;
766         int rc = 0;
767         int cpu;
768         int i;
769
770         LASSERT(number > 0);
771
772         if (number >= cpumask_weight(node_mask)) {
773                 while (!cpumask_empty(node_mask)) {
774                         cpu = cpumask_first(node_mask);
775                         cpumask_clear_cpu(cpu, node_mask);
776
777                         if (!cpu_online(cpu))
778                                 continue;
779
780                         rc = cfs_cpt_set_cpu(cptab, cpt, cpu);
781                         if (!rc)
782                                 return -EINVAL;
783                 }
784                 return 0;
785         }
786
787         /* allocate scratch buffer */
788         LIBCFS_ALLOC(socket_mask, cpumask_size());
789         LIBCFS_ALLOC(core_mask, cpumask_size());
790         if (!socket_mask || !core_mask) {
791                 rc = -ENOMEM;
792                 goto out;
793         }
794
795         while (!cpumask_empty(node_mask)) {
796                 cpu = cpumask_first(node_mask);
797
798                 /* get cpumask for cores in the same socket */
799                 cpumask_and(socket_mask, topology_core_cpumask(cpu), node_mask);
800                 while (!cpumask_empty(socket_mask)) {
801                         /* get cpumask for hts in the same core */
802                         cpumask_and(core_mask, topology_sibling_cpumask(cpu),
803                                     node_mask);
804
805                         for_each_cpu(i, core_mask) {
806                                 cpumask_clear_cpu(i, socket_mask);
807                                 cpumask_clear_cpu(i, node_mask);
808
809                                 if (!cpu_online(i))
810                                         continue;
811
812                                 rc = cfs_cpt_set_cpu(cptab, cpt, i);
813                                 if (!rc) {
814                                         rc = -EINVAL;
815                                         goto out;
816                                 }
817
818                                 if (!--number)
819                                         goto out;
820                         }
821                         cpu = cpumask_first(socket_mask);
822                 }
823         }
824
825 out:
826         if (core_mask)
827                 LIBCFS_FREE(core_mask, cpumask_size());
828         if (socket_mask)
829                 LIBCFS_FREE(socket_mask, cpumask_size());
830         return rc;
831 }
832
833 #define CPT_WEIGHT_MIN 4
834
835 static int cfs_cpt_num_estimate(void)
836 {
837         int nthr = cpumask_weight(topology_sibling_cpumask(smp_processor_id()));
838         int ncpu = num_online_cpus();
839         int ncpt = 1;
840
841         if (ncpu > CPT_WEIGHT_MIN)
842                 for (ncpt = 2; ncpu > 2 * nthr * ncpt; ncpt++)
843                         ; /* nothing */
844
845 #if (BITS_PER_LONG == 32)
846         /* config many CPU partitions on 32-bit system could consume
847          * too much memory
848          */
849         ncpt = min(2, ncpt);
850 #endif
851         while (ncpu % ncpt)
852                 ncpt--; /* worst case is 1 */
853
854         return ncpt;
855 }
856
857 static struct cfs_cpt_table *cfs_cpt_table_create(int ncpt)
858 {
859         struct cfs_cpt_table *cptab = NULL;
860         cpumask_t *node_mask = NULL;
861         int cpt = 0;
862         int node;
863         int num;
864         int rem;
865         int rc = 0;
866
867         num = cfs_cpt_num_estimate();
868         if (ncpt <= 0)
869                 ncpt = num;
870
871         if (ncpt > num_online_cpus() || ncpt > 4 * num) {
872                 CWARN("CPU partition number %d is larger than suggested value (%d), your system may have performance issue or run out of memory while under pressure\n",
873                       ncpt, num);
874         }
875
876         cptab = cfs_cpt_table_alloc(ncpt);
877         if (!cptab) {
878                 CERROR("Failed to allocate CPU map(%d)\n", ncpt);
879                 rc = -ENOMEM;
880                 goto failed;
881         }
882
883         LIBCFS_ALLOC(node_mask, cpumask_size());
884         if (!node_mask) {
885                 CERROR("Failed to allocate scratch cpumask\n");
886                 rc = -ENOMEM;
887                 goto failed;
888         }
889
890         num = num_online_cpus() / ncpt;
891         rem = num_online_cpus() % ncpt;
892         for_each_online_node(node) {
893                 cpumask_copy(node_mask, cpumask_of_node(node));
894
895                 while (cpt < ncpt && !cpumask_empty(node_mask)) {
896                         struct cfs_cpu_partition *part = &cptab->ctb_parts[cpt];
897                         int ncpu = cpumask_weight(part->cpt_cpumask);
898
899                         rc = cfs_cpt_choose_ncpus(cptab, cpt, node_mask,
900                                                   num - ncpu);
901                         if (rc < 0) {
902                                 rc = -EINVAL;
903                                 goto failed_mask;
904                         }
905
906                         ncpu = cpumask_weight(part->cpt_cpumask);
907                         if (ncpu == num + !!(rem > 0)) {
908                                 cpt++;
909                                 rem--;
910                         }
911                 }
912         }
913
914         LIBCFS_FREE(node_mask, cpumask_size());
915
916         return cptab;
917
918 failed_mask:
919         if (node_mask)
920                 LIBCFS_FREE(node_mask, cpumask_size());
921 failed:
922         CERROR("Failed (rc = %d) to setup CPU partition table with %d partitions, online HW NUMA nodes: %d, HW CPU cores: %d.\n",
923                rc, ncpt, num_online_nodes(), num_online_cpus());
924
925         if (cptab)
926                 cfs_cpt_table_free(cptab);
927
928         return ERR_PTR(rc);
929 }
930
931 static struct cfs_cpt_table *cfs_cpt_table_create_pattern(const char *pattern)
932 {
933         struct cfs_cpt_table *cptab;
934         char *pattern_dup;
935         char *bracket;
936         char *str;
937         int node = 0;
938         int ncpt = 0;
939         int cpt = 0;
940         int high;
941         int rc;
942         int c;
943         int i;
944
945         pattern_dup = kstrdup(pattern, GFP_KERNEL);
946         if (!pattern_dup) {
947                 CERROR("Failed to duplicate pattern '%s'\n", pattern);
948                 return ERR_PTR(-ENOMEM);
949         }
950
951         str = cfs_trimwhite(pattern_dup);
952         if (*str == 'n' || *str == 'N') {
953                 str++; /* skip 'N' char */
954                 node = 1; /* NUMA pattern */
955                 if (*str == '\0') {
956                         node = -1;
957                         for_each_online_node(i) {
958                                 if (!cpumask_empty(cpumask_of_node(i)))
959                                         ncpt++;
960                         }
961                         if (ncpt == 1) { /* single NUMA node */
962                                 kfree(pattern_dup);
963                                 return cfs_cpt_table_create(cpu_npartitions);
964                         }
965                 }
966         }
967
968         if (!ncpt) { /* scanning bracket which is mark of partition */
969                 bracket = str;
970                 while ((bracket = strchr(bracket, '['))) {
971                         bracket++;
972                         ncpt++;
973                 }
974         }
975
976         if (!ncpt ||
977             (node && ncpt > num_online_nodes()) ||
978             (!node && ncpt > num_online_cpus())) {
979                 CERROR("Invalid pattern '%s', or too many partitions %d\n",
980                        pattern_dup, ncpt);
981                 rc = -EINVAL;
982                 goto err_free_str;
983         }
984
985         cptab = cfs_cpt_table_alloc(ncpt);
986         if (!cptab) {
987                 CERROR("Failed to allocate CPU partition table\n");
988                 rc = -ENOMEM;
989                 goto err_free_str;
990         }
991
992         if (node < 0) { /* shortcut to create CPT from NUMA & CPU topology */
993                 for_each_online_node(i) {
994                         if (cpumask_empty(cpumask_of_node(i)))
995                                 continue;
996
997                         rc = cfs_cpt_set_node(cptab, cpt++, i);
998                         if (!rc) {
999                                 rc = -EINVAL;
1000                                 goto err_free_table;
1001                         }
1002                 }
1003                 kfree(pattern_dup);
1004                 return cptab;
1005         }
1006
1007         high = node ? nr_node_ids - 1 : nr_cpu_ids - 1;
1008
1009         for (str = cfs_trimwhite(str), c = 0; /* until break */; c++) {
1010                 struct cfs_range_expr *range;
1011                 struct cfs_expr_list *el;
1012                 int n;
1013
1014                 bracket = strchr(str, '[');
1015                 if (!bracket) {
1016                         if (*str) {
1017                                 CERROR("Invalid pattern '%s'\n", str);
1018                                 rc = -EINVAL;
1019                                 goto err_free_table;
1020                         } else if (c != ncpt) {
1021                                 CERROR("Expect %d partitions but found %d\n",
1022                                        ncpt, c);
1023                                 rc = -EINVAL;
1024                                 goto err_free_table;
1025                         }
1026                         break;
1027                 }
1028
1029                 if (sscanf(str, "%d%n", &cpt, &n) < 1) {
1030                         CERROR("Invalid CPU pattern '%s'\n", str);
1031                         rc = -EINVAL;
1032                         goto err_free_table;
1033                 }
1034
1035                 if (cpt < 0 || cpt >= ncpt) {
1036                         CERROR("Invalid partition id %d, total partitions %d\n",
1037                                cpt, ncpt);
1038                         rc = -EINVAL;
1039                         goto err_free_table;
1040                 }
1041
1042                 if (cfs_cpt_weight(cptab, cpt)) {
1043                         CERROR("Partition %d has already been set.\n", cpt);
1044                         rc = -EPERM;
1045                         goto err_free_table;
1046                 }
1047
1048                 str = cfs_trimwhite(str + n);
1049                 if (str != bracket) {
1050                         CERROR("Invalid pattern '%s'\n", str);
1051                         rc = -EINVAL;
1052                         goto err_free_table;
1053                 }
1054
1055                 bracket = strchr(str, ']');
1056                 if (!bracket) {
1057                         CERROR("Missing right bracket for partition %d in '%s'\n",
1058                                cpt, str);
1059                         rc = -EINVAL;
1060                         goto err_free_table;
1061                 }
1062
1063                 rc = cfs_expr_list_parse(str, (bracket - str) + 1, 0, high,
1064                                          &el);
1065                 if (rc) {
1066                         CERROR("Can't parse number range in '%s'\n", str);
1067                         rc = -ERANGE;
1068                         goto err_free_table;
1069                 }
1070
1071                 list_for_each_entry(range, &el->el_exprs, re_link) {
1072                         for (i = range->re_lo; i <= range->re_hi; i++) {
1073                                 if ((i - range->re_lo) % range->re_stride)
1074                                         continue;
1075
1076                                 rc = node ? cfs_cpt_set_node(cptab, cpt, i)
1077                                           : cfs_cpt_set_cpu(cptab, cpt, i);
1078                                 if (!rc) {
1079                                         cfs_expr_list_free(el);
1080                                         rc = -EINVAL;
1081                                         goto err_free_table;
1082                                 }
1083                         }
1084                 }
1085
1086                 cfs_expr_list_free(el);
1087
1088                 if (!cfs_cpt_online(cptab, cpt)) {
1089                         CERROR("No online CPU is found on partition %d\n", cpt);
1090                         rc = -ENODEV;
1091                         goto err_free_table;
1092                 }
1093
1094                 str = cfs_trimwhite(bracket + 1);
1095         }
1096
1097         kfree(pattern_dup);
1098         return cptab;
1099
1100 err_free_table:
1101         cfs_cpt_table_free(cptab);
1102 err_free_str:
1103         kfree(pattern_dup);
1104         return ERR_PTR(rc);
1105 }
1106
1107 #ifdef CONFIG_HOTPLUG_CPU
1108 #ifdef HAVE_HOTPLUG_STATE_MACHINE
1109 static enum cpuhp_state lustre_cpu_online;
1110
1111 static int cfs_cpu_online(unsigned int cpu)
1112 {
1113         return 0;
1114 }
1115 #endif
1116
1117 static int cfs_cpu_dead(unsigned int cpu)
1118 {
1119         bool warn;
1120
1121         /* if all HTs in a core are offline, it may break affinity */
1122         warn = cpumask_any_and(topology_sibling_cpumask(cpu),
1123                                cpu_online_mask) >= nr_cpu_ids;
1124         CDEBUG(warn ? D_WARNING : D_INFO,
1125                "Lustre: can't support CPU plug-out well now, performance and stability could be impacted [CPU %u]\n",
1126                cpu);
1127         return 0;
1128 }
1129
1130 #ifndef HAVE_HOTPLUG_STATE_MACHINE
1131 static int cfs_cpu_notify(struct notifier_block *self, unsigned long action,
1132                           void *hcpu)
1133 {
1134         int cpu = (unsigned long)hcpu;
1135
1136         switch (action) {
1137         case CPU_DEAD:
1138         case CPU_DEAD_FROZEN:
1139         case CPU_ONLINE:
1140         case CPU_ONLINE_FROZEN:
1141         default:
1142                 if (action != CPU_DEAD && action != CPU_DEAD_FROZEN) {
1143                         CDEBUG(D_INFO, "CPU changed [cpu %u action %lx]\n",
1144                                cpu, action);
1145                         break;
1146                 }
1147
1148                 cfs_cpu_dead(cpu);
1149         }
1150
1151         return NOTIFY_OK;
1152 }
1153
1154 static struct notifier_block cfs_cpu_notifier = {
1155         .notifier_call  = cfs_cpu_notify,
1156         .priority       = 0
1157 };
1158 #endif /* !HAVE_HOTPLUG_STATE_MACHINE */
1159 #endif /* CONFIG_HOTPLUG_CPU */
1160
1161 void cfs_cpu_fini(void)
1162 {
1163         if (!IS_ERR_OR_NULL(cfs_cpt_table))
1164                 cfs_cpt_table_free(cfs_cpt_table);
1165
1166 #ifdef CONFIG_HOTPLUG_CPU
1167 #ifdef HAVE_HOTPLUG_STATE_MACHINE
1168         if (lustre_cpu_online > 0)
1169                 cpuhp_remove_state_nocalls(lustre_cpu_online);
1170         cpuhp_remove_state_nocalls(CPUHP_LUSTRE_CFS_DEAD);
1171 #else
1172         unregister_hotcpu_notifier(&cfs_cpu_notifier);
1173 #endif /* !HAVE_HOTPLUG_STATE_MACHINE */
1174 #endif /* CONFIG_HOTPLUG_CPU */
1175 }
1176
1177 int cfs_cpu_init(void)
1178 {
1179         int ret;
1180
1181         LASSERT(!cfs_cpt_table);
1182
1183 #ifdef CONFIG_HOTPLUG_CPU
1184 #ifdef HAVE_HOTPLUG_STATE_MACHINE
1185         ret = cpuhp_setup_state_nocalls(CPUHP_LUSTRE_CFS_DEAD,
1186                                         "fs/lustre/cfe:dead", NULL,
1187                                         cfs_cpu_dead);
1188         if (ret < 0)
1189                 goto failed_cpu_dead;
1190
1191         ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
1192                                         "fs/lustre/cfe:online",
1193                                         cfs_cpu_online, NULL);
1194         if (ret < 0)
1195                 goto failed_cpu_online;
1196
1197         lustre_cpu_online = ret;
1198 #else
1199         register_hotcpu_notifier(&cfs_cpu_notifier);
1200 #endif /* !HAVE_HOTPLUG_STATE_MACHINE */
1201 #endif /* CONFIG_HOTPLUG_CPU */
1202
1203         get_online_cpus();
1204         if (*cpu_pattern) {
1205                 cfs_cpt_table = cfs_cpt_table_create_pattern(cpu_pattern);
1206                 if (IS_ERR(cfs_cpt_table)) {
1207                         CERROR("Failed to create cptab from pattern '%s'\n",
1208                                cpu_pattern);
1209                         ret = PTR_ERR(cfs_cpt_table);
1210                         goto failed_alloc_table;
1211                 }
1212
1213         } else {
1214                 cfs_cpt_table = cfs_cpt_table_create(cpu_npartitions);
1215                 if (IS_ERR(cfs_cpt_table)) {
1216                         CERROR("Failed to create cptab with npartitions %d\n",
1217                                cpu_npartitions);
1218                         ret = PTR_ERR(cfs_cpt_table);
1219                         goto failed_alloc_table;
1220                 }
1221         }
1222
1223         put_online_cpus();
1224
1225         LCONSOLE(0, "HW NUMA nodes: %d, HW CPU cores: %d, npartitions: %d\n",
1226                  num_online_nodes(), num_online_cpus(),
1227                  cfs_cpt_number(cfs_cpt_table));
1228         return 0;
1229
1230 failed_alloc_table:
1231         put_online_cpus();
1232
1233         if (cfs_cpt_table)
1234                 cfs_cpt_table_free(cfs_cpt_table);
1235
1236 #ifdef CONFIG_HOTPLUG_CPU
1237 #ifdef HAVE_HOTPLUG_STATE_MACHINE
1238         if (lustre_cpu_online > 0)
1239                 cpuhp_remove_state_nocalls(lustre_cpu_online);
1240 failed_cpu_online:
1241         cpuhp_remove_state_nocalls(CPUHP_LUSTRE_CFS_DEAD);
1242 failed_cpu_dead:
1243 #else
1244         unregister_hotcpu_notifier(&cfs_cpu_notifier);
1245 #endif /* !HAVE_HOTPLUG_STATE_MACHINE */
1246 #endif /* CONFIG_HOTPLUG_CPU */
1247         return ret;
1248 }
1249
1250 #else /* ! CONFIG_SMP */
1251
1252 struct cfs_cpt_table *cfs_cpt_table_alloc(int ncpt)
1253 {
1254         struct cfs_cpt_table *cptab;
1255
1256         if (ncpt != 1) {
1257                 CERROR("Can't support cpu partition number %d\n", ncpt);
1258                 return NULL;
1259         }
1260
1261         LIBCFS_ALLOC(cptab, sizeof(*cptab));
1262         if (!cptab)
1263                 return NULL;
1264
1265         cpumask_set_cpu(0, cptab->ctb_cpumask);
1266         node_set(0, cptab->ctb_nodemask);
1267
1268         return cptab;
1269 }
1270 EXPORT_SYMBOL(cfs_cpt_table_alloc);
1271
1272 int cfs_cpt_table_print(struct cfs_cpt_table *cptab, char *buf, int len)
1273 {
1274         int rc;
1275
1276         rc = snprintf(buf, len, "0\t: 0\n");
1277         len -= rc;
1278         if (len <= 0)
1279                 return -EFBIG;
1280
1281         return rc;
1282 }
1283 EXPORT_SYMBOL(cfs_cpt_table_print);
1284
1285 int cfs_cpt_distance_print(struct cfs_cpt_table *cptab, char *buf, int len)
1286 {
1287         int rc;
1288
1289         rc = snprintf(buf, len, "0\t: 0:1\n");
1290         len -= rc;
1291         if (len <= 0)
1292                 return -EFBIG;
1293
1294         return rc;
1295 }
1296 EXPORT_SYMBOL(cfs_cpt_distance_print);
1297
1298 void cfs_cpu_fini(void)
1299 {
1300         if (cfs_cpt_table) {
1301                 cfs_cpt_table_free(cfs_cpt_table);
1302                 cfs_cpt_table = NULL;
1303         }
1304 }
1305
1306 int cfs_cpu_init(void)
1307 {
1308         cfs_cpt_table = cfs_cpt_table_alloc(1);
1309
1310         return cfs_cpt_table ? 0 : -1;
1311 }
1312
1313 #endif /* !CONFIG_SMP */