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