···2020#include "xfs_icache.h"2121#include "xfs_health.h"2222#include "xfs_trans.h"2323+#include "xfs_pwork.h"23242425/*2526 * Walking Inodes in the Filesystem···4645 */47464847struct xfs_iwalk_ag {4848+ /* parallel work control data; will be null if single threaded */4949+ struct xfs_pwork pwork;5050+4951 struct xfs_mount *mp;5052 struct xfs_trans *tp;5153···186182187183 trace_xfs_iwalk_ag_rec(mp, agno, irec);188184185185+ if (xfs_pwork_want_abort(&iwag->pwork))186186+ return 0;187187+189188 if (iwag->inobt_walk_fn) {190189 error = iwag->inobt_walk_fn(mp, tp, agno, irec,191190 iwag->data);···200193 continue;201194202195 for (j = 0; j < XFS_INODES_PER_CHUNK; j++) {196196+ if (xfs_pwork_want_abort(&iwag->pwork))197197+ return 0;198198+203199 /* Skip if this inode is free */204200 if (XFS_INOBT_MASK(j) & irec->ir_free)205201 continue;···397387 struct xfs_inobt_rec_incore *irec;398388399389 cond_resched();390390+ if (xfs_pwork_want_abort(&iwag->pwork))391391+ goto out;400392401393 /* Fetch the inobt record. */402394 irec = &iwag->recs[iwag->nr_recs];···532520 .sz_recs = xfs_iwalk_prefetch(inode_records),533521 .trim_start = 1,534522 .skip_empty = 1,523523+ .pwork = XFS_PWORK_SINGLE_THREADED,535524 };536525 xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino);537526 int error;···552539553540 xfs_iwalk_free(&iwag);554541 return error;542542+}543543+544544+/* Run per-thread iwalk work. */545545+static int546546+xfs_iwalk_ag_work(547547+ struct xfs_mount *mp,548548+ struct xfs_pwork *pwork)549549+{550550+ struct xfs_iwalk_ag *iwag;551551+ int error = 0;552552+553553+ iwag = container_of(pwork, struct xfs_iwalk_ag, pwork);554554+ if (xfs_pwork_want_abort(pwork))555555+ goto out;556556+557557+ error = xfs_iwalk_alloc(iwag);558558+ if (error)559559+ goto out;560560+561561+ error = xfs_iwalk_ag(iwag);562562+ xfs_iwalk_free(iwag);563563+out:564564+ kmem_free(iwag);565565+ return error;566566+}567567+568568+/*569569+ * Walk all the inodes in the filesystem using multiple threads to process each570570+ * AG.571571+ */572572+int573573+xfs_iwalk_threaded(574574+ struct xfs_mount *mp,575575+ xfs_ino_t startino,576576+ xfs_iwalk_fn iwalk_fn,577577+ unsigned int inode_records,578578+ void *data)579579+{580580+ struct xfs_pwork_ctl pctl;581581+ xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino);582582+ unsigned int nr_threads;583583+ int error;584584+585585+ ASSERT(agno < mp->m_sb.sb_agcount);586586+587587+ nr_threads = xfs_pwork_guess_datadev_parallelism(mp);588588+ error = xfs_pwork_init(mp, &pctl, xfs_iwalk_ag_work, "xfs_iwalk",589589+ nr_threads);590590+ if (error)591591+ return error;592592+593593+ for (; agno < mp->m_sb.sb_agcount; agno++) {594594+ struct xfs_iwalk_ag *iwag;595595+596596+ if (xfs_pwork_ctl_want_abort(&pctl))597597+ break;598598+599599+ iwag = kmem_zalloc(sizeof(struct xfs_iwalk_ag), KM_SLEEP);600600+ iwag->mp = mp;601601+ iwag->iwalk_fn = iwalk_fn;602602+ iwag->data = data;603603+ iwag->startino = startino;604604+ iwag->sz_recs = xfs_iwalk_prefetch(inode_records);605605+ xfs_pwork_queue(&pctl, &iwag->pwork);606606+ startino = XFS_AGINO_TO_INO(mp, agno + 1, 0);607607+ }608608+609609+ return xfs_pwork_destroy(&pctl);555610}556611557612/*···682601 .data = data,683602 .startino = startino,684603 .sz_recs = xfs_inobt_walk_prefetch(inobt_records),604604+ .pwork = XFS_PWORK_SINGLE_THREADED,685605 };686606 xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino);687607 int error;
+2
fs/xfs/xfs_iwalk.h
···15151616int xfs_iwalk(struct xfs_mount *mp, struct xfs_trans *tp, xfs_ino_t startino,1717 xfs_iwalk_fn iwalk_fn, unsigned int inode_records, void *data);1818+int xfs_iwalk_threaded(struct xfs_mount *mp, xfs_ino_t startino,1919+ xfs_iwalk_fn iwalk_fn, unsigned int inode_records, void *data);18201921/* Walk all inode btree records in the filesystem starting from @startino. */2022typedef int (*xfs_inobt_walk_fn)(struct xfs_mount *mp, struct xfs_trans *tp,
+117
fs/xfs/xfs_pwork.c
···11+// SPDX-License-Identifier: GPL-2.0-or-later22+/*33+ * Copyright (C) 2019 Oracle. All Rights Reserved.44+ * Author: Darrick J. Wong <darrick.wong@oracle.com>55+ */66+#include "xfs.h"77+#include "xfs_fs.h"88+#include "xfs_shared.h"99+#include "xfs_format.h"1010+#include "xfs_log_format.h"1111+#include "xfs_trans_resv.h"1212+#include "xfs_mount.h"1313+#include "xfs_trace.h"1414+#include "xfs_sysctl.h"1515+#include "xfs_pwork.h"1616+1717+/*1818+ * Parallel Work Queue1919+ * ===================2020+ *2121+ * Abstract away the details of running a large and "obviously" parallelizable2222+ * task across multiple CPUs. Callers initialize the pwork control object with2323+ * a desired level of parallelization and a work function. Next, they embed2424+ * struct xfs_pwork in whatever structure they use to pass work context to a2525+ * worker thread and queue that pwork. The work function will be passed the2626+ * pwork item when it is run (from process context) and any returned error will2727+ * be recorded in xfs_pwork_ctl.error. Work functions should check for errors2828+ * and abort if necessary; the non-zeroness of xfs_pwork_ctl.error does not2929+ * stop workqueue item processing.3030+ *3131+ * This is the rough equivalent of the xfsprogs workqueue code, though we can't3232+ * reuse that name here.3333+ */3434+3535+/* Invoke our caller's function. */3636+static void3737+xfs_pwork_work(3838+ struct work_struct *work)3939+{4040+ struct xfs_pwork *pwork;4141+ struct xfs_pwork_ctl *pctl;4242+ int error;4343+4444+ pwork = container_of(work, struct xfs_pwork, work);4545+ pctl = pwork->pctl;4646+ error = pctl->work_fn(pctl->mp, pwork);4747+ if (error && !pctl->error)4848+ pctl->error = error;4949+}5050+5151+/*5252+ * Set up control data for parallel work. @work_fn is the function that will5353+ * be called. @tag will be written into the kernel threads. @nr_threads is5454+ * the level of parallelism desired, or 0 for no limit.5555+ */5656+int5757+xfs_pwork_init(5858+ struct xfs_mount *mp,5959+ struct xfs_pwork_ctl *pctl,6060+ xfs_pwork_work_fn work_fn,6161+ const char *tag,6262+ unsigned int nr_threads)6363+{6464+#ifdef DEBUG6565+ if (xfs_globals.pwork_threads >= 0)6666+ nr_threads = xfs_globals.pwork_threads;6767+#endif6868+ trace_xfs_pwork_init(mp, nr_threads, current->pid);6969+7070+ pctl->wq = alloc_workqueue("%s-%d", WQ_FREEZABLE, nr_threads, tag,7171+ current->pid);7272+ if (!pctl->wq)7373+ return -ENOMEM;7474+ pctl->work_fn = work_fn;7575+ pctl->error = 0;7676+ pctl->mp = mp;7777+7878+ return 0;7979+}8080+8181+/* Queue some parallel work. */8282+void8383+xfs_pwork_queue(8484+ struct xfs_pwork_ctl *pctl,8585+ struct xfs_pwork *pwork)8686+{8787+ INIT_WORK(&pwork->work, xfs_pwork_work);8888+ pwork->pctl = pctl;8989+ queue_work(pctl->wq, &pwork->work);9090+}9191+9292+/* Wait for the work to finish and tear down the control structure. */9393+int9494+xfs_pwork_destroy(9595+ struct xfs_pwork_ctl *pctl)9696+{9797+ destroy_workqueue(pctl->wq);9898+ pctl->wq = NULL;9999+ return pctl->error;100100+}101101+102102+/*103103+ * Return the amount of parallelism that the data device can handle, or 0 for104104+ * no limit.105105+ */106106+unsigned int107107+xfs_pwork_guess_datadev_parallelism(108108+ struct xfs_mount *mp)109109+{110110+ struct xfs_buftarg *btp = mp->m_ddev_targp;111111+112112+ /*113113+ * For now we'll go with the most conservative setting possible,114114+ * which is two threads for an SSD and 1 thread everywhere else.115115+ */116116+ return blk_queue_nonrot(btp->bt_bdev->bd_queue) ? 2 : 1;117117+}
+58
fs/xfs/xfs_pwork.h
···11+/* SPDX-License-Identifier: GPL-2.0-or-later */22+/*33+ * Copyright (C) 2019 Oracle. All Rights Reserved.44+ * Author: Darrick J. Wong <darrick.wong@oracle.com>55+ */66+#ifndef __XFS_PWORK_H__77+#define __XFS_PWORK_H__88+99+struct xfs_pwork;1010+struct xfs_mount;1111+1212+typedef int (*xfs_pwork_work_fn)(struct xfs_mount *mp, struct xfs_pwork *pwork);1313+1414+/*1515+ * Parallel work coordination structure.1616+ */1717+struct xfs_pwork_ctl {1818+ struct workqueue_struct *wq;1919+ struct xfs_mount *mp;2020+ xfs_pwork_work_fn work_fn;2121+ int error;2222+};2323+2424+/*2525+ * Embed this parallel work control item inside your own work structure,2626+ * then queue work with it.2727+ */2828+struct xfs_pwork {2929+ struct work_struct work;3030+ struct xfs_pwork_ctl *pctl;3131+};3232+3333+#define XFS_PWORK_SINGLE_THREADED { .pctl = NULL }3434+3535+/* Have we been told to abort? */3636+static inline bool3737+xfs_pwork_ctl_want_abort(3838+ struct xfs_pwork_ctl *pctl)3939+{4040+ return pctl && pctl->error;4141+}4242+4343+/* Have we been told to abort? */4444+static inline bool4545+xfs_pwork_want_abort(4646+ struct xfs_pwork *pwork)4747+{4848+ return xfs_pwork_ctl_want_abort(pwork->pctl);4949+}5050+5151+int xfs_pwork_init(struct xfs_mount *mp, struct xfs_pwork_ctl *pctl,5252+ xfs_pwork_work_fn work_fn, const char *tag,5353+ unsigned int nr_threads);5454+void xfs_pwork_queue(struct xfs_pwork_ctl *pctl, struct xfs_pwork *pwork);5555+int xfs_pwork_destroy(struct xfs_pwork_ctl *pctl);5656+unsigned int xfs_pwork_guess_datadev_parallelism(struct xfs_mount *mp);5757+5858+#endif /* __XFS_PWORK_H__ */
···8282extern xfs_param_t xfs_params;83838484struct xfs_globals {8585+#ifdef DEBUG8686+ int pwork_threads; /* parallel workqueue threads */8787+#endif8588 int log_recovery_delay; /* log recovery delay (secs) */8689 int mount_delay; /* mount setup delay (secs) */8790 bool bug_on_assert; /* BUG() the kernel on assert failure */
+40
fs/xfs/xfs_sysfs.c
···204204}205205XFS_SYSFS_ATTR_RW(always_cow);206206207207+#ifdef DEBUG208208+/*209209+ * Override how many threads the parallel work queue is allowed to create.210210+ * This has to be a debug-only global (instead of an errortag) because one of211211+ * the main users of parallel workqueues is mount time quotacheck.212212+ */213213+STATIC ssize_t214214+pwork_threads_store(215215+ struct kobject *kobject,216216+ const char *buf,217217+ size_t count)218218+{219219+ int ret;220220+ int val;221221+222222+ ret = kstrtoint(buf, 0, &val);223223+ if (ret)224224+ return ret;225225+226226+ if (val < -1 || val > num_possible_cpus())227227+ return -EINVAL;228228+229229+ xfs_globals.pwork_threads = val;230230+231231+ return count;232232+}233233+234234+STATIC ssize_t235235+pwork_threads_show(236236+ struct kobject *kobject,237237+ char *buf)238238+{239239+ return snprintf(buf, PAGE_SIZE, "%d\n", xfs_globals.pwork_threads);240240+}241241+XFS_SYSFS_ATTR_RW(pwork_threads);242242+#endif /* DEBUG */243243+207244static struct attribute *xfs_dbg_attrs[] = {208245 ATTR_LIST(bug_on_assert),209246 ATTR_LIST(log_recovery_delay),210247 ATTR_LIST(mount_delay),211248 ATTR_LIST(always_cow),249249+#ifdef DEBUG250250+ ATTR_LIST(pwork_threads),251251+#endif212252 NULL,213253};214254