| @@ -1292,9 +1292,44 @@ int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res) · 1291 unmodified lines |
| 1292 | return 0; | 1292 | return 0; |
| 1293 | } | 1293 | } |
| 1294 | | 1294 | |
| | 1295 | /* |
| | 1296 | * Create @group's rtpoll worker after psi_trigger_create() reported the need |
| | 1297 | * for one. kthread creation depends on the whole fork path and we don't want |
| | 1298 | * all of that nested inside cgroup_mutex, so the caller must drop it and any |
| | 1299 | * other lock that forks can wait behind. If two callers race, the loser stops |
| | 1300 | * its never-woken kthread. |
| | 1301 | */ |
| | 1302 | int psi_trigger_create_rtpoll_worker(struct psi_group *group) |
| | 1303 | { |
| | 1304 | struct task_struct *task; |
| | 1305 | |
| | 1306 | task = kthread_create(psi_rtpoll_worker, group, "psimon"); |
| | 1307 | if (IS_ERR(task)) |
| | 1308 | return PTR_ERR(task); |
| | 1309 | |
| | 1310 | scoped_guard(mutex, &group->rtpoll_trigger_lock) { |
| | 1311 | if (!rcu_access_pointer(group->rtpoll_task)) { |
| | 1312 | atomic_set(&group->rtpoll_wakeup, 0); |
| | 1313 | wake_up_process(task); |
| | 1314 | rcu_assign_pointer(group->rtpoll_task, task); |
| | 1315 | |
| | 1316 | /* |
| | 1317 | * Poll once to catch up on scheduling attempts dropped |
| | 1318 | * while there was no rtpoll worker. |
| | 1319 | */ |
| | 1320 | psi_schedule_rtpoll_work(group, 1, true); |
| | 1321 | return 0; |
| | 1322 | } |
| | 1323 | } |
| | 1324 | |
| | 1325 | kthread_stop(task); |
| | 1326 | return 0; |
| | 1327 | } |
| | 1328 | |
| 1295 | struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, | 1329 | struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, |
| 1296 | enum psi_res res, struct file *file, | 1330 | enum psi_res res, struct file *file, |
| 1297 | struct kernfs_open_file *of) | 1331 | struct kernfs_open_file *of, |
| | 1332 | bool *need_rtpoll_worker) |
| 1298 | { | 1333 | { |
| 1299 | struct psi_trigger *t; | 1334 | struct psi_trigger *t; |
| 1300 | enum psi_states state; | 1335 | enum psi_states state; |
| @@ -1302,6 +1337,8 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, · 1 unmodified line |
| 1302 | bool privileged; | 1337 | bool privileged; |
| 1303 | u32 window_us; | 1338 | u32 window_us; |
| 1304 | | 1339 | |
| | 1340 | *need_rtpoll_worker = false; |
| | 1341 | |
| 1305 | if (static_branch_likely(&psi_disabled)) | 1342 | if (static_branch_likely(&psi_disabled)) |
| 1306 | return ERR_PTR(-EOPNOTSUPP); | 1343 | return ERR_PTR(-EOPNOTSUPP); |
| 1307 | | 1344 | |
| @@ -1362,26 +1399,14 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, · 54 unmodified lines |
| 1362 | if (privileged) { | 1399 | if (privileged) { |
| 1363 | mutex_lock(&group->rtpoll_trigger_lock); | 1400 | mutex_lock(&group->rtpoll_trigger_lock); |
| 1364 | | 1401 | |
| 1365 | if (!rcu_access_pointer(group->rtpoll_task)) { | | |
| 1366 | struct task_struct *task; | | |
| 1367 | | | |
| 1368 | task = kthread_create(psi_rtpoll_worker, group, "psimon"); | | |
| 1369 | if (IS_ERR(task)) { | | |
| 1370 | kfree(t); | | |
| 1371 | mutex_unlock(&group->rtpoll_trigger_lock); | | |
| 1372 | return ERR_CAST(task); | | |
| 1373 | } | | |
| 1374 | atomic_set(&group->rtpoll_wakeup, 0); | | |
| 1375 | wake_up_process(task); | | |
| 1376 | rcu_assign_pointer(group->rtpoll_task, task); | | |
| 1377 | } | | |
| 1378 | | | |
| 1379 | list_add(&t->node, &group->rtpoll_triggers); | 1402 | list_add(&t->node, &group->rtpoll_triggers); |
| 1380 | group->rtpoll_min_period = min(group->rtpoll_min_period, | 1403 | group->rtpoll_min_period = min(group->rtpoll_min_period, |
| 1381 | div_u64(t->win.size, UPDATES_PER_WINDOW)); | 1404 | div_u64(t->win.size, UPDATES_PER_WINDOW)); |
| 1382 | group->rtpoll_nr_triggers[t->state]++; | 1405 | group->rtpoll_nr_triggers[t->state]++; |
| 1383 | group->rtpoll_states |= (1 << t->state); | 1406 | group->rtpoll_states |= (1 << t->state); |
| 1384 | | 1407 | |
| | 1408 | *need_rtpoll_worker = !rcu_access_pointer(group->rtpoll_task); |
| | 1409 | |
| 1385 | mutex_unlock(&group->rtpoll_trigger_lock); | 1410 | mutex_unlock(&group->rtpoll_trigger_lock); |
| 1386 | } else { | 1411 | } else { |
| 1387 | mutex_lock(&group->avgs_lock); | 1412 | mutex_lock(&group->avgs_lock); |
| @@ -1541,6 +1566,8 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf, · 153 unmodified lines |
| 1541 | size_t buf_size; | 1566 | size_t buf_size; |
| 1542 | struct seq_file *seq; | 1567 | struct seq_file *seq; |
| 1543 | struct psi_trigger *new; | 1568 | struct psi_trigger *new; |
| | 1569 | bool need_rtpoll_worker; |
| | 1570 | int ret; |
| 1544 | | 1571 | |
| 1545 | if (static_branch_likely(&psi_disabled)) | 1572 | if (static_branch_likely(&psi_disabled)) |
| 1546 | return -EOPNOTSUPP; | 1573 | return -EOPNOTSUPP; |
| @@ -1565,12 +1592,22 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf, · 18 unmodified lines |
| 1565 | return -EBUSY; | 1592 | return -EBUSY; |
| 1566 | } | 1593 | } |
| 1567 | | 1594 | |
| 1568 | new = psi_trigger_create(&psi_system, buf, res, file, NULL); | 1595 | new = psi_trigger_create(&psi_system, buf, res, file, NULL, |
| | 1596 | &need_rtpoll_worker); |
| 1569 | if (IS_ERR(new)) { | 1597 | if (IS_ERR(new)) { |
| 1570 | mutex_unlock(&seq->lock); | 1598 | mutex_unlock(&seq->lock); |
| 1571 | return PTR_ERR(new); | 1599 | return PTR_ERR(new); |
| 1572 | } | 1600 | } |
| 1573 | | 1601 | |
| | 1602 | if (need_rtpoll_worker) { |
| | 1603 | ret = psi_trigger_create_rtpoll_worker(&psi_system); |
| | 1604 | if (ret) { |
| | 1605 | psi_trigger_destroy(new); |
| | 1606 | mutex_unlock(&seq->lock); |
| | 1607 | return ret; |
| | 1608 | } |
| | 1609 | } |
| | 1610 | |
| 1574 | smp_store_release(&seq->private, new); | 1611 | smp_store_release(&seq->private, new); |
| 1575 | mutex_unlock(&seq->lock); | 1612 | mutex_unlock(&seq->lock); |
| 1576 | | 1613 | |