[PATCH] Implement multiple commit threads (3 of 6)
[email protected] Thu, 20 May 2004 16:50:26 -0500
| Newsgroups | gmane.comp.file-systems.jfs.patches |
|---|---|
| Message-ID | <[email protected]> |
# This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2004/05/19 11:29:37-05:00 [email protected] # JFS: Implement multiple commit threads # # For scalability, jfs now allows specifying the number of commit # threads with the module parameter commit_threads=<num>. It defaults # to the number of processors. # # You can also change the number of txBlocks and txLocks allocated with # the nTxBlock and nTxLock module parameters. "modinfo jfs.ko" for # specifics. # diff -Nru a/fs/jfs/jfs_incore.h b/fs/jfs/jfs_incore.h --- a/fs/jfs/jfs_incore.h 2004-05-20 16:47:30 -05:00 +++ b/fs/jfs/jfs_incore.h 2004-05-20 16:47:30 -05:00 @@ -152,6 +152,11 @@ pxd_t ait2; /* pxd describing AIT copy */ char uuid[16]; /* 128-bit uuid for volume */ char loguuid[16]; /* 128-bit uuid for log */ + /* + * commit_state is used for synchronization of the jfs_commit + * threads. It is protected by LAZY_LOCK(). + */ + int commit_state; /* commit state */ /* Formerly in ipimap */ uint gengen; /* inode generation generator*/ uint inostamp; /* shows inode belongs to fileset*/ @@ -163,6 +168,9 @@ unsigned long flag; /* mount time flags */ uint p_state; /* state prior to going no integrity */ }; + +/* jfs_sb_info commit_state */ +#define IN_LAZYCOMMIT 1 static inline struct jfs_inode_info *JFS_IP(struct inode *inode) { diff -Nru a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c --- a/fs/jfs/jfs_logmgr.c 2004-05-20 16:47:30 -05:00 +++ b/fs/jfs/jfs_logmgr.c 2004-05-20 16:47:30 -05:00 @@ -171,6 +171,7 @@ extern void txLazyUnlock(struct tblock * tblk); extern int jfs_stop_threads; extern struct completion jfsIOwait; +extern int jfs_tlocks_low; /* * forward references @@ -524,12 +525,7 @@ tblk->eor = log->eor; /* enqueue transaction to commit queue */ - tblk->cqnext = NULL; - if (log->cqueue.head) { - log->cqueue.tail->cqnext = tblk; - log->cqueue.tail = tblk; - } else - log->cqueue.head = log->cqueue.tail = tblk; + list_add_tail(&tblk->cqueue, &log->cqueue); LOGGC_UNLOCK(log); } @@ -587,7 +583,10 @@ * write or queue the full page at the tail of write queue */ /* get the tail tblk on commit queue */ - tblk = log->cqueue.tail; + if (list_empty(&log->cqueue)) + tblk = NULL; + else + tblk = list_entry(log->cqueue.prev, struct tblock, cqueue); /* every tblk who has COMMIT record on the current page, * and has not been committed, must be on commit queue @@ -688,8 +687,9 @@ if (tblk->xflag & COMMIT_LAZY) tblk->flag |= tblkGC_LAZY; - if ((!(log->cflag & logGC_PAGEOUT)) && log->cqueue.head && - (!(tblk->xflag & COMMIT_LAZY) || test_bit(log_FLUSH, &log->flag))) { + if ((!(log->cflag & logGC_PAGEOUT)) && (!list_empty(&log->cqueue)) && + (!(tblk->xflag & COMMIT_LAZY) || test_bit(log_FLUSH, &log->flag) + || jfs_tlocks_low)) { /* * No pageout in progress * @@ -753,7 +753,7 @@ struct logpage *lp; int gcpn; /* group commit page number */ struct tblock *tblk; - struct tblock *xtblk; + struct tblock *xtblk = NULL; /* * build the commit group of a log page @@ -762,15 +762,16 @@ * transactions with COMMIT records on the same log page. */ /* get the head tblk on the commit queue */ - tblk = xtblk = log->cqueue.head; - gcpn = tblk->pn; + gcpn = list_entry(log->cqueue.next, struct tblock, cqueue)->pn; + + list_for_each_entry(tblk, &log->cqueue, cqueue) { + if (tblk->pn != gcpn) + break; - while (tblk && tblk->pn == gcpn) { xtblk = tblk; /* state transition: (QUEUE, READY) -> COMMIT */ tblk->flag |= tblkGC_COMMIT; - tblk = tblk->cqnext; } tblk = xtblk; /* last tblk of the page */ @@ -816,7 +817,7 @@ unsigned long flags; struct jfs_log *log = bp->l_log; struct logpage *lp; - struct tblock *tblk; + struct tblock *tblk, *temp; //LOGGC_LOCK(log); spin_lock_irqsave(&log->gclock, flags); @@ -826,7 +827,9 @@ * remove/wakeup transactions from commit queue who were * group committed with the current log page */ - while ((tblk = log->cqueue.head) && (tblk->flag & tblkGC_COMMIT)) { + list_for_each_entry_safe(tblk, temp, &log->cqueue, cqueue) { + if (!(tblk->flag & tblkGC_COMMIT)) + break; /* if transaction was marked GC_COMMIT then * it has been shipped in the current pageout * and made it to disk - it is committed. @@ -836,11 +839,8 @@ tblk->flag |= tblkGC_ERROR; /* remove it from the commit queue */ - log->cqueue.head = tblk->cqnext; - if (log->cqueue.head == NULL) - log->cqueue.tail = NULL; + list_del(&tblk->cqueue); tblk->flag &= ~tblkGC_QUEUE; - tblk->cqnext = 0; if (tblk == log->flush_tblk) { /* we can stop flushing the log now */ @@ -893,9 +893,9 @@ * select the latest ready transaction as new group leader and * wake her up to lead her group. */ - if ((tblk = log->cqueue.head) && + if ((!list_empty(&log->cqueue)) && ((log->gcrtc > 0) || (tblk->bp->l_wqnext != NULL) || - test_bit(log_FLUSH, &log->flag))) + test_bit(log_FLUSH, &log->flag) || jfs_tlocks_low)) /* * Call lmGCwrite with new group leader */ @@ -1288,7 +1288,7 @@ init_waitqueue_head(&log->syncwait); - log->cqueue.head = log->cqueue.tail = NULL; + INIT_LIST_HEAD(&log->cqueue); log->flush_tblk = NULL; log->count = 0; @@ -1535,7 +1535,7 @@ void jfs_flush_journal(struct jfs_log *log, int wait) { int i; - struct tblock *target; + struct tblock *target = NULL; /* jfs_write_inode may call us during read-only mount */ if (!log) @@ -1545,13 +1545,12 @@ LOGGC_LOCK(log); - target = log->cqueue.head; - - if (target) { + if (!list_empty(&log->cqueue)) { /* * This ensures that we will keep writing to the journal as long * as there are unwritten commit records */ + target = list_entry(log->cqueue.prev, struct tblock, cqueue); if (test_bit(log_FLUSH, &log->flag)) { /* @@ -1602,16 +1601,16 @@ * If there was recent activity, we may need to wait * for the lazycommit thread to catch up */ - if (log->cqueue.head || !list_empty(&log->synclist)) { + if ((!list_empty(&log->cqueue)) || !list_empty(&log->synclist)) { for (i = 0; i < 800; i++) { /* Too much? */ current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ / 4); - if ((log->cqueue.head == NULL) && + if (list_empty(&log->cqueue) && list_empty(&log->synclist)) break; } } - assert(log->cqueue.head == NULL); + assert(list_empty(&log->cqueue)); assert(list_empty(&log->synclist)); clear_bit(log_FLUSH, &log->flag); } diff -Nru a/fs/jfs/jfs_logmgr.h b/fs/jfs/jfs_logmgr.h --- a/fs/jfs/jfs_logmgr.h 2004-05-20 16:47:30 -05:00 +++ b/fs/jfs/jfs_logmgr.h 2004-05-20 16:47:30 -05:00 @@ -398,10 +398,7 @@ /* commit */ uint cflag; /* 4: */ - struct { /* 8: FIFO commit queue header */ - struct tblock *head; - struct tblock *tail; - } cqueue; + struct list_head cqueue; /* FIFO commit queue */ struct tblock *flush_tblk; /* tblk we're waiting on for flush */ int gcrtc; /* 4: GC_READY transaction count */ struct tblock *gclrt; /* 4: latest GC_READY transaction */ diff -Nru a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c --- a/fs/jfs/jfs_txnmgr.c 2004-05-20 16:47:30 -05:00 +++ b/fs/jfs/jfs_txnmgr.c 2004-05-20 16:47:30 -05:00 @@ -48,6 +48,8 @@ #include <linux/smp_lock.h> #include <linux/completion.h> #include <linux/suspend.h> +#include <linux/module.h> +#include <linux/moduleparam.h> #include "jfs_incore.h" #include "jfs_filsys.h" #include "jfs_metapage.h" @@ -61,25 +63,22 @@ * transaction management structures */ static struct { - /* tblock */ int freetid; /* index of a free tid structure */ - wait_queue_head_t freewait; /* eventlist of free tblock */ - - /* tlock */ int freelock; /* index first free lock word */ + wait_queue_head_t freewait; /* eventlist of free tblock */ wait_queue_head_t freelockwait; /* eventlist of free tlock */ wait_queue_head_t lowlockwait; /* eventlist of ample tlocks */ int tlocksInUse; /* Number of tlocks in use */ - int TlocksLow; /* Indicates low number of available tlocks */ spinlock_t LazyLock; /* synchronize sync_queue & unlock_queue */ /* struct tblock *sync_queue; * Transactions waiting for data sync */ - struct tblock *unlock_queue; /* Txns waiting to be released */ - struct tblock *unlock_tail; /* Tail of unlock_queue */ + struct list_head unlock_queue; /* Txns waiting to be released */ struct list_head anon_list; /* inodes having anonymous txns */ struct list_head anon_list2; /* inodes having anonymous txns that couldn't be sync'ed */ } TxAnchor; +int jfs_tlocks_low; /* Indicates low number of available tlocks */ + #ifdef CONFIG_JFS_STATISTICS struct { uint txBegin; @@ -95,11 +94,19 @@ #endif static int nTxBlock = 512; /* number of transaction blocks */ -struct tblock *TxBlock; /* transaction block table */ +module_param(nTxBlock, int, 0); +MODULE_PARM_DESC(nTxBlock, + "Number of transaction blocks (default:512, max:65536)"); static int nTxLock = 4096; /* number of transaction locks */ -static int TxLockLWM = 4096*.4; /* Low water mark for number of txLocks used */ -static int TxLockHWM = 4096*.8; /* High water mark for number of txLocks used */ +module_param(nTxLock, int, 0); +MODULE_PARM_DESC(nTxLock, + "Number of transaction locks (default:4096, max:65536)"); + +struct tblock *TxBlock; /* transaction block table */ +static int TxLockLWM; /* Low water mark for number of txLocks used */ +static int TxLockHWM; /* High water mark for number of txLocks used */ +static int TxLockVHWM; /* Very High water mark */ struct tlock *TxLock; /* transaction lock table */ @@ -162,7 +169,6 @@ extern int jfs_commit_inode(struct inode *, int); extern int jfs_stop_threads; -struct task_struct *jfsCommitTask; extern struct completion jfsIOwait; /* @@ -210,9 +216,9 @@ TXN_SLEEP(&TxAnchor.freelockwait); TxAnchor.freelock = TxLock[lid].next; HIGHWATERMARK(stattx.maxlid, lid); - if ((++TxAnchor.tlocksInUse > TxLockHWM) && (TxAnchor.TlocksLow == 0)) { - jfs_info("txLockAlloc TlocksLow"); - TxAnchor.TlocksLow = 1; + if ((++TxAnchor.tlocksInUse > TxLockHWM) && (jfs_tlocks_low == 0)) { + jfs_info("txLockAlloc tlocks low"); + jfs_tlocks_low = 1; wake_up(&jfs_sync_thread_wait); } @@ -224,9 +230,9 @@ TxLock[lid].next = TxAnchor.freelock; TxAnchor.freelock = lid; TxAnchor.tlocksInUse--; - if (TxAnchor.TlocksLow && (TxAnchor.tlocksInUse < TxLockLWM)) { - jfs_info("txLockFree TlocksLow no more"); - TxAnchor.TlocksLow = 0; + if (jfs_tlocks_low && (TxAnchor.tlocksInUse < TxLockLWM)) { + jfs_info("txLockFree jfs_tlocks_low no more"); + jfs_tlocks_low = 0; TXN_WAKEUP(&TxAnchor.lowlockwait); } TXN_WAKEUP(&TxAnchor.freelockwait); @@ -245,12 +251,25 @@ { int k, size; + /* Verify tunable parameters */ + if (nTxBlock < 16) + nTxBlock = 16; /* No one should set it this low */ + if (nTxBlock > 65536) + nTxBlock = 65536; + if (nTxLock < 256) + nTxLock = 256; /* No one should set it this low */ + if (nTxLock > 65536) + nTxLock = 65536; /* * initialize transaction block (tblock) table * * transaction id (tid) = tblock index * tid = 0 is reserved. */ + TxLockLWM = (nTxLock * 4) / 10; + TxLockHWM = (nTxLock * 8) / 10; + TxLockVHWM = (nTxLock * 9) / 10; + size = sizeof(struct tblock) * nTxBlock; TxBlock = (struct tblock *) vmalloc(size); if (TxBlock == NULL) @@ -295,6 +314,9 @@ INIT_LIST_HEAD(&TxAnchor.anon_list); INIT_LIST_HEAD(&TxAnchor.anon_list2); + LAZY_LOCK_INIT(); + INIT_LIST_HEAD(&TxAnchor.unlock_queue); + stattx.maxlid = 1; /* statistics */ return 0; @@ -358,7 +380,7 @@ * unless COMMIT_FORCE or COMMIT_INODE (which may ultimately * free tlocks) */ - if (TxAnchor.TlocksLow) { + if (TxAnchor.tlocksInUse > TxLockVHWM) { INCREMENT(TxStat.txBegin_lockslow); TXN_SLEEP(&TxAnchor.lowlockwait); goto retry; @@ -450,7 +472,7 @@ /* * Don't begin transaction if we're getting starved for tlocks */ - if (TxAnchor.TlocksLow) { + if (TxAnchor.tlocksInUse > TxLockVHWM) { INCREMENT(TxStat.txBeginAnon_lockslow); TXN_SLEEP(&TxAnchor.lowlockwait); goto retry; @@ -2559,6 +2581,7 @@ if (!jfs_ip->atlhead) return; + TXN_LOCK(); xtlck = (struct tlock *) &jfs_ip->atlhead; while ((lid = xtlck->next)) { @@ -2579,10 +2602,9 @@ /* * If inode was on anon_list, remove it */ - TXN_LOCK(); list_del_init(&jfs_ip->anon_inode_list); - TXN_UNLOCK(); } + TXN_UNLOCK(); } @@ -2707,50 +2729,54 @@ int WorkDone; struct tblock *tblk; unsigned long flags; + struct jfs_sb_info *sbi; daemonize("jfsCommit"); - jfsCommitTask = current; - - LAZY_LOCK_INIT(); - TxAnchor.unlock_queue = TxAnchor.unlock_tail = 0; - complete(&jfsIOwait); do { LAZY_LOCK(flags); -restart: - WorkDone = 0; - while ((tblk = TxAnchor.unlock_queue)) { - /* - * We can't get ahead of user thread. Spinning is - * simpler than blocking/waking. We shouldn't spin - * very long, since user thread shouldn't be blocking - * between lmGroupCommit & txEnd. - */ - WorkDone = 1; + while (!list_empty(&TxAnchor.unlock_queue)) { + WorkDone = 0; + list_for_each_entry(tblk, &TxAnchor.unlock_queue, + cqueue) { - /* - * Remove first transaction from queue - */ - TxAnchor.unlock_queue = tblk->cqnext; - tblk->cqnext = 0; - if (TxAnchor.unlock_tail == tblk) - TxAnchor.unlock_tail = 0; + sbi = JFS_SBI(tblk->sb); + /* + * For each volume, the transactions must be + * handled in order. If another commit thread + * is handling a tblk for this superblock, + * skip it + */ + if (sbi->commit_state & IN_LAZYCOMMIT) + continue; - LAZY_UNLOCK(flags); - txLazyCommit(tblk); + sbi->commit_state |= IN_LAZYCOMMIT; + WorkDone = 1; - /* - * We can be running indefinitely if other processors - * are adding transactions to this list - */ - cond_resched(); - LAZY_LOCK(flags); - } + /* + * Remove transaction from queue + */ + list_del(&tblk->cqueue); - if (WorkDone) - goto restart; + LAZY_UNLOCK(flags); + txLazyCommit(tblk); + LAZY_LOCK(flags); + + sbi->commit_state &= ~IN_LAZYCOMMIT; + /* + * Don't continue in the for loop. (We can't + * anyway, it's unsafe!) We want to go back to + * the beginning of the list. + */ + break; + } + + /* If there was nothing to do, don't continue */ + if (!WorkDone) + break; + } if (current->flags & PF_FREEZE) { LAZY_UNLOCK(flags); @@ -2767,7 +2793,7 @@ } } while (!jfs_stop_threads); - if (TxAnchor.unlock_queue) + if (!list_empty(&TxAnchor.unlock_queue)) jfs_err("jfs_lazycommit being killed w/pending transactions!"); else jfs_info("jfs_lazycommit being killed\n"); @@ -2780,14 +2806,14 @@ LAZY_LOCK(flags); - if (TxAnchor.unlock_tail) - TxAnchor.unlock_tail->cqnext = tblk; - else - TxAnchor.unlock_queue = tblk; - TxAnchor.unlock_tail = tblk; - tblk->cqnext = 0; + list_add_tail(&tblk->cqueue, &TxAnchor.unlock_queue); + /* + * Don't wake up a commit thread if there is already one servicing + * this superblock. + */ + if (!(JFS_SBI(tblk->sb)->commit_state & IN_LAZYCOMMIT)) + wake_up(&jfs_commit_thread_wait); LAZY_UNLOCK(flags); - wake_up(&jfs_commit_thread_wait); } static void LogSyncRelease(struct metapage * mp) @@ -2821,7 +2847,7 @@ * completion * * This does almost the same thing as jfs_sync below. We don't - * worry about deadlocking when TlocksLow is set, since we would + * worry about deadlocking when jfs_tlocks_low is set, since we would * expect jfs_sync to get us out of that jam. */ void txQuiesce(struct super_block *sb) @@ -2912,7 +2938,7 @@ * write each inode on the anonymous inode list */ TXN_LOCK(); - while (TxAnchor.TlocksLow && !list_empty(&TxAnchor.anon_list)) { + while (jfs_tlocks_low && !list_empty(&TxAnchor.anon_list)) { jfs_ip = list_entry(TxAnchor.anon_list.next, struct jfs_inode_info, anon_inode_list); @@ -3008,18 +3034,16 @@ "freelockwait = %s\n" "lowlockwait = %s\n" "tlocksInUse = %d\n" - "TlocksLow = %d\n" - "unlock_queue = 0x%p\n" - "unlock_tail = 0x%p\n", + "jfs_tlocks_low = %d\n" + "unlock_queue is %sempty\n", TxAnchor.freetid, freewait, TxAnchor.freelock, freelockwait, lowlockwait, TxAnchor.tlocksInUse, - TxAnchor.TlocksLow, - TxAnchor.unlock_queue, - TxAnchor.unlock_tail); + jfs_tlocks_low, + list_empty(&TxAnchor.unlock_queue) ? "" : "not "); begin = offset; *start = buffer + begin; diff -Nru a/fs/jfs/jfs_txnmgr.h b/fs/jfs/jfs_txnmgr.h --- a/fs/jfs/jfs_txnmgr.h 2004-05-20 16:47:30 -05:00 +++ b/fs/jfs/jfs_txnmgr.h 2004-05-20 16:47:30 -05:00 @@ -1,5 +1,5 @@ /* - * Copyright (c) International Business Machines Corp., 2000-2002 + * Copyright (C) International Business Machines Corp., 2000-2004 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -53,7 +53,7 @@ u32 logtid; /* log transaction id */ /* commit management */ - struct tblock *cqnext; /* commit queue link */ + struct list_head cqueue; /* commit queue list */ s32 clsn; /* commit lsn */ struct lbuf *bp; s32 pn; /* commit record log page number */ @@ -93,16 +93,16 @@ * transaction lock */ struct tlock { - lid_t next; /* index next lockword on tid locklist + lid_t next; /* 2: index next lockword on tid locklist * next lockword on freelist */ - tid_t tid; /* transaction id holding lock */ + tid_t tid; /* 2: transaction id holding lock */ u16 flag; /* 2: lock control */ u16 type; /* 2: log type */ - struct metapage *mp; /* 4: object page buffer locked */ - struct inode *ip; /* 4: object */ + struct metapage *mp; /* 4/8: object page buffer locked */ + struct inode *ip; /* 4/8: object */ /* (16) */ s16 lock[24]; /* 48: overlay area */ @@ -167,7 +167,7 @@ #define TLOCKLONG 28 struct linelock { - u16 next; /* 2: next linelock */ + lid_t next; /* 2: next linelock */ s8 maxcnt; /* 1: */ s8 index; /* 1: */ @@ -183,7 +183,7 @@ #define dt_lock linelock struct xtlock { - u16 next; /* 2: */ + lid_t next; /* 2: */ s8 maxcnt; /* 1: */ s8 index; /* 1: */ @@ -214,7 +214,7 @@ * free maplock (i.e., number of maplock) in the tlock; */ struct maplock { - u16 next; /* 2: */ + lid_t next; /* 2: */ u8 maxcnt; /* 2: */ u8 index; /* 2: next free maplock index */ @@ -242,7 +242,7 @@ #define pxd_lock maplock struct xdlistlock { - u16 next; /* 2: */ + lid_t next; /* 2: */ u8 maxcnt; /* 2: */ u8 index; /* 2: */ diff -Nru a/fs/jfs/jfs_types.h b/fs/jfs/jfs_types.h --- a/fs/jfs/jfs_types.h 2004-05-20 16:47:30 -05:00 +++ b/fs/jfs/jfs_types.h 2004-05-20 16:47:30 -05:00 @@ -1,5 +1,5 @@ /* - * Copyright (c) International Business Machines Corp., 2000-2002 + * Copyright (C) International Business Machines Corp., 2000-2004 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,9 +34,12 @@ /* * transaction and lock id's + * + * Don't change these without carefully considering the impact on the + * size and alignment of all of the linelock variants */ -typedef uint tid_t; -typedef uint lid_t; +typedef u16 tid_t; +typedef u16 lid_t; /* * Almost identical to Linux's timespec, but not quite diff -Nru a/fs/jfs/super.c b/fs/jfs/super.c --- a/fs/jfs/super.c 2004-05-20 16:47:30 -05:00 +++ b/fs/jfs/super.c 2004-05-20 16:47:30 -05:00 @@ -23,6 +23,7 @@ #include <linux/parser.h> #include <linux/completion.h> #include <linux/vfs.h> +#include <linux/moduleparam.h> #include <asm/uaccess.h> #include "jfs_incore.h" @@ -44,15 +45,20 @@ static struct export_operations jfs_export_operations; static struct file_system_type jfs_fs_type; +#define MAX_COMMIT_THREADS 64 +static int commit_threads = 0; +module_param(commit_threads, int, 0); +MODULE_PARM_DESC(commit_threads, "Number of commit threads"); + int jfs_stop_threads; static pid_t jfsIOthread; -static pid_t jfsCommitThread; +static pid_t jfsCommitThread[MAX_COMMIT_THREADS]; static pid_t jfsSyncThread; DECLARE_COMPLETION(jfsIOwait); #ifdef CONFIG_JFS_DEBUG int jfsloglevel = JFS_LOGLEVEL_WARN; -MODULE_PARM(jfsloglevel, "i"); +module_param(jfsloglevel, int, 644); MODULE_PARM_DESC(jfsloglevel, "Specify JFS loglevel (0, 1 or 2)"); #endif @@ -564,6 +570,7 @@ static int __init init_jfs_fs(void) { + int i; int rc; jfs_inode_cachep = @@ -600,12 +607,23 @@ } wait_for_completion(&jfsIOwait); /* Wait until thread starts */ - jfsCommitThread = kernel_thread(jfs_lazycommit, 0, CLONE_KERNEL); - if (jfsCommitThread < 0) { - jfs_err("init_jfs_fs: fork failed w/rc = %d", jfsCommitThread); - goto kill_iotask; + if (commit_threads < 1) + commit_threads = num_online_cpus(); + else if (commit_threads > MAX_COMMIT_THREADS) + commit_threads = MAX_COMMIT_THREADS; + + for (i = 0; i < commit_threads; i++) { + jfsCommitThread[i] = kernel_thread(jfs_lazycommit, 0, + CLONE_KERNEL); + if (jfsCommitThread[i] < 0) { + jfs_err("init_jfs_fs: fork failed w/rc = %d", + jfsCommitThread[i]); + commit_threads = i; + goto kill_committask; + } + /* Wait until thread starts */ + wait_for_completion(&jfsIOwait); } - wait_for_completion(&jfsIOwait); /* Wait until thread starts */ jfsSyncThread = kernel_thread(jfs_sync, 0, CLONE_KERNEL); if (jfsSyncThread < 0) { @@ -622,10 +640,10 @@ kill_committask: jfs_stop_threads = 1; - wake_up(&jfs_commit_thread_wait); - wait_for_completion(&jfsIOwait); /* Wait for thread exit */ -kill_iotask: - jfs_stop_threads = 1; + wake_up_all(&jfs_commit_thread_wait); + for (i = 0; i < commit_threads; i++) + wait_for_completion(&jfsIOwait); + wake_up(&jfs_IO_thread_wait); wait_for_completion(&jfsIOwait); /* Wait for thread exit */ end_txmngr: @@ -639,6 +657,8 @@ static void __exit exit_jfs_fs(void) { + int i; + jfs_info("exit_jfs_fs called"); jfs_stop_threads = 1; @@ -646,8 +666,9 @@ metapage_exit(); wake_up(&jfs_IO_thread_wait); wait_for_completion(&jfsIOwait); /* Wait until IO thread exits */ - wake_up(&jfs_commit_thread_wait); - wait_for_completion(&jfsIOwait); /* Wait until Commit thread exits */ + wake_up_all(&jfs_commit_thread_wait); + for (i = 0; i < commit_threads; i++) + wait_for_completion(&jfsIOwait); wake_up(&jfs_sync_thread_wait); wait_for_completion(&jfsIOwait); /* Wait until Sync thread exits */ #ifdef PROC_FS_JFS