@@ -86,6 +86,30 @@ struct fmeter {
86
86
spinlock_t lock ; /* guards read or write of above */
87
87
};
88
88
89
+ /*
90
+ * Invalid partition error code
91
+ */
92
+ enum prs_errcode {
93
+ PERR_NONE = 0 ,
94
+ PERR_INVCPUS ,
95
+ PERR_INVPARENT ,
96
+ PERR_NOTPART ,
97
+ PERR_NOTEXCL ,
98
+ PERR_NOCPUS ,
99
+ PERR_HOTPLUG ,
100
+ PERR_CPUSEMPTY ,
101
+ };
102
+
103
+ static const char * const perr_strings [] = {
104
+ [PERR_INVCPUS ] = "Invalid cpu list in cpuset.cpus" ,
105
+ [PERR_INVPARENT ] = "Parent is an invalid partition root" ,
106
+ [PERR_NOTPART ] = "Parent is not a partition root" ,
107
+ [PERR_NOTEXCL ] = "Cpu list in cpuset.cpus not exclusive" ,
108
+ [PERR_NOCPUS ] = "Parent unable to distribute cpu downstream" ,
109
+ [PERR_HOTPLUG ] = "No cpu available due to hotplug" ,
110
+ [PERR_CPUSEMPTY ] = "cpuset.cpus is empty" ,
111
+ };
112
+
89
113
struct cpuset {
90
114
struct cgroup_subsys_state css ;
91
115
@@ -169,6 +193,9 @@ struct cpuset {
169
193
int use_parent_ecpus ;
170
194
int child_ecpus_count ;
171
195
196
+ /* Invalid partition error code, not lock protected */
197
+ enum prs_errcode prs_err ;
198
+
172
199
/* Handle for cpuset.cpus.partition */
173
200
struct cgroup_file partition_file ;
174
201
};
@@ -298,6 +325,10 @@ static inline void notify_partition_change(struct cpuset *cs, int old_prs)
298
325
if (old_prs == cs -> partition_root_state )
299
326
return ;
300
327
cgroup_file_notify (& cs -> partition_file );
328
+
329
+ /* Reset prs_err if not invalid */
330
+ if (is_partition_valid (cs ))
331
+ WRITE_ONCE (cs -> prs_err , PERR_NONE );
301
332
}
302
333
303
334
static struct cpuset top_cpuset = {
@@ -1235,7 +1266,7 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
1235
1266
* @cmd: Partition root state change command
1236
1267
* @newmask: Optional new cpumask for partcmd_update
1237
1268
* @tmp: Temporary addmask and delmask
1238
- * Return: 0 or -1 ( error)
1269
+ * Return: 0 or a partition root state error code
1239
1270
*
1240
1271
* For partcmd_enable, the cpuset is being transformed from a non-partition
1241
1272
* root to a partition root. The cpus_allowed mask of the given cpuset will
@@ -1261,7 +1292,7 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
1261
1292
*
1262
1293
* The partcmd_update command is used by update_cpumasks_hier() with newmask
1263
1294
* NULL and update_cpumask() with newmask set. The callers won't check for
1264
- * error and so partition_root_state will be updated directly.
1295
+ * error and so partition_root_state and prs_error will be updated directly.
1265
1296
*/
1266
1297
static int update_parent_subparts_cpumask (struct cpuset * cs , int cmd ,
1267
1298
struct cpumask * newmask ,
@@ -1271,7 +1302,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
1271
1302
int adding ; /* Moving cpus from effective_cpus to subparts_cpus */
1272
1303
int deleting ; /* Moving cpus from subparts_cpus to effective_cpus */
1273
1304
int old_prs , new_prs ;
1274
- bool part_error = false ; /* Partition error? */
1305
+ int part_error = PERR_NONE ; /* Partition error? */
1275
1306
1276
1307
percpu_rwsem_assert_held (& cpuset_rwsem );
1277
1308
@@ -1280,10 +1311,13 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
1280
1311
* The new cpumask, if present, or the current cpus_allowed must
1281
1312
* not be empty.
1282
1313
*/
1283
- if (!is_partition_valid (parent ) ||
1284
- (newmask && cpumask_empty (newmask )) ||
1314
+ if (!is_partition_valid (parent )) {
1315
+ return is_partition_invalid (parent )
1316
+ ? PERR_INVPARENT : PERR_NOTPART ;
1317
+ }
1318
+ if ((newmask && cpumask_empty (newmask )) ||
1285
1319
(!newmask && cpumask_empty (cs -> cpus_allowed )))
1286
- return -1 ;
1320
+ return PERR_CPUSEMPTY ;
1287
1321
1288
1322
/*
1289
1323
* new_prs will only be changed for the partcmd_update command.
@@ -1296,15 +1330,15 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
1296
1330
* doesn't overlap parent's cpus_allowed.
1297
1331
*/
1298
1332
if (!cpumask_intersects (cs -> cpus_allowed , parent -> cpus_allowed ))
1299
- return -1 ;
1333
+ return PERR_INVCPUS ;
1300
1334
1301
1335
/*
1302
1336
* A parent can be left with no CPU as long as there is no
1303
1337
* task directly associated with the parent partition.
1304
1338
*/
1305
1339
if (!cpumask_intersects (cs -> cpus_allowed , parent -> effective_cpus ) &&
1306
1340
partition_is_populated (parent , cs ))
1307
- return -1 ;
1341
+ return PERR_NOCPUS ;
1308
1342
1309
1343
cpumask_copy (tmp -> addmask , cs -> cpus_allowed );
1310
1344
adding = true;
@@ -1341,7 +1375,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
1341
1375
cpumask_subset (parent -> effective_cpus , tmp -> addmask ) &&
1342
1376
!cpumask_intersects (tmp -> delmask , cpu_active_mask ) &&
1343
1377
partition_is_populated (parent , cs )) {
1344
- part_error = true ;
1378
+ part_error = PERR_NOCPUS ;
1345
1379
adding = false;
1346
1380
deleting = cpumask_and (tmp -> delmask , cs -> cpus_allowed ,
1347
1381
parent -> subparts_cpus );
@@ -1373,7 +1407,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
1373
1407
(adding &&
1374
1408
cpumask_subset (parent -> effective_cpus , tmp -> addmask ) &&
1375
1409
partition_is_populated (parent , cs ))) {
1376
- part_error = true ;
1410
+ part_error = PERR_NOCPUS ;
1377
1411
adding = false;
1378
1412
}
1379
1413
@@ -1382,6 +1416,8 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
1382
1416
deleting = cpumask_and (tmp -> delmask , cs -> cpus_allowed ,
1383
1417
parent -> subparts_cpus );
1384
1418
}
1419
+ if (part_error )
1420
+ WRITE_ONCE (cs -> prs_err , part_error );
1385
1421
1386
1422
if (cmd == partcmd_update ) {
1387
1423
/*
@@ -1412,7 +1448,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
1412
1448
if (old_prs != new_prs ) {
1413
1449
if (is_prs_invalid (old_prs ) && !is_cpu_exclusive (cs ) &&
1414
1450
(update_flag (CS_CPU_EXCLUSIVE , cs , 1 ) < 0 ))
1415
- return -1 ;
1451
+ return PERR_NOTEXCL ;
1416
1452
if (is_prs_invalid (new_prs ) && is_cpu_exclusive (cs ))
1417
1453
update_flag (CS_CPU_EXCLUSIVE , cs , 0 );
1418
1454
}
@@ -1547,6 +1583,9 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,
1547
1583
*/
1548
1584
if (is_partition_valid (cp ))
1549
1585
new_prs = - cp -> partition_root_state ;
1586
+ WRITE_ONCE (cp -> prs_err ,
1587
+ is_partition_invalid (parent )
1588
+ ? PERR_INVPARENT : PERR_NOTPART );
1550
1589
break ;
1551
1590
}
1552
1591
}
@@ -2121,13 +2160,13 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
2121
2160
* update_prstate - update partition_root_state
2122
2161
* @cs: the cpuset to update
2123
2162
* @new_prs: new partition root state
2124
- * Return: 0 if successful, < 0 if error
2163
+ * Return: 0 if successful, != 0 if error
2125
2164
*
2126
2165
* Call with cpuset_rwsem held.
2127
2166
*/
2128
2167
static int update_prstate (struct cpuset * cs , int new_prs )
2129
2168
{
2130
- int err = 0 , old_prs = cs -> partition_root_state ;
2169
+ int err = PERR_NONE , old_prs = cs -> partition_root_state ;
2131
2170
bool sched_domain_rebuilt = false;
2132
2171
struct cpuset * parent = parent_cs (cs );
2133
2172
struct tmpmasks tmpmask ;
@@ -2154,13 +2193,15 @@ static int update_prstate(struct cpuset *cs, int new_prs)
2154
2193
* cannot be empty.
2155
2194
*/
2156
2195
if (cpumask_empty (cs -> cpus_allowed )) {
2157
- err = 1 ;
2196
+ err = PERR_CPUSEMPTY ;
2158
2197
goto out ;
2159
2198
}
2160
2199
2161
2200
err = update_flag (CS_CPU_EXCLUSIVE , cs , 1 );
2162
- if (err )
2201
+ if (err ) {
2202
+ err = PERR_NOTEXCL ;
2163
2203
goto out ;
2204
+ }
2164
2205
2165
2206
err = update_parent_subparts_cpumask (cs , partcmd_enable ,
2166
2207
NULL , & tmpmask );
@@ -2730,6 +2771,7 @@ static s64 cpuset_read_s64(struct cgroup_subsys_state *css, struct cftype *cft)
2730
2771
static int sched_partition_show (struct seq_file * seq , void * v )
2731
2772
{
2732
2773
struct cpuset * cs = css_cs (seq_css (seq ));
2774
+ const char * err , * type = NULL ;
2733
2775
2734
2776
switch (cs -> partition_root_state ) {
2735
2777
case PRS_ROOT :
@@ -2742,9 +2784,17 @@ static int sched_partition_show(struct seq_file *seq, void *v)
2742
2784
seq_puts (seq , "member\n" );
2743
2785
break ;
2744
2786
case PRS_INVALID_ROOT :
2745
- seq_puts ( seq , "root invalid\n" ) ;
2746
- break ;
2787
+ type = "root" ;
2788
+ fallthrough ;
2747
2789
case PRS_INVALID_ISOLATED :
2790
+ if (!type )
2791
+ type = "isolated" ;
2792
+ err = perr_strings [READ_ONCE (cs -> prs_err )];
2793
+ if (err )
2794
+ seq_printf (seq , "%s invalid (%s)\n" , type , err );
2795
+ else
2796
+ seq_printf (seq , "%s invalid\n" , type );
2797
+ break ;
2748
2798
seq_puts (seq , "isolated invalid\n" );
2749
2799
break ;
2750
2800
}
@@ -3335,7 +3385,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
3335
3385
*/
3336
3386
if (is_partition_valid (cs ) && (!parent -> nr_subparts_cpus ||
3337
3387
(cpumask_empty (& new_cpus ) && partition_is_populated (cs , NULL )))) {
3338
- int old_prs ;
3388
+ int old_prs , parent_prs ;
3339
3389
3340
3390
update_parent_subparts_cpumask (cs , partcmd_disable , NULL , tmp );
3341
3391
if (cs -> nr_subparts_cpus ) {
@@ -3347,10 +3397,17 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
3347
3397
}
3348
3398
3349
3399
old_prs = cs -> partition_root_state ;
3400
+ parent_prs = parent -> partition_root_state ;
3350
3401
if (is_partition_valid (cs )) {
3351
3402
spin_lock_irq (& callback_lock );
3352
3403
make_partition_invalid (cs );
3353
3404
spin_unlock_irq (& callback_lock );
3405
+ if (is_prs_invalid (parent_prs ))
3406
+ WRITE_ONCE (cs -> prs_err , PERR_INVPARENT );
3407
+ else if (!parent_prs )
3408
+ WRITE_ONCE (cs -> prs_err , PERR_NOTPART );
3409
+ else
3410
+ WRITE_ONCE (cs -> prs_err , PERR_HOTPLUG );
3354
3411
notify_partition_change (cs , old_prs );
3355
3412
}
3356
3413
cpuset_force_rebuild ();
0 commit comments