Whamcloud - gitweb
18c4839ca9b9303caba8e443361a15509c2c2147
[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 = smp_processor_id();
682         int cpt = cptab->ctb_cpu2cpt[cpu];
683
684         if (cpt < 0) {
685                 if (!remap)
686                         return cpt;
687
688                 /* don't return negative value for safety of upper layer,
689                  * instead we shadow the unknown cpu to a valid partition ID
690                  */
691                 cpt = cpu % cptab->ctb_nparts;
692         }
693
694         return cpt;
695 }
696 EXPORT_SYMBOL(cfs_cpt_current);
697
698 int cfs_cpt_of_cpu(struct cfs_cpt_table *cptab, int cpu)
699 {
700         LASSERT(cpu >= 0 && cpu < nr_cpu_ids);
701
702         return cptab->ctb_cpu2cpt[cpu];
703 }
704 EXPORT_SYMBOL(cfs_cpt_of_cpu);
705
706 int cfs_cpt_of_node(struct cfs_cpt_table *cptab, int node)
707 {
708         if (node < 0 || node > nr_node_ids)
709                 return CFS_CPT_ANY;
710
711         return cptab->ctb_node2cpt[node];
712 }
713 EXPORT_SYMBOL(cfs_cpt_of_node);
714
715 int cfs_cpt_bind(struct cfs_cpt_table *cptab, int cpt)
716 {
717         nodemask_t *nodemask;
718         cpumask_t *cpumask;
719         int cpu;
720         int rc;
721
722         LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
723
724         if (cpt == CFS_CPT_ANY) {
725                 cpumask = cptab->ctb_cpumask;
726                 nodemask = cptab->ctb_nodemask;
727         } else {
728                 cpumask = cptab->ctb_parts[cpt].cpt_cpumask;
729                 nodemask = cptab->ctb_parts[cpt].cpt_nodemask;
730         }
731
732         if (!cpumask_intersects(cpumask, cpu_online_mask)) {
733                 CDEBUG(D_INFO,
734                        "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",
735                         cpt);
736                 return -ENODEV;
737         }
738
739         for_each_online_cpu(cpu) {
740                 if (cpumask_test_cpu(cpu, cpumask))
741                         continue;
742
743                 rc = set_cpus_allowed_ptr(current, cpumask);
744                 set_mems_allowed(*nodemask);
745                 if (!rc)
746                         schedule(); /* switch to allowed CPU */
747
748                 return rc;
749         }
750
751         /* don't need to set affinity because all online CPUs are covered */
752         return 0;
753 }
754 EXPORT_SYMBOL(cfs_cpt_bind);
755
756 /**
757  * Choose max to \a number CPUs from \a node and set them in \a cpt.
758  * We always prefer to choose CPU in the same core/socket.
759  */
760 static int cfs_cpt_choose_ncpus(struct cfs_cpt_table *cptab, int cpt,
761                                 cpumask_t *node_mask, int number)
762 {
763         cpumask_t *socket_mask = NULL;
764         cpumask_t *core_mask = NULL;
765         int rc = 0;
766         int cpu;
767         int i;
768
769         LASSERT(number > 0);
770
771         if (number >= cpumask_weight(node_mask)) {
772                 while (!cpumask_empty(node_mask)) {
773                         cpu = cpumask_first(node_mask);
774                         cpumask_clear_cpu(cpu, node_mask);
775
776                         if (!cpu_online(cpu))
777                                 continue;
778
779                         rc = cfs_cpt_set_cpu(cptab, cpt, cpu);
780                         if (!rc)
781                                 return -EINVAL;
782                 }
783                 return 0;
784         }
785
786         /* allocate scratch buffer */
787         LIBCFS_ALLOC(socket_mask, cpumask_size());
788         LIBCFS_ALLOC(core_mask, cpumask_size());
789         if (!socket_mask || !core_mask) {
790                 rc = -ENOMEM;
791                 goto out;
792         }
793
794         while (!cpumask_empty(node_mask)) {
795                 cpu = cpumask_first(node_mask);
796
797                 /* get cpumask for cores in the same socket */
798                 cpumask_and(socket_mask, topology_core_cpumask(cpu), node_mask);
799                 while (!cpumask_empty(socket_mask)) {
800                         /* get cpumask for hts in the same core */
801                         cpumask_and(core_mask, topology_sibling_cpumask(cpu),
802                                     node_mask);
803
804                         for_each_cpu(i, core_mask) {
805                                 cpumask_clear_cpu(i, socket_mask);
806                                 cpumask_clear_cpu(i, node_mask);
807
808                                 if (!cpu_online(i))
809                                         continue;
810
811                                 rc = cfs_cpt_set_cpu(cptab, cpt, i);
812                                 if (!rc) {
813                                         rc = -EINVAL;
814                                         goto out;
815                                 }
816
817                                 if (!--number)
818                                         goto out;
819                         }
820                         cpu = cpumask_first(socket_mask);
821                 }
822         }
823
824 out:
825         if (core_mask)
826                 LIBCFS_FREE(core_mask, cpumask_size());
827         if (socket_mask)
828                 LIBCFS_FREE(socket_mask, cpumask_size());
829         return rc;
830 }
831
832 #define CPT_WEIGHT_MIN 4
833
834 static int cfs_cpt_num_estimate(void)
835 {
836         int nthr = cpumask_weight(topology_sibling_cpumask(smp_processor_id()));
837         int ncpu = num_online_cpus();
838         int ncpt = 1;
839
840         if (ncpu > CPT_WEIGHT_MIN)
841                 for (ncpt = 2; ncpu > 2 * nthr * ncpt; ncpt++)
842                         ; /* nothing */
843
844 #if (BITS_PER_LONG == 32)
845         /* config many CPU partitions on 32-bit system could consume
846          * too much memory
847          */
848         ncpt = min(2, ncpt);
849 #endif
850         while (ncpu % ncpt)
851                 ncpt--; /* worst case is 1 */
852
853         return ncpt;
854 }
855
856 static struct cfs_cpt_table *cfs_cpt_table_create(int ncpt)
857 {
858         struct cfs_cpt_table *cptab = NULL;
859         cpumask_t *node_mask = NULL;
860         int cpt = 0;
861         int node;
862         int num;
863         int rem;
864         int rc = 0;
865
866         num = cfs_cpt_num_estimate();
867         if (ncpt <= 0)
868                 ncpt = num;
869
870         if (ncpt > num_online_cpus() || ncpt > 4 * num) {
871                 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",
872                       ncpt, num);
873         }
874
875         cptab = cfs_cpt_table_alloc(ncpt);
876         if (!cptab) {
877                 CERROR("Failed to allocate CPU map(%d)\n", ncpt);
878                 rc = -ENOMEM;
879                 goto failed;
880         }
881
882         LIBCFS_ALLOC(node_mask, cpumask_size());
883         if (!node_mask) {
884                 CERROR("Failed to allocate scratch cpumask\n");
885                 rc = -ENOMEM;
886                 goto failed;
887         }
888
889         num = num_online_cpus() / ncpt;
890         rem = num_online_cpus() % ncpt;
891         for_each_online_node(node) {
892                 cpumask_copy(node_mask, cpumask_of_node(node));
893
894                 while (cpt < ncpt && !cpumask_empty(node_mask)) {
895                         struct cfs_cpu_partition *part = &cptab->ctb_parts[cpt];
896                         int ncpu = cpumask_weight(part->cpt_cpumask);
897
898                         rc = cfs_cpt_choose_ncpus(cptab, cpt, node_mask,
899                                                   num - ncpu);
900                         if (rc < 0) {
901                                 rc = -EINVAL;
902                                 goto failed_mask;
903                         }
904
905                         ncpu = cpumask_weight(part->cpt_cpumask);
906                         if (ncpu == num + !!(rem > 0)) {
907                                 cpt++;
908                                 rem--;
909                         }
910                 }
911         }
912
913         LIBCFS_FREE(node_mask, cpumask_size());
914
915         return cptab;
916
917 failed_mask:
918         if (node_mask)
919                 LIBCFS_FREE(node_mask, cpumask_size());
920 failed:
921         CERROR("Failed (rc = %d) to setup CPU partition table with %d partitions, online HW NUMA nodes: %d, HW CPU cores: %d.\n",
922                rc, ncpt, num_online_nodes(), num_online_cpus());
923
924         if (cptab)
925                 cfs_cpt_table_free(cptab);
926
927         return ERR_PTR(rc);
928 }
929
930 static struct cfs_cpt_table *cfs_cpt_table_create_pattern(const char *pattern)
931 {
932         struct cfs_cpt_table *cptab;
933         char *pattern_dup;
934         char *bracket;
935         char *str;
936         int node = 0;
937         int ncpt = 0;
938         int cpt = 0;
939         int high;
940         int rc;
941         int c;
942         int i;
943
944         pattern_dup = kstrdup(pattern, GFP_KERNEL);
945         if (!pattern_dup) {
946                 CERROR("Failed to duplicate pattern '%s'\n", pattern);
947                 return ERR_PTR(-ENOMEM);
948         }
949
950         str = cfs_trimwhite(pattern_dup);
951         if (*str == 'n' || *str == 'N') {
952                 str++; /* skip 'N' char */
953                 node = 1; /* NUMA pattern */
954                 if (*str == '\0') {
955                         node = -1;
956                         for_each_online_node(i) {
957                                 if (!cpumask_empty(cpumask_of_node(i)))
958                                         ncpt++;
959                         }
960                         if (ncpt == 1) { /* single NUMA node */
961                                 kfree(pattern_dup);
962                                 return cfs_cpt_table_create(cpu_npartitions);
963                         }
964                 }
965         }
966
967         if (!ncpt) { /* scanning bracket which is mark of partition */
968                 bracket = str;
969                 while ((bracket = strchr(bracket, '['))) {
970                         bracket++;
971                         ncpt++;
972                 }
973         }
974
975         if (!ncpt ||
976             (node && ncpt > num_online_nodes()) ||
977             (!node && ncpt > num_online_cpus())) {
978                 CERROR("Invalid pattern '%s', or too many partitions %d\n",
979                        pattern_dup, ncpt);
980                 rc = -EINVAL;
981                 goto err_free_str;
982         }
983
984         cptab = cfs_cpt_table_alloc(ncpt);
985         if (!cptab) {
986                 CERROR("Failed to allocate CPU partition table\n");
987                 rc = -ENOMEM;
988                 goto err_free_str;
989         }
990
991         if (node < 0) { /* shortcut to create CPT from NUMA & CPU topology */
992                 for_each_online_node(i) {
993                         if (cpumask_empty(cpumask_of_node(i)))
994                                 continue;
995
996                         rc = cfs_cpt_set_node(cptab, cpt++, i);
997                         if (!rc) {
998                                 rc = -EINVAL;
999                                 goto err_free_table;
1000                         }
1001                 }
1002                 kfree(pattern_dup);
1003                 return cptab;
1004         }
1005
1006         high = node ? nr_node_ids - 1 : nr_cpu_ids - 1;
1007
1008         for (str = cfs_trimwhite(str), c = 0; /* until break */; c++) {
1009                 struct cfs_range_expr *range;
1010                 struct cfs_expr_list *el;
1011                 int n;
1012
1013                 bracket = strchr(str, '[');
1014                 if (!bracket) {
1015                         if (*str) {
1016                                 CERROR("Invalid pattern '%s'\n", str);
1017                                 rc = -EINVAL;
1018                                 goto err_free_table;
1019                         } else if (c != ncpt) {
1020                                 CERROR("Expect %d partitions but found %d\n",
1021                                        ncpt, c);
1022                                 rc = -EINVAL;
1023                                 goto err_free_table;
1024                         }
1025                         break;
1026                 }
1027
1028                 if (sscanf(str, "%d%n", &cpt, &n) < 1) {
1029                         CERROR("Invalid CPU pattern '%s'\n", str);
1030                         rc = -EINVAL;
1031                         goto err_free_table;
1032                 }
1033
1034                 if (cpt < 0 || cpt >= ncpt) {
1035                         CERROR("Invalid partition id %d, total partitions %d\n",
1036                                cpt, ncpt);
1037                         rc = -EINVAL;
1038                         goto err_free_table;
1039                 }
1040
1041                 if (cfs_cpt_weight(cptab, cpt)) {
1042                         CERROR("Partition %d has already been set.\n", cpt);
1043                         rc = -EPERM;
1044                         goto err_free_table;
1045                 }
1046
1047                 str = cfs_trimwhite(str + n);
1048                 if (str != bracket) {
1049                         CERROR("Invalid pattern '%s'\n", str);
1050                         rc = -EINVAL;
1051                         goto err_free_table;
1052                 }
1053
1054                 bracket = strchr(str, ']');
1055                 if (!bracket) {
1056                         CERROR("Missing right bracket for partition %d in '%s'\n",
1057                                cpt, str);
1058                         rc = -EINVAL;
1059                         goto err_free_table;
1060                 }
1061
1062                 rc = cfs_expr_list_parse(str, (bracket - str) + 1, 0, high,
1063                                          &el);
1064                 if (rc) {
1065                         CERROR("Can't parse number range in '%s'\n", str);
1066                         rc = -ERANGE;
1067                         goto err_free_table;
1068                 }
1069
1070                 list_for_each_entry(range, &el->el_exprs, re_link) {
1071                         for (i = range->re_lo; i <= range->re_hi; i++) {
1072                                 if ((i - range->re_lo) % range->re_stride)
1073                                         continue;
1074
1075                                 rc = node ? cfs_cpt_set_node(cptab, cpt, i)
1076                                           : cfs_cpt_set_cpu(cptab, cpt, i);
1077                                 if (!rc) {
1078                                         cfs_expr_list_free(el);
1079                                         rc = -EINVAL;
1080                                         goto err_free_table;
1081                                 }
1082                         }
1083                 }
1084
1085                 cfs_expr_list_free(el);
1086
1087                 if (!cfs_cpt_online(cptab, cpt)) {
1088                         CERROR("No online CPU is found on partition %d\n", cpt);
1089                         rc = -ENODEV;
1090                         goto err_free_table;
1091                 }
1092
1093                 str = cfs_trimwhite(bracket + 1);
1094         }
1095
1096         kfree(pattern_dup);
1097         return cptab;
1098
1099 err_free_table:
1100         cfs_cpt_table_free(cptab);
1101 err_free_str:
1102         kfree(pattern_dup);
1103         return ERR_PTR(rc);
1104 }
1105
1106 #ifdef CONFIG_HOTPLUG_CPU
1107 #ifdef HAVE_HOTPLUG_STATE_MACHINE
1108 static enum cpuhp_state lustre_cpu_online;
1109
1110 static int cfs_cpu_online(unsigned int cpu)
1111 {
1112         return 0;
1113 }
1114 #endif
1115
1116 static int cfs_cpu_dead(unsigned int cpu)
1117 {
1118         bool warn;
1119
1120         /* if all HTs in a core are offline, it may break affinity */
1121         warn = cpumask_any_and(topology_sibling_cpumask(cpu),
1122                                cpu_online_mask) >= nr_cpu_ids;
1123         CDEBUG(warn ? D_WARNING : D_INFO,
1124                "Lustre: can't support CPU plug-out well now, performance and stability could be impacted [CPU %u]\n",
1125                cpu);
1126         return 0;
1127 }
1128
1129 #ifndef HAVE_HOTPLUG_STATE_MACHINE
1130 static int cfs_cpu_notify(struct notifier_block *self, unsigned long action,
1131                           void *hcpu)
1132 {
1133         int cpu = (unsigned long)hcpu;
1134
1135         switch (action) {
1136         case CPU_DEAD:
1137         case CPU_DEAD_FROZEN:
1138         case CPU_ONLINE:
1139         case CPU_ONLINE_FROZEN:
1140         default:
1141                 if (action != CPU_DEAD && action != CPU_DEAD_FROZEN) {
1142                         CDEBUG(D_INFO, "CPU changed [cpu %u action %lx]\n",
1143                                cpu, action);
1144                         break;
1145                 }
1146
1147                 cfs_cpu_dead(cpu);
1148         }
1149
1150         return NOTIFY_OK;
1151 }
1152
1153 static struct notifier_block cfs_cpu_notifier = {
1154         .notifier_call  = cfs_cpu_notify,
1155         .priority       = 0
1156 };
1157 #endif /* !HAVE_HOTPLUG_STATE_MACHINE */
1158 #endif /* CONFIG_HOTPLUG_CPU */
1159
1160 void cfs_cpu_fini(void)
1161 {
1162         if (!IS_ERR_OR_NULL(cfs_cpt_table))
1163                 cfs_cpt_table_free(cfs_cpt_table);
1164
1165 #ifdef CONFIG_HOTPLUG_CPU
1166 #ifdef HAVE_HOTPLUG_STATE_MACHINE
1167         if (lustre_cpu_online > 0)
1168                 cpuhp_remove_state_nocalls(lustre_cpu_online);
1169         cpuhp_remove_state_nocalls(CPUHP_LUSTRE_CFS_DEAD);
1170 #else
1171         unregister_hotcpu_notifier(&cfs_cpu_notifier);
1172 #endif /* !HAVE_HOTPLUG_STATE_MACHINE */
1173 #endif /* CONFIG_HOTPLUG_CPU */
1174 }
1175
1176 int cfs_cpu_init(void)
1177 {
1178         int ret;
1179
1180         LASSERT(!cfs_cpt_table);
1181
1182 #ifdef CONFIG_HOTPLUG_CPU
1183 #ifdef HAVE_HOTPLUG_STATE_MACHINE
1184         ret = cpuhp_setup_state_nocalls(CPUHP_LUSTRE_CFS_DEAD,
1185                                         "fs/lustre/cfe:dead", NULL,
1186                                         cfs_cpu_dead);
1187         if (ret < 0)
1188                 goto failed_cpu_dead;
1189
1190         ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
1191                                         "fs/lustre/cfe:online",
1192                                         cfs_cpu_online, NULL);
1193         if (ret < 0)
1194                 goto failed_cpu_online;
1195
1196         lustre_cpu_online = ret;
1197 #else
1198         register_hotcpu_notifier(&cfs_cpu_notifier);
1199 #endif /* !HAVE_HOTPLUG_STATE_MACHINE */
1200 #endif /* CONFIG_HOTPLUG_CPU */
1201
1202         get_online_cpus();
1203         if (*cpu_pattern) {
1204                 cfs_cpt_table = cfs_cpt_table_create_pattern(cpu_pattern);
1205                 if (IS_ERR(cfs_cpt_table)) {
1206                         CERROR("Failed to create cptab from pattern '%s'\n",
1207                                cpu_pattern);
1208                         ret = PTR_ERR(cfs_cpt_table);
1209                         goto failed_alloc_table;
1210                 }
1211
1212         } else {
1213                 cfs_cpt_table = cfs_cpt_table_create(cpu_npartitions);
1214                 if (IS_ERR(cfs_cpt_table)) {
1215                         CERROR("Failed to create cptab with npartitions %d\n",
1216                                cpu_npartitions);
1217                         ret = PTR_ERR(cfs_cpt_table);
1218                         goto failed_alloc_table;
1219                 }
1220         }
1221
1222         put_online_cpus();
1223
1224         LCONSOLE(0, "HW NUMA nodes: %d, HW CPU cores: %d, npartitions: %d\n",
1225                  num_online_nodes(), num_online_cpus(),
1226                  cfs_cpt_number(cfs_cpt_table));
1227         return 0;
1228
1229 failed_alloc_table:
1230         put_online_cpus();
1231
1232         if (cfs_cpt_table)
1233                 cfs_cpt_table_free(cfs_cpt_table);
1234
1235 #ifdef CONFIG_HOTPLUG_CPU
1236 #ifdef HAVE_HOTPLUG_STATE_MACHINE
1237         if (lustre_cpu_online > 0)
1238                 cpuhp_remove_state_nocalls(lustre_cpu_online);
1239 failed_cpu_online:
1240         cpuhp_remove_state_nocalls(CPUHP_LUSTRE_CFS_DEAD);
1241 failed_cpu_dead:
1242 #else
1243         unregister_hotcpu_notifier(&cfs_cpu_notifier);
1244 #endif /* !HAVE_HOTPLUG_STATE_MACHINE */
1245 #endif /* CONFIG_HOTPLUG_CPU */
1246         return ret;
1247 }
1248
1249 #else /* ! CONFIG_SMP */
1250
1251 struct cfs_cpt_table *cfs_cpt_table_alloc(int ncpt)
1252 {
1253         struct cfs_cpt_table *cptab;
1254
1255         if (ncpt != 1) {
1256                 CERROR("Can't support cpu partition number %d\n", ncpt);
1257                 return NULL;
1258         }
1259
1260         LIBCFS_ALLOC(cptab, sizeof(*cptab));
1261         if (!cptab)
1262                 return NULL;
1263
1264         cpumask_set_cpu(0, cptab->ctb_cpumask);
1265         node_set(0, cptab->ctb_nodemask);
1266
1267         return cptab;
1268 }
1269 EXPORT_SYMBOL(cfs_cpt_table_alloc);
1270
1271 int cfs_cpt_table_print(struct cfs_cpt_table *cptab, char *buf, int len)
1272 {
1273         int rc;
1274
1275         rc = snprintf(buf, len, "0\t: 0\n");
1276         len -= rc;
1277         if (len <= 0)
1278                 return -EFBIG;
1279
1280         return rc;
1281 }
1282 EXPORT_SYMBOL(cfs_cpt_table_print);
1283
1284 int cfs_cpt_distance_print(struct cfs_cpt_table *cptab, char *buf, int len)
1285 {
1286         int rc;
1287
1288         rc = snprintf(buf, len, "0\t: 0:1\n");
1289         len -= rc;
1290         if (len <= 0)
1291                 return -EFBIG;
1292
1293         return rc;
1294 }
1295 EXPORT_SYMBOL(cfs_cpt_distance_print);
1296
1297 void cfs_cpu_fini(void)
1298 {
1299         if (cfs_cpt_table) {
1300                 cfs_cpt_table_free(cfs_cpt_table);
1301                 cfs_cpt_table = NULL;
1302         }
1303 }
1304
1305 int cfs_cpu_init(void)
1306 {
1307         cfs_cpt_table = cfs_cpt_table_alloc(1);
1308
1309         return cfs_cpt_table ? 0 : -1;
1310 }
1311
1312 #endif /* !CONFIG_SMP */