Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S935622Ab3DIJA5 (ORCPT ); Tue, 9 Apr 2013 05:00:57 -0400 Received: from mailout2.samsung.com ([203.254.224.25]:59070 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934875Ab3DIJAu (ORCPT ); Tue, 9 Apr 2013 05:00:50 -0400 X-AuditID: cbfee691-b7f5f6d000002fda-b6-5163d8c03634 Message-id: <1365497997.4353.73.camel@kjgkr> Subject: Re: [PATCH v3] f2fs: introduce a new global lock scheme From: Jaegeuk Kim Reply-to: jaegeuk.kim@samsung.com To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net Date: Tue, 09 Apr 2013 17:59:57 +0900 In-reply-to: <1365126869.4353.36.camel@kjgkr> References: <1365040434-17264-1-git-send-email-jaegeuk.kim@samsung.com> <1365126869.4353.36.camel@kjgkr> Organization: samsung Content-type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="=-m1FxC3NOzLU6cgfqMI9/" X-Mailer: Evolution 3.2.3-0ubuntu6 MIME-version: 1.0 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrNIsWRmVeSWpSXmKPExsVy+t8zY92DN5IDDU7O4re4tMjdYs/ekywW l3fNYXNg9ti94DOTx+dNcgFMUVw2Kak5mWWpRfp2CVwZj5YcYyp4fZipYtPGZrYGxr9tTF2M nBwSAiYSj7rvs0DYYhIX7q1n62Lk4hASWMYocbLlOAtM0Z2N6xghEosYJT5MWsAMkhASeM0o cfkWaxcjBwevgI7EzxN1IGFhAQeJ189esoCE2QS0JTbvN4CoVpR4u/8uWLUIkH35vROIySzg IbHrWClIBYuAqsSFS7/YQGxOAV2Jju61rBCdWRLPJx0As/kFRCVOtn5iBLGZBaokjq49ywxx pJLE7vZOdhCbV0BQ4sfkeywgB0sIfGSXuP37JyvEAgGJb5MPgV0mISArsekAVK+kxMEVN1gm MIrPQjJ2FpJREHFNidbtv9khbG2JZQtfM0PYthLr1r2HqrGR2HR1ASOELS+x/e0c5gWM7KsY RVMLkguKk9KLTPWKE3OLS/PS9ZLzczcxQmJ04g7G+wesDzFWAZ04kVlKNDkfGON5JfGGxmZG FqYmpsZG5pZmVBFWEudVb7EOFBJITyxJzU5NLUgtii8qzUktPsTIxMEp1cAYf01cN/9c0fUM rlUbXfhTjxdtFLbcwDRFcH6qxwYNm237b1aLPZs4j7910lM2UwvvI7zRLgzv3a6pJAWL/rn3 3Nhgz7/7HyZerM65HDpNIFPq0SeRnVIr1/1YsOxVqVZgprKMyNm0BiffUMFLHzfvW7y2m+WL 9sX08iXFfIzb1Wat2BLR0npLiaU4I9FQi7moOBEAmy7phv4CAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrDKsWRmVeSWpSXmKPExsVy+t9jAd0DN5IDDVZMk7G4tMjdYs/ekywW l3fNYXNg9ti94DOTx+dNcgFMUQ2MNhmpiSmpRQqpecn5KZl56bZK3sHxzvGmZgaGuoaWFuZK CnmJuam2Si4+AbpumTlAO5QUyhJzSoFCAYnFxUr6dpgmhIa46VrANEbo+oYEwfUYGaCBhHWM GY+WHGMqeH2YqWLTxma2Bsa/bUxdjJwcEgImEnc2rmOEsMUkLtxbz9bFyMUhJLCIUeLDpAXM IAkhgdeMEpdvsXYxcnDwCuhI/DxRBxIWFnCQeP3sJQtImE1AW2LzfgOIakWJt/vvglWLANmX 3zuBmMwCHhK7jpWCVLAIqEpcuPSLDcTmFNCV6OheywrRmSXxfNIBMJtfQFTiZOsnsMOYBaok jq49ywxxpJLE7vZOdhCbV0BQ4sfkeywTGAVnISmbhSQFEdeUaN3+mx3C1pZYtvA1M4RtK7Fu 3XuoGhuJTVcXMELY8hLb385hXsDIvopRNLUguaA4KT3XSK84Mbe4NC9dLzk/dxMjOAE8k97B uKrB4hCjAAejEg/vhSdJgUKsiWXFlbmHGFWA5jzasPoCoxRLXn5eqpIIb7dhcqAQb0piZVVq UX58UWlOavEhxomMwOCYyCwlmpwPTFt5JfGGxiZmRpZGZhZGJubmtBRWEuc92GodKCSQnliS mp2aWpBaBHMUEwenVANj3NUzMwtC11b8udMV6RYW6MXbYVS9drtQsuWjx9NDcxQ/KKfWhjst 2bo7IMq/RsfnKJdodzzHnKBtfmtDJVQ+fH4WpZ241N/21/WOqX93S2sW6cnJu37p3ph68JZC U2lE0FXLZ3dCI0xSJK/2f/8413Xi7unbVh+UPHf+1ITgMxGTPyw//jRAiaU4I9FQi7moOBEA MNUMWX8DAAA= DLP-Filter: Pass X-MTR: 20000000000000000@CPGS X-CFilter-Loop: Reflected Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 40238 Lines: 1465 --=-m1FxC3NOzLU6cgfqMI9/ Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Chang log from v2: o resolve a deadlock path [ilock count] [call path] 1 =20 0 f2fs_unlink 0 - truncate_hole 0 - try to grab ilock (wait) =46rom 66b15368995b7218a37f589b9ada0c63794c4dc6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 22 Nov 2012 16:21:29 +0900 Subject: [PATCH] f2fs: introduce a new global lock scheme Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net In the previous version, f2fs uses global locks according to the usage types, such as directory operations, block allocation, block write, and so on. Reference the following lock types in f2fs.h. enum lock_type { RENAME, /* for renaming operations */ DENTRY_OPS, /* for directory operations */ DATA_WRITE, /* for data write */ DATA_NEW, /* for data allocation */ DATA_TRUNC, /* for data truncate */ NODE_NEW, /* for node allocation */ NODE_TRUNC, /* for node truncate */ NODE_WRITE, /* for node write */ NR_LOCK_TYPE, }; In that case, we lose the performance under the multi-threading environment, since every types of operations must be conducted one at a time. In order to address the problem, let's share the locks globally with a mutex array regardless of any types. So, let users grab a mutex and perform their jobs in parallel as much as possbile. For this, I propose a new global lock scheme as follows. 0. Data structure - f2fs_sb_info -> mutex_lock[NR_GLOBAL_LOCKS] - f2fs_sb_info -> node_write 1. mutex_lock_op(sbi) - try to get an avaiable lock from the array. - returns the index of the gottern lock variable. 2. mutex_unlock_op(sbi, index of the lock) - unlock the given index of the lock. 3. mutex_lock_all(sbi) - grab all the locks in the array before the checkpoint. 4. mutex_unlock_all(sbi) - release all the locks in the array after checkpoint. 5. block_operations() - call mutex_lock_all() - sync_dirty_dir_inodes() - grab node_write - sync_node_pages() Note that, the pairs of mutex_lock_op()/mutex_unlock_op() and mutex_lock_all()/mutex_unlock_all() should be used together. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 39 ++++++------------- fs/f2fs/data.c | 77 +++++++++++++++++++------------------ fs/f2fs/dir.c | 105 +++++++++++++++++++++++---------------------------- fs/f2fs/f2fs.h | 63 ++++++++++++++++++++----------- fs/f2fs/file.c | 45 +++++++++++----------- fs/f2fs/gc.c | 2 - fs/f2fs/inode.c | 48 ++++++++++++----------- fs/f2fs/namei.c | 44 +++++++++++++-------- fs/f2fs/node.c | 44 +++++++++------------ fs/f2fs/recovery.c | 8 +++- fs/f2fs/super.c | 4 +- fs/f2fs/xattr.c | 10 +++-- 12 files changed, 248 insertions(+), 241 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 93fd57d..be6aa2e 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -543,54 +543,39 @@ retry: */ static void block_operations(struct f2fs_sb_info *sbi) { - int t; struct writeback_control wbc =3D { .sync_mode =3D WB_SYNC_ALL, .nr_to_write =3D LONG_MAX, .for_reclaim =3D 0, }; +retry_flush_dents: + mutex_lock_all(sbi); =20 - /* Stop renaming operation */ - mutex_lock_op(sbi, RENAME); - mutex_lock_op(sbi, DENTRY_OPS); - -retry_dents: /* write all the dirty dentry pages */ - sync_dirty_dir_inodes(sbi); - - mutex_lock_op(sbi, DATA_WRITE); if (get_pages(sbi, F2FS_DIRTY_DENTS)) { - mutex_unlock_op(sbi, DATA_WRITE); - goto retry_dents; + mutex_unlock_all(sbi); + sync_dirty_dir_inodes(sbi); + goto retry_flush_dents; } =20 - /* block all the operations */ - for (t =3D DATA_NEW; t <=3D NODE_TRUNC; t++) - mutex_lock_op(sbi, t); - - mutex_lock(&sbi->write_inode); - /* * POR: we should ensure that there is no dirty node pages * until finishing nat/sit flush. */ -retry: - sync_node_pages(sbi, 0, &wbc); - - mutex_lock_op(sbi, NODE_WRITE); +retry_flush_nodes: + mutex_lock(&sbi->node_write); =20 if (get_pages(sbi, F2FS_DIRTY_NODES)) { - mutex_unlock_op(sbi, NODE_WRITE); - goto retry; + mutex_unlock(&sbi->node_write); + sync_node_pages(sbi, 0, &wbc); + goto retry_flush_nodes; } - mutex_unlock(&sbi->write_inode); } =20 static void unblock_operations(struct f2fs_sb_info *sbi) { - int t; - for (t =3D NODE_WRITE; t >=3D RENAME; t--) - mutex_unlock_op(sbi, t); + mutex_unlock(&sbi->node_write); + mutex_unlock_all(sbi); } =20 static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index cf9ff5f..72a1b30 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -260,6 +260,9 @@ struct page *get_lock_data_page(struct inode *inode, pgoff_t index) /* * Caller ensures that this data page is never allocated. * A new zero-filled data page is allocated in the page cache. + * + * Also, caller should grab and release a mutex by calling mutex_lock_op() and + * mutex_unlock_op(). */ struct page *get_new_data_page(struct inode *inode, pgoff_t index, bool new_i_size) @@ -479,10 +482,11 @@ static int f2fs_write_data_page(struct page *page, const pgoff_t end_index =3D ((unsigned long long) i_size) >> PAGE_CACHE_SHIFT; unsigned offset; + bool need_balance_fs =3D false; int err =3D 0; =20 if (page->index < end_index) - goto out; + goto write; =20 /* * If the offset is out-of-range of file size, @@ -494,50 +498,46 @@ static int f2fs_write_data_page(struct page *page, dec_page_count(sbi, F2FS_DIRTY_DENTS); inode_dec_dirty_dents(inode); } - goto unlock_out; + goto out; } =20 zero_user_segment(page, offset, PAGE_CACHE_SIZE); -out: - if (sbi->por_doing) - goto redirty_out; - - if (wbc->for_reclaim && !S_ISDIR(inode->i_mode) && ! is_cold_data(page)) +write: + if (sbi->por_doing) { + err =3D AOP_WRITEPAGE_ACTIVATE; goto redirty_out; + } =20 - mutex_lock_op(sbi, DATA_WRITE); + /* Dentry blocks are controlled by checkpoint */ if (S_ISDIR(inode->i_mode)) { dec_page_count(sbi, F2FS_DIRTY_DENTS); inode_dec_dirty_dents(inode); + err =3D do_write_data_page(page); + } else { + int ilock =3D mutex_lock_op(sbi); + err =3D do_write_data_page(page); + mutex_unlock_op(sbi, ilock); + need_balance_fs =3D true; } - err =3D do_write_data_page(page); - if (err && err !=3D -ENOENT) { - wbc->pages_skipped++; - set_page_dirty(page); - } - mutex_unlock_op(sbi, DATA_WRITE); + if (err =3D=3D -ENOENT) + goto out; + else if (err) + goto redirty_out; =20 if (wbc->for_reclaim) f2fs_submit_bio(sbi, DATA, true); =20 - if (err =3D=3D -ENOENT) - goto unlock_out; - clear_cold_data(page); +out: unlock_page(page); - - if (!wbc->for_reclaim && !S_ISDIR(inode->i_mode)) + if (need_balance_fs) f2fs_balance_fs(sbi); return 0; =20 -unlock_out: - unlock_page(page); - return (err =3D=3D -ENOENT) ? 0 : err; - redirty_out: wbc->pages_skipped++; set_page_dirty(page); - return AOP_WRITEPAGE_ACTIVATE; + return err; } =20 #define MAX_DESIRED_PAGES_WP 4096 @@ -592,6 +592,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, pgoff_t index =3D ((unsigned long long) pos) >> PAGE_CACHE_SHIFT; struct dnode_of_data dn; int err =3D 0; + int ilock; =20 /* for nobh_write_end */ *fsdata =3D NULL; @@ -603,28 +604,21 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, return -ENOMEM; *pagep =3D page; =20 - mutex_lock_op(sbi, DATA_NEW); + ilock =3D mutex_lock_op(sbi); =20 set_new_dnode(&dn, inode, NULL, NULL, 0); err =3D get_dnode_of_data(&dn, index, ALLOC_NODE); - if (err) { - mutex_unlock_op(sbi, DATA_NEW); - f2fs_put_page(page, 1); - return err; - } + if (err) + goto err; =20 - if (dn.data_blkaddr =3D=3D NULL_ADDR) { + if (dn.data_blkaddr =3D=3D NULL_ADDR) err =3D reserve_new_block(&dn); - if (err) { - f2fs_put_dnode(&dn); - mutex_unlock_op(sbi, DATA_NEW); - f2fs_put_page(page, 1); - return err; - } - } + f2fs_put_dnode(&dn); + if (err) + goto err; =20 - mutex_unlock_op(sbi, DATA_NEW); + mutex_unlock_op(sbi, ilock); =20 if ((len =3D=3D PAGE_CACHE_SIZE) || PageUptodate(page)) return 0; @@ -654,6 +648,11 @@ out: SetPageUptodate(page); clear_cold_data(page); return 0; + +err: + mutex_unlock_op(sbi, ilock); + f2fs_put_page(page, 1); + return err; } =20 static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 2851ae6..cd3342d 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -249,9 +249,6 @@ ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr) void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, struct page *page, struct inode *inode) { - struct f2fs_sb_info *sbi =3D F2FS_SB(dir->i_sb); - - mutex_lock_op(sbi, DENTRY_OPS); lock_page(page); wait_on_page_writeback(page); de->ino =3D cpu_to_le32(inode->i_ino); @@ -265,7 +262,6 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, F2FS_I(inode)->i_pino =3D dir->i_ino; =20 f2fs_put_page(page, 1); - mutex_unlock_op(sbi, DENTRY_OPS); } =20 void init_dent_inode(const struct qstr *name, struct page *ipage) @@ -284,6 +280,43 @@ void init_dent_inode(const struct qstr *name, struct page *ipage) set_page_dirty(ipage); } =20 +static int make_empty_dir(struct inode *inode, struct inode *parent) +{ + struct page *dentry_page; + struct f2fs_dentry_block *dentry_blk; + struct f2fs_dir_entry *de; + void *kaddr; + + dentry_page =3D get_new_data_page(inode, 0, true); + if (IS_ERR(dentry_page)) + return PTR_ERR(dentry_page); + + kaddr =3D kmap_atomic(dentry_page); + dentry_blk =3D (struct f2fs_dentry_block *)kaddr; + + de =3D &dentry_blk->dentry[0]; + de->name_len =3D cpu_to_le16(1); + de->hash_code =3D 0; + de->ino =3D cpu_to_le32(inode->i_ino); + memcpy(dentry_blk->filename[0], ".", 1); + set_de_type(de, inode); + + de =3D &dentry_blk->dentry[1]; + de->hash_code =3D 0; + de->name_len =3D cpu_to_le16(2); + de->ino =3D cpu_to_le32(parent->i_ino); + memcpy(dentry_blk->filename[1], "..", 2); + set_de_type(de, inode); + + test_and_set_bit_le(0, &dentry_blk->dentry_bitmap); + test_and_set_bit_le(1, &dentry_blk->dentry_bitmap); + kunmap_atomic(kaddr); + + set_page_dirty(dentry_page); + f2fs_put_page(dentry_page, 1); + return 0; +} + static int init_inode_metadata(struct inode *inode, struct inode *dir, const struct qstr *name) { @@ -294,7 +327,7 @@ static int init_inode_metadata(struct inode *inode, return err; =20 if (S_ISDIR(inode->i_mode)) { - err =3D f2fs_make_empty(inode, dir); + err =3D make_empty_dir(inode, dir); if (err) { remove_inode_page(inode); return err; @@ -317,7 +350,7 @@ static int init_inode_metadata(struct inode *inode, } if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { inc_nlink(inode); - f2fs_write_inode(inode, NULL); + update_inode_page(inode); } return 0; } @@ -341,7 +374,7 @@ static void update_parent_metadata(struct inode *dir, struct inode *inode, } =20 if (need_dir_update) - f2fs_write_inode(dir, NULL); + update_inode_page(dir); else mark_inode_dirty(dir); =20 @@ -373,6 +406,10 @@ next: goto next; } =20 +/* + * Caller should grab and release a mutex by calling mutex_lock_op() and + * mutex_unlock_op(). + */ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *inode) { unsigned int bit_pos; @@ -382,7 +419,6 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *in f2fs_hash_t dentry_hash; struct f2fs_dir_entry *de; unsigned int nbucket, nblock; - struct f2fs_sb_info *sbi =3D F2FS_SB(dir->i_sb); size_t namelen =3D name->len; struct page *dentry_page =3D NULL; struct f2fs_dentry_block *dentry_blk =3D NULL; @@ -412,12 +448,9 @@ start: bidx =3D dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket)); =20 for (block =3D bidx; block <=3D (bidx + nblock - 1); block++) { - mutex_lock_op(sbi, DENTRY_OPS); dentry_page =3D get_new_data_page(dir, block, true); - if (IS_ERR(dentry_page)) { - mutex_unlock_op(sbi, DENTRY_OPS); + if (IS_ERR(dentry_page)) return PTR_ERR(dentry_page); - } =20 dentry_blk =3D kmap(dentry_page); bit_pos =3D room_for_filename(dentry_blk, slots); @@ -426,7 +459,6 @@ start: =20 kunmap(dentry_page); f2fs_put_page(dentry_page, 1); - mutex_unlock_op(sbi, DENTRY_OPS); } =20 /* Move to next level to find the empty slot for new dentry */ @@ -456,7 +488,6 @@ add_dentry: fail: kunmap(dentry_page); f2fs_put_page(dentry_page, 1); - mutex_unlock_op(sbi, DENTRY_OPS); return err; } =20 @@ -476,8 +507,6 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, void *kaddr =3D page_address(page); int i; =20 - mutex_lock_op(sbi, DENTRY_OPS); - lock_page(page); wait_on_page_writeback(page); =20 @@ -497,7 +526,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, =20 if (inode && S_ISDIR(inode->i_mode)) { drop_nlink(dir); - f2fs_write_inode(dir, NULL); + update_inode_page(dir); } else { mark_inode_dirty(dir); } @@ -509,7 +538,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, drop_nlink(inode); i_size_write(inode, 0); } - f2fs_write_inode(inode, NULL); + update_inode_page(inode); + if (inode->i_nlink =3D=3D 0) add_orphan_inode(sbi, inode->i_ino); } @@ -522,45 +552,6 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, inode_dec_dirty_dents(dir); } f2fs_put_page(page, 1); - - mutex_unlock_op(sbi, DENTRY_OPS); -} - -int f2fs_make_empty(struct inode *inode, struct inode *parent) -{ - struct page *dentry_page; - struct f2fs_dentry_block *dentry_blk; - struct f2fs_dir_entry *de; - void *kaddr; - - dentry_page =3D get_new_data_page(inode, 0, true); - if (IS_ERR(dentry_page)) - return PTR_ERR(dentry_page); - - kaddr =3D kmap_atomic(dentry_page); - dentry_blk =3D (struct f2fs_dentry_block *)kaddr; - - de =3D &dentry_blk->dentry[0]; - de->name_len =3D cpu_to_le16(1); - de->hash_code =3D f2fs_dentry_hash(".", 1); - de->ino =3D cpu_to_le32(inode->i_ino); - memcpy(dentry_blk->filename[0], ".", 1); - set_de_type(de, inode); - - de =3D &dentry_blk->dentry[1]; - de->hash_code =3D f2fs_dentry_hash("..", 2); - de->name_len =3D cpu_to_le16(2); - de->ino =3D cpu_to_le32(parent->i_ino); - memcpy(dentry_blk->filename[1], "..", 2); - set_de_type(de, inode); - - test_and_set_bit_le(0, &dentry_blk->dentry_bitmap); - test_and_set_bit_le(1, &dentry_blk->dentry_bitmap); - kunmap_atomic(kaddr); - - set_page_dirty(dentry_page); - f2fs_put_page(dentry_page, 1); - return 0; } =20 bool f2fs_empty_dir(struct inode *dir) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 71eacd3..06cc75c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -309,23 +309,12 @@ enum count_type { }; =20 /* - * FS_LOCK nesting subclasses for the lock validator: - * - * The locking order between these classes is - * RENAME -> DENTRY_OPS -> DATA_WRITE -> DATA_NEW - * -> DATA_TRUNC -> NODE_WRITE -> NODE_NEW -> NODE_TRUNC + * Uses as sbi->fs_lock[NR_GLOBAL_LOCKS]. + * The checkpoint procedure blocks all the locks in this fs_lock array. + * Some FS operations grab free locks, and if there is no free lock, + * then wait to grab a lock in a round-robin manner. */ -enum lock_type { - RENAME, /* for renaming operations */ - DENTRY_OPS, /* for directory operations */ - DATA_WRITE, /* for data write */ - DATA_NEW, /* for data allocation */ - DATA_TRUNC, /* for data truncate */ - NODE_NEW, /* for node allocation */ - NODE_TRUNC, /* for node truncate */ - NODE_WRITE, /* for node write */ - NR_LOCK_TYPE, -}; +#define NR_GLOBAL_LOCKS 8 =20 /* * The below are the page types of bios used in submti_bio(). @@ -365,10 +354,11 @@ struct f2fs_sb_info { /* for checkpoint */ struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ struct inode *meta_inode; /* cache meta blocks */ - struct mutex cp_mutex; /* for checkpoint procedure */ - struct mutex fs_lock[NR_LOCK_TYPE]; /* for blocking FS operations */ - struct mutex write_inode; /* mutex for write inode */ + struct mutex cp_mutex; /* checkpoint procedure lock */ + struct mutex fs_lock[NR_GLOBAL_LOCKS]; /* blocking FS operations */ + struct mutex node_write; /* locking node writes */ struct mutex writepages; /* mutex for writepages() */ + unsigned char next_lock_num; /* round-robin global locks */ int por_doing; /* recovery is doing or not */ =20 /* for orphan inode management */ @@ -503,14 +493,40 @@ static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) cp->ckpt_flags =3D cpu_to_le32(ckpt_flags); } =20 -static inline void mutex_lock_op(struct f2fs_sb_info *sbi, enum lock_type t) +static inline void mutex_lock_all(struct f2fs_sb_info *sbi) +{ + int i =3D 0; + for (; i < NR_GLOBAL_LOCKS; i++) + mutex_lock(&sbi->fs_lock[i]); +} + +static inline void mutex_unlock_all(struct f2fs_sb_info *sbi) { - mutex_lock_nested(&sbi->fs_lock[t], t); + int i =3D 0; + for (; i < NR_GLOBAL_LOCKS; i++) + mutex_unlock(&sbi->fs_lock[i]); } =20 -static inline void mutex_unlock_op(struct f2fs_sb_info *sbi, enum lock_type t) +static inline int mutex_lock_op(struct f2fs_sb_info *sbi) { - mutex_unlock(&sbi->fs_lock[t]); + unsigned char next_lock =3D sbi->next_lock_num % NR_GLOBAL_LOCKS; + int i =3D 0; + + for (; i < NR_GLOBAL_LOCKS; i++) + if (mutex_trylock(&sbi->fs_lock[i])) + return i; + + mutex_lock(&sbi->fs_lock[next_lock]); + sbi->next_lock_num++; + return next_lock; +} + +static inline void mutex_unlock_op(struct f2fs_sb_info *sbi, int ilock) +{ + if (ilock < 0) + return; + BUG_ON(ilock >=3D NR_GLOBAL_LOCKS); + mutex_unlock(&sbi->fs_lock[ilock]); } =20 /* @@ -879,6 +895,7 @@ long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long); void f2fs_set_inode_flags(struct inode *); struct inode *f2fs_iget(struct super_block *, unsigned long); void update_inode(struct inode *, struct page *); +int update_inode_page(struct inode *); int f2fs_write_inode(struct inode *, struct writeback_control *); void f2fs_evict_inode(struct inode *); =20 diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 155b362..70a5d6b 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -34,19 +34,18 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, struct f2fs_sb_info *sbi =3D F2FS_SB(inode->i_sb); block_t old_blk_addr; struct dnode_of_data dn; - int err; + int err, ilock; =20 f2fs_balance_fs(sbi); =20 sb_start_pagefault(inode->i_sb); =20 - mutex_lock_op(sbi, DATA_NEW); - /* block allocation */ + ilock =3D mutex_lock_op(sbi); set_new_dnode(&dn, inode, NULL, NULL, 0); err =3D get_dnode_of_data(&dn, page->index, ALLOC_NODE); if (err) { - mutex_unlock_op(sbi, DATA_NEW); + mutex_unlock_op(sbi, ilock); goto out; } =20 @@ -56,13 +55,12 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, err =3D reserve_new_block(&dn); if (err) { f2fs_put_dnode(&dn); - mutex_unlock_op(sbi, DATA_NEW); + mutex_unlock_op(sbi, ilock); goto out; } } f2fs_put_dnode(&dn); - - mutex_unlock_op(sbi, DATA_NEW); + mutex_unlock_op(sbi, ilock); =20 lock_page(page); if (page->mapping !=3D inode->i_mapping || @@ -223,20 +221,19 @@ static int truncate_blocks(struct inode *inode, u64 from) unsigned int blocksize =3D inode->i_sb->s_blocksize; struct dnode_of_data dn; pgoff_t free_from; - int count =3D 0; + int count =3D 0, ilock =3D -1; int err; =20 free_from =3D (pgoff_t) ((from + blocksize - 1) >> (sbi->log_blocksize)); =20 - mutex_lock_op(sbi, DATA_TRUNC); - + ilock =3D mutex_lock_op(sbi); set_new_dnode(&dn, inode, NULL, NULL, 0); err =3D get_dnode_of_data(&dn, free_from, LOOKUP_NODE); if (err) { if (err =3D=3D -ENOENT) goto free_next; - mutex_unlock_op(sbi, DATA_TRUNC); + mutex_unlock_op(sbi, ilock); return err; } =20 @@ -247,6 +244,7 @@ static int truncate_blocks(struct inode *inode, u64 from) =20 count -=3D dn.ofs_in_node; BUG_ON(count < 0); + if (dn.ofs_in_node || IS_INODE(dn.node_page)) { truncate_data_blocks_range(&dn, count); free_from +=3D count; @@ -255,7 +253,7 @@ static int truncate_blocks(struct inode *inode, u64 from) f2fs_put_dnode(&dn); free_next: err =3D truncate_inode_blocks(inode, free_from); - mutex_unlock_op(sbi, DATA_TRUNC); + mutex_unlock_op(sbi, ilock); =20 /* lastly zero out the first data page */ truncate_partial_data_page(inode, from); @@ -363,15 +361,16 @@ static void fill_zero(struct inode *inode, pgoff_t index, { struct f2fs_sb_info *sbi =3D F2FS_SB(inode->i_sb); struct page *page; + int ilock; =20 if (!len) return; =20 f2fs_balance_fs(sbi); =20 - mutex_lock_op(sbi, DATA_NEW); + ilock =3D mutex_lock_op(sbi); page =3D get_new_data_page(inode, index, false); - mutex_unlock_op(sbi, DATA_NEW); + mutex_unlock_op(sbi, ilock); =20 if (!IS_ERR(page)) { wait_on_page_writeback(page); @@ -390,11 +389,9 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end) struct dnode_of_data dn; struct f2fs_sb_info *sbi =3D F2FS_SB(inode->i_sb); =20 - mutex_lock_op(sbi, DATA_TRUNC); set_new_dnode(&dn, inode, NULL, NULL, 0); err =3D get_dnode_of_data(&dn, index, LOOKUP_NODE); if (err) { - mutex_unlock_op(sbi, DATA_TRUNC); if (err =3D=3D -ENOENT) continue; return err; @@ -403,7 +400,6 @@ int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end) if (dn.data_blkaddr !=3D NULL_ADDR) truncate_data_blocks_range(&dn, 1); f2fs_put_dnode(&dn); - mutex_unlock_op(sbi, DATA_TRUNC); } return 0; } @@ -434,6 +430,7 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len, int mode) struct address_space *mapping =3D inode->i_mapping; loff_t blk_start, blk_end; struct f2fs_sb_info *sbi =3D F2FS_SB(inode->i_sb); + int ilock; =20 f2fs_balance_fs(sbi); =20 @@ -441,7 +438,10 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len, int mode) blk_end =3D pg_end << PAGE_CACHE_SHIFT; truncate_inode_pages_range(mapping, blk_start, blk_end - 1); + + ilock =3D mutex_lock_op(sbi); ret =3D truncate_hole(inode, pg_start, pg_end); + mutex_unlock_op(sbi, ilock); } } =20 @@ -475,13 +475,13 @@ static int expand_inode_data(struct inode *inode, loff_t offset, =20 for (index =3D pg_start; index <=3D pg_end; index++) { struct dnode_of_data dn; + int ilock; =20 - mutex_lock_op(sbi, DATA_NEW); - + ilock =3D mutex_lock_op(sbi); set_new_dnode(&dn, inode, NULL, NULL, 0); ret =3D get_dnode_of_data(&dn, index, ALLOC_NODE); if (ret) { - mutex_unlock_op(sbi, DATA_NEW); + mutex_unlock_op(sbi, ilock); break; } =20 @@ -489,13 +489,12 @@ static int expand_inode_data(struct inode *inode, loff_t offset, ret =3D reserve_new_block(&dn); if (ret) { f2fs_put_dnode(&dn); - mutex_unlock_op(sbi, DATA_NEW); + mutex_unlock_op(sbi, ilock); break; } } f2fs_put_dnode(&dn); - - mutex_unlock_op(sbi, DATA_NEW); + mutex_unlock_op(sbi, ilock); =20 if (pg_start =3D=3D pg_end) new_size =3D offset + len; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index e97f301..83cec8f 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -510,7 +510,6 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type) wait_on_page_writeback(page); } =20 - mutex_lock_op(sbi, DATA_WRITE); if (clear_page_dirty_for_io(page) && S_ISDIR(inode->i_mode)) { dec_page_count(sbi, F2FS_DIRTY_DENTS); @@ -518,7 +517,6 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type) } set_cold_data(page); do_write_data_page(page); - mutex_unlock_op(sbi, DATA_WRITE); clear_cold_data(page); } out: diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index f798ddf..60105b7 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -195,46 +195,49 @@ void update_inode(struct inode *inode, struct page *node_page) set_page_dirty(node_page); } =20 -int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) +int update_inode_page(struct inode *inode) { struct f2fs_sb_info *sbi =3D F2FS_SB(inode->i_sb); struct page *node_page; - bool need_lock =3D false; - - if (inode->i_ino =3D=3D F2FS_NODE_INO(sbi) || - inode->i_ino =3D=3D F2FS_META_INO(sbi)) - return 0; - - if (wbc) - f2fs_balance_fs(sbi); =20 node_page =3D get_node_page(sbi, inode->i_ino); if (IS_ERR(node_page)) return PTR_ERR(node_page); =20 - if (!PageDirty(node_page)) { - need_lock =3D true; - f2fs_put_page(node_page, 1); - mutex_lock(&sbi->write_inode); - node_page =3D get_node_page(sbi, inode->i_ino); - if (IS_ERR(node_page)) { - mutex_unlock(&sbi->write_inode); - return PTR_ERR(node_page); - } - } update_inode(inode, node_page); f2fs_put_page(node_page, 1); - if (need_lock) - mutex_unlock(&sbi->write_inode); return 0; } =20 +int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + struct f2fs_sb_info *sbi =3D F2FS_SB(inode->i_sb); + int ret, ilock; + + if (inode->i_ino =3D=3D F2FS_NODE_INO(sbi) || + inode->i_ino =3D=3D F2FS_META_INO(sbi)) + return 0; + + if (wbc) + f2fs_balance_fs(sbi); + + /* + * We need to lock here to prevent from producing dirty node pages + * during the urgent cleaning time when runing out of free sections. + */ + ilock =3D mutex_lock_op(sbi); + ret =3D update_inode_page(inode); + mutex_unlock_op(sbi, ilock); + return ret; +} + /* * Called at the last iput() if i_nlink is zero */ void f2fs_evict_inode(struct inode *inode) { struct f2fs_sb_info *sbi =3D F2FS_SB(inode->i_sb); + int ilock; =20 truncate_inode_pages(&inode->i_data, 0); =20 @@ -255,7 +258,10 @@ void f2fs_evict_inode(struct inode *inode) if (F2FS_HAS_BLOCKS(inode)) f2fs_truncate(inode); =20 + ilock =3D mutex_lock_op(sbi); remove_inode_page(inode); + mutex_unlock_op(sbi, ilock); + sb_end_intwrite(inode->i_sb); no_delete: clear_inode(inode); diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 7c6e219..841f6b4 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -26,19 +26,19 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) nid_t ino; struct inode *inode; bool nid_free =3D false; - int err; + int err, ilock; =20 inode =3D new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); =20 - mutex_lock_op(sbi, NODE_NEW); + ilock =3D mutex_lock_op(sbi); if (!alloc_nid(sbi, &ino)) { - mutex_unlock_op(sbi, NODE_NEW); + mutex_unlock_op(sbi, ilock); err =3D -ENOSPC; goto fail; } - mutex_unlock_op(sbi, NODE_NEW); + mutex_unlock_op(sbi, ilock); =20 inode->i_uid =3D current_fsuid(); =20 @@ -122,7 +122,7 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, struct f2fs_sb_info *sbi =3D F2FS_SB(sb); struct inode *inode; nid_t ino =3D 0; - int err; + int err, ilock; =20 f2fs_balance_fs(sbi); =20 @@ -138,7 +138,9 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, inode->i_mapping->a_ops =3D &f2fs_dblock_aops; ino =3D inode->i_ino; =20 + ilock =3D mutex_lock_op(sbi); err =3D f2fs_add_link(dentry, inode); + mutex_unlock_op(sbi, ilock); if (err) goto out; =20 @@ -162,7 +164,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, struct inode *inode =3D old_dentry->d_inode; struct super_block *sb =3D dir->i_sb; struct f2fs_sb_info *sbi =3D F2FS_SB(sb); - int err; + int err, ilock; =20 f2fs_balance_fs(sbi); =20 @@ -170,7 +172,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir, atomic_inc(&inode->i_count); =20 set_inode_flag(F2FS_I(inode), FI_INC_LINK); + ilock =3D mutex_lock_op(sbi); err =3D f2fs_add_link(dentry, inode); + mutex_unlock_op(sbi, ilock); if (err) goto out; =20 @@ -229,6 +233,7 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) struct f2fs_dir_entry *de; struct page *page; int err =3D -ENOENT; + int ilock; =20 f2fs_balance_fs(sbi); =20 @@ -243,7 +248,9 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) goto fail; } =20 + ilock =3D mutex_lock_op(sbi); f2fs_delete_entry(de, page, inode); + mutex_unlock_op(sbi, ilock); =20 /* In order to evict this inode, we set it dirty */ mark_inode_dirty(inode); @@ -258,7 +265,7 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, struct f2fs_sb_info *sbi =3D F2FS_SB(sb); struct inode *inode; size_t symlen =3D strlen(symname) + 1; - int err; + int err, ilock; =20 f2fs_balance_fs(sbi); =20 @@ -269,7 +276,9 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry, inode->i_op =3D &f2fs_symlink_inode_operations; inode->i_mapping->a_ops =3D &f2fs_dblock_aops; =20 + ilock =3D mutex_lock_op(sbi); err =3D f2fs_add_link(dentry, inode); + mutex_unlock_op(sbi, ilock); if (err) goto out; =20 @@ -291,7 +300,7 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { struct f2fs_sb_info *sbi =3D F2FS_SB(dir->i_sb); struct inode *inode; - int err; + int err, ilock; =20 f2fs_balance_fs(sbi); =20 @@ -305,7 +314,9 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); =20 set_inode_flag(F2FS_I(inode), FI_INC_LINK); + ilock =3D mutex_lock_op(sbi); err =3D f2fs_add_link(dentry, inode); + mutex_unlock_op(sbi, ilock); if (err) goto out_fail; =20 @@ -340,6 +351,7 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, struct f2fs_sb_info *sbi =3D F2FS_SB(sb); struct inode *inode; int err =3D 0; + int ilock; =20 if (!new_valid_dev(rdev)) return -EINVAL; @@ -353,7 +365,9 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry, init_special_inode(inode, inode->i_mode, rdev); inode->i_op =3D &f2fs_special_inode_operations; =20 + ilock =3D mutex_lock_op(sbi); err =3D f2fs_add_link(dentry, inode); + mutex_unlock_op(sbi, ilock); if (err) goto out; =20 @@ -381,7 +395,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, struct f2fs_dir_entry *old_dir_entry =3D NULL; struct f2fs_dir_entry *old_entry; struct f2fs_dir_entry *new_entry; - int err =3D -ENOENT; + int err =3D -ENOENT, ilock =3D -1; =20 f2fs_balance_fs(sbi); =20 @@ -396,7 +410,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out_old; } =20 - mutex_lock_op(sbi, RENAME); + ilock =3D mutex_lock_op(sbi); =20 if (new_inode) { struct page *new_page; @@ -419,7 +433,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, drop_nlink(new_inode); if (!new_inode->i_nlink) add_orphan_inode(sbi, new_inode->i_ino); - f2fs_write_inode(new_inode, NULL); + update_inode_page(new_inode); } else { err =3D f2fs_add_link(new_dentry, old_inode); if (err) @@ -427,7 +441,7 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, =20 if (old_dir_entry) { inc_nlink(new_dir); - f2fs_write_inode(new_dir, NULL); + update_inode_page(new_dir); } } =20 @@ -445,10 +459,10 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, f2fs_put_page(old_dir_page, 0); } drop_nlink(old_dir); - f2fs_write_inode(old_dir, NULL); + update_inode_page(old_dir); } =20 - mutex_unlock_op(sbi, RENAME); + mutex_unlock_op(sbi, ilock); return 0; =20 out_dir: @@ -456,7 +470,7 @@ out_dir: kunmap(old_dir_page); f2fs_put_page(old_dir_page, 0); } - mutex_unlock_op(sbi, RENAME); + mutex_unlock_op(sbi, ilock); out_old: kunmap(old_page); f2fs_put_page(old_page, 0); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index ad3adbe..5a7edf9 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -385,6 +385,9 @@ got: =20 /* * Caller should call f2fs_put_dnode(dn). + * Also, it should grab and release a mutex by calling mutex_lock_op() and + * mutex_unlock_op() only if ro is not set RDONLY_NODE. + * In the case of RDONLY_NODE, we don't need to care about mutex. */ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) { @@ -415,11 +418,8 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) bool done =3D false; =20 if (!nids[i] && mode =3D=3D ALLOC_NODE) { - mutex_lock_op(sbi, NODE_NEW); - /* alloc new node */ if (!alloc_nid(sbi, &(nids[i]))) { - mutex_unlock_op(sbi, NODE_NEW); err =3D -ENOSPC; goto release_pages; } @@ -428,14 +428,12 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) npage[i] =3D new_node_page(dn, noffset[i]); if (IS_ERR(npage[i])) { alloc_nid_failed(sbi, nids[i]); - mutex_unlock_op(sbi, NODE_NEW); err =3D PTR_ERR(npage[i]); goto release_pages; } =20 set_nid(parent, offset[i - 1], nids[i], i =3D=3D 1); alloc_nid_done(sbi, nids[i]); - mutex_unlock_op(sbi, NODE_NEW); done =3D true; } else if (mode =3D=3D LOOKUP_NODE_RA && i =3D=3D level && level > 1) { npage[i] =3D get_node_page_ra(parent, offset[i - 1]); @@ -745,6 +743,10 @@ fail: return err > 0 ? 0 : err; } =20 +/* + * Caller should grab and release a mutex by calling mutex_lock_op() and + * mutex_unlock_op(). + */ int remove_inode_page(struct inode *inode) { struct f2fs_sb_info *sbi =3D F2FS_SB(inode->i_sb); @@ -752,21 +754,16 @@ int remove_inode_page(struct inode *inode) nid_t ino =3D inode->i_ino; struct dnode_of_data dn; =20 - mutex_lock_op(sbi, NODE_TRUNC); page =3D get_node_page(sbi, ino); - if (IS_ERR(page)) { - mutex_unlock_op(sbi, NODE_TRUNC); + if (IS_ERR(page)) return PTR_ERR(page); - } =20 if (F2FS_I(inode)->i_xattr_nid) { nid_t nid =3D F2FS_I(inode)->i_xattr_nid; struct page *npage =3D get_node_page(sbi, nid); =20 - if (IS_ERR(npage)) { - mutex_unlock_op(sbi, NODE_TRUNC); + if (IS_ERR(npage)) return PTR_ERR(npage); - } =20 F2FS_I(inode)->i_xattr_nid =3D 0; set_new_dnode(&dn, inode, page, npage, nid); @@ -778,23 +775,18 @@ int remove_inode_page(struct inode *inode) BUG_ON(inode->i_blocks !=3D 0 && inode->i_blocks !=3D 1); set_new_dnode(&dn, inode, page, page, ino); truncate_node(&dn); - - mutex_unlock_op(sbi, NODE_TRUNC); return 0; } =20 int new_inode_page(struct inode *inode, const struct qstr *name) { - struct f2fs_sb_info *sbi =3D F2FS_SB(inode->i_sb); struct page *page; struct dnode_of_data dn; =20 /* allocate inode page for new inode */ set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); - mutex_lock_op(sbi, NODE_NEW); page =3D new_node_page(&dn, 0); init_dent_inode(name, page); - mutex_unlock_op(sbi, NODE_NEW); if (IS_ERR(page)) return PTR_ERR(page); f2fs_put_page(page, 1); @@ -985,7 +977,7 @@ void sync_inode_page(struct dnode_of_data *dn) if (!dn->inode_page_locked) unlock_page(dn->inode_page); } else { - f2fs_write_inode(dn->inode, NULL); + update_inode_page(dn->inode); } } =20 @@ -1102,8 +1094,6 @@ static int f2fs_write_node_page(struct page *page, =20 wait_on_page_writeback(page); =20 - mutex_lock_op(sbi, NODE_WRITE); - /* get old block addr of this node page */ nid =3D nid_of_node(page); BUG_ON(page->index !=3D nid); @@ -1111,25 +1101,25 @@ static int f2fs_write_node_page(struct page *page, get_node_info(sbi, nid, &ni); =20 /* This page is already truncated */ - if (ni.blk_addr =3D=3D NULL_ADDR) - goto out; + if (ni.blk_addr =3D=3D NULL_ADDR) { + dec_page_count(sbi, F2FS_DIRTY_NODES); + unlock_page(page); + return 0; + } =20 if (wbc->for_reclaim) { dec_page_count(sbi, F2FS_DIRTY_NODES); wbc->pages_skipped++; set_page_dirty(page); - mutex_unlock_op(sbi, NODE_WRITE); return AOP_WRITEPAGE_ACTIVATE; } =20 + mutex_lock(&sbi->node_write); set_page_writeback(page); - - /* insert node offset */ write_node_page(sbi, page, nid, ni.blk_addr, &new_addr); set_node_addr(sbi, &ni, new_addr); -out: dec_page_count(sbi, F2FS_DIRTY_NODES); - mutex_unlock_op(sbi, NODE_WRITE); + mutex_unlock(&sbi->node_write); unlock_page(page); return 0; } diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 61bdaa7..f16d12d 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -242,6 +242,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, struct f2fs_summary sum; struct node_info ni; int err =3D 0; + int ilock; =20 start =3D start_bidx_of_node(ofs_of_node(page)); if (IS_INODE(page)) @@ -249,10 +250,14 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, else end =3D start + ADDRS_PER_BLOCK; =20 + ilock =3D mutex_lock_op(sbi); set_new_dnode(&dn, inode, NULL, NULL, 0); + err =3D get_dnode_of_data(&dn, start, ALLOC_NODE); - if (err) + if (err) { + mutex_unlock_op(sbi, ilock); return err; + } =20 wait_on_page_writeback(dn.node_page); =20 @@ -297,6 +302,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, =20 recover_node_page(sbi, dn.node_page, &sum, &ni, blkaddr); f2fs_put_dnode(&dn); + mutex_unlock_op(sbi, ilock); return 0; } =20 diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ca54133..2643c49 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -556,11 +556,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) sbi->raw_super =3D raw_super; sbi->raw_super_buf =3D raw_super_buf; mutex_init(&sbi->gc_mutex); - mutex_init(&sbi->write_inode); mutex_init(&sbi->writepages); mutex_init(&sbi->cp_mutex); - for (i =3D 0; i < NR_LOCK_TYPE; i++) + for (i =3D 0; i < NR_GLOBAL_LOCKS; i++) mutex_init(&sbi->fs_lock[i]); + mutex_init(&sbi->node_write); sbi->por_doing =3D 0; spin_lock_init(&sbi->stat_lock); init_rwsem(&sbi->bio_sem); diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 3bfea80..0b02dce 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -307,6 +307,7 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, int error, found, free, newsize; size_t name_len; char *pval; + int ilock; =20 if (name =3D=3D NULL) return -EINVAL; @@ -321,7 +322,8 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, =20 f2fs_balance_fs(sbi); =20 - mutex_lock_op(sbi, NODE_NEW); + ilock =3D mutex_lock_op(sbi); + if (!fi->i_xattr_nid) { /* Allocate new attribute block */ struct dnode_of_data dn; @@ -433,13 +435,13 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, inode->i_ctime =3D CURRENT_TIME; clear_inode_flag(fi, FI_ACL_MODE); } - f2fs_write_inode(inode, NULL); - mutex_unlock_op(sbi, NODE_NEW); + update_inode_page(inode); + mutex_unlock_op(sbi, ilock); =20 return 0; cleanup: f2fs_put_page(page, 1); exit: - mutex_unlock_op(sbi, NODE_NEW); + mutex_unlock_op(sbi, ilock); return error; } --=20 1.8.1.3.566.gaa39828 --=20 Jaegeuk Kim Samsung --=-m1FxC3NOzLU6cgfqMI9/ Content-Type: application/pgp-signature; name="signature.asc" Content-Description: This is a digitally signed message part Content-Transfer-Encoding: 7bit -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJRY9iNAAoJEEAUqH6CSFDSZrgP/3KJKoRV4VmxdCdNe1COvpLw 2Vzwu6yeJ5cqIF//F/uxQUMt1UbBR+JCF0QnuY8BUpwp6sGZRgzgQ3PsIrGmID2o vlUhuYINaVjDxRJqT/oeUuriCYLU+f4O8lFqi4E5ZPIhgM8BQ5o7nKY6lWgfnZ3M lELIBu/Lv4pdnQDcZ+6j81G+mLdRC/FrlhGQY0Sp/elhaGNX/grXA+pq41FfWpt6 5qddrS9JBtEnRNo3t9O34Cc5DdkgAeiEDIhvOsVsx3fg1rLrJZtPsiji/52lMpow f5tPAfPLDKpe/L3tRR26rm7Tvqkw0pCe1phJs227KVEFSjcxPi8VBPn72Hs31eKN P5Y+eQ2JbKTVJTwdoIgg2m92vGc0CyMq0RJXmUIZmsizbnsrZc+NaHwHbTP5d70c 39yfvErUIGTBfcJlZ4wrxvl9T2744VxLYur/d21999iAyD/e/0uvQmR1yd0zUwx3 wD866DHdns1PauXTLMK3nJJJ4ejAd6p9+uzChDJGwenwIwd/udUbvAuBcFXrWFiO CjFFKMCh/DQLIwN6ZxoXl/JqKFV8l60e1W54XUqj9hXxUp01HKEgnkrj1uyDcw78 TXBWcZYvLFLvLSNlU6jymv7Edrc+xSEf9DWDDAf61Mq5ePsZQulaiFGKre7YDz7v ySkm6h6OFSeFEtGGb5eU =5eWV -----END PGP SIGNATURE----- --=-m1FxC3NOzLU6cgfqMI9/-- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/