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