From 0378da0fda6edf5aaffda6f1248a78986bd955b5 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Thu, 12 Aug 2010 10:25:28 +0800 Subject: ocfs2: pass struct file* to ocfs2_write_begin_nolock. struct file * has file_ra_state to store the readahead state and data. So pass this to ocfs2_write_begin_nolock so that it can be used in ocfs2_refcount_cow. Signed-off-by: Tao Ma --- fs/ocfs2/aops.c | 5 +++-- fs/ocfs2/aops.h | 3 ++- fs/ocfs2/mmap.c | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 0de69c9a08b..e3efdd93773 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -1642,7 +1642,8 @@ static int ocfs2_zero_tail(struct inode *inode, struct buffer_head *di_bh, return ret; } -int ocfs2_write_begin_nolock(struct address_space *mapping, +int ocfs2_write_begin_nolock(struct file *filp, + struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata, struct buffer_head *di_bh, struct page *mmap_page) @@ -1854,7 +1855,7 @@ static int ocfs2_write_begin(struct file *file, struct address_space *mapping, */ down_write(&OCFS2_I(inode)->ip_alloc_sem); - ret = ocfs2_write_begin_nolock(mapping, pos, len, flags, pagep, + ret = ocfs2_write_begin_nolock(file, mapping, pos, len, flags, pagep, fsdata, di_bh, NULL); if (ret) { mlog_errno(ret); diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h index c48e93ffc51..7606f663da6 100644 --- a/fs/ocfs2/aops.h +++ b/fs/ocfs2/aops.h @@ -48,7 +48,8 @@ int ocfs2_write_end_nolock(struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata); -int ocfs2_write_begin_nolock(struct address_space *mapping, +int ocfs2_write_begin_nolock(struct file *filp, + struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata, struct buffer_head *di_bh, struct page *mmap_page); diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index af2b8fe1f13..b04d6961c0d 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -59,10 +59,11 @@ static int ocfs2_fault(struct vm_area_struct *area, struct vm_fault *vmf) return ret; } -static int __ocfs2_page_mkwrite(struct inode *inode, struct buffer_head *di_bh, +static int __ocfs2_page_mkwrite(struct file *file, struct buffer_head *di_bh, struct page *page) { int ret; + struct inode *inode = file->f_path.dentry->d_inode; struct address_space *mapping = inode->i_mapping; loff_t pos = page_offset(page); unsigned int len = PAGE_CACHE_SIZE; @@ -109,7 +110,7 @@ static int __ocfs2_page_mkwrite(struct inode *inode, struct buffer_head *di_bh, if (page->index == last_index) len = size & ~PAGE_CACHE_MASK; - ret = ocfs2_write_begin_nolock(mapping, pos, len, 0, &locked_page, + ret = ocfs2_write_begin_nolock(file, mapping, pos, len, 0, &locked_page, &fsdata, di_bh, page); if (ret) { if (ret != -ENOSPC) @@ -157,7 +158,7 @@ static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) */ down_write(&OCFS2_I(inode)->ip_alloc_sem); - ret = __ocfs2_page_mkwrite(inode, di_bh, page); + ret = __ocfs2_page_mkwrite(vma->vm_file, di_bh, page); up_write(&OCFS2_I(inode)->ip_alloc_sem); -- cgit v1.2.3-18-g5258 From b890823635e022467b924e3d9da8c5166a4e349c Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Thu, 12 Aug 2010 10:27:14 +0800 Subject: ocfs2: pass struct file* to ocfs2_prepare_inode_for_write. struct file * has file_ra_state to store the readahead state and data. So pass this to ocfs2_prepare_inode_for_write. so that it can be used in ocfs2_refcount_cow. Signed-off-by: Tao Ma --- fs/ocfs2/file.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 81296b4e364..5920159a421 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2053,6 +2053,7 @@ out: } static int ocfs2_prepare_inode_for_refcount(struct inode *inode, + struct file *file, loff_t pos, size_t count, int *meta_level) { @@ -2078,7 +2079,7 @@ out: return ret; } -static int ocfs2_prepare_inode_for_write(struct dentry *dentry, +static int ocfs2_prepare_inode_for_write(struct file *file, loff_t *ppos, size_t count, int appending, @@ -2086,6 +2087,7 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry, int *has_refcount) { int ret = 0, meta_level = 0; + struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; loff_t saved_pos, end; @@ -2141,6 +2143,7 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry, meta_level = -1; ret = ocfs2_prepare_inode_for_refcount(inode, + file, saved_pos, count, &meta_level); @@ -2255,7 +2258,7 @@ relock: } can_do_direct = direct_io; - ret = ocfs2_prepare_inode_for_write(file->f_path.dentry, ppos, + ret = ocfs2_prepare_inode_for_write(file, ppos, iocb->ki_left, appending, &can_do_direct, &has_refcount); if (ret < 0) { @@ -2385,7 +2388,7 @@ static int ocfs2_splice_to_file(struct pipe_inode_info *pipe, { int ret; - ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, &sd->pos, + ret = ocfs2_prepare_inode_for_write(out, &sd->pos, sd->total_len, 0, NULL, NULL); if (ret < 0) { mlog_errno(ret); -- cgit v1.2.3-18-g5258 From 155027121fe52f9b4f25e9d156c22f2f5012e5fe Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Thu, 12 Aug 2010 10:36:38 +0800 Subject: ocfs2: Add struct file to ocfs2_refcount_cow. Add a new parameter 'struct file *' to ocfs2_refcount_cow so that we can add readahead support later. Signed-off-by: Tao Ma --- fs/ocfs2/aops.c | 2 +- fs/ocfs2/file.c | 8 ++++---- fs/ocfs2/refcounttree.c | 4 +++- fs/ocfs2/refcounttree.h | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index e3efdd93773..7155c5a919d 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -1693,7 +1693,7 @@ int ocfs2_write_begin_nolock(struct file *filp, mlog_errno(ret); goto out; } else if (ret == 1) { - ret = ocfs2_refcount_cow(inode, di_bh, + ret = ocfs2_refcount_cow(inode, filp, di_bh, wc->w_cpos, wc->w_clen, UINT_MAX); if (ret) { mlog_errno(ret); diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 5920159a421..4331f57e9fd 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -361,7 +361,7 @@ static int ocfs2_cow_file_pos(struct inode *inode, if (!(ext_flags & OCFS2_EXT_REFCOUNTED)) goto out; - return ocfs2_refcount_cow(inode, fe_bh, cpos, 1, cpos+1); + return ocfs2_refcount_cow(inode, NULL, fe_bh, cpos, 1, cpos+1); out: return status; @@ -904,8 +904,8 @@ static int ocfs2_zero_extend_get_range(struct inode *inode, zero_clusters = last_cpos - zero_cpos; if (needs_cow) { - rc = ocfs2_refcount_cow(inode, di_bh, zero_cpos, zero_clusters, - UINT_MAX); + rc = ocfs2_refcount_cow(inode, NULL, di_bh, zero_cpos, + zero_clusters, UINT_MAX); if (rc) { mlog_errno(rc); goto out; @@ -2071,7 +2071,7 @@ static int ocfs2_prepare_inode_for_refcount(struct inode *inode, *meta_level = 1; - ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX); + ret = ocfs2_refcount_cow(inode, file, di_bh, cpos, clusters, UINT_MAX); if (ret) mlog_errno(ret); out: diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 3ac5aa733e9..66dab3c0d49 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -3404,6 +3404,7 @@ static int ocfs2_replace_cow(struct ocfs2_cow_context *context) * unrefcounted extent. */ static int ocfs2_refcount_cow_hunk(struct inode *inode, + struct file *file, struct buffer_head *di_bh, u32 cpos, u32 write_len, u32 max_cpos) { @@ -3481,6 +3482,7 @@ out: * clusters between cpos and cpos+write_len are safe to modify. */ int ocfs2_refcount_cow(struct inode *inode, + struct file *file, struct buffer_head *di_bh, u32 cpos, u32 write_len, u32 max_cpos) { @@ -3500,7 +3502,7 @@ int ocfs2_refcount_cow(struct inode *inode, num_clusters = write_len; if (ext_flags & OCFS2_EXT_REFCOUNTED) { - ret = ocfs2_refcount_cow_hunk(inode, di_bh, cpos, + ret = ocfs2_refcount_cow_hunk(inode, file, di_bh, cpos, num_clusters, max_cpos); if (ret) { mlog_errno(ret); diff --git a/fs/ocfs2/refcounttree.h b/fs/ocfs2/refcounttree.h index 9983ba1570e..29cba0eaa92 100644 --- a/fs/ocfs2/refcounttree.h +++ b/fs/ocfs2/refcounttree.h @@ -52,7 +52,8 @@ int ocfs2_prepare_refcount_change_for_del(struct inode *inode, u32 clusters, int *credits, int *ref_blocks); -int ocfs2_refcount_cow(struct inode *inode, struct buffer_head *di_bh, +int ocfs2_refcount_cow(struct inode *inode, + struct file *filep, struct buffer_head *di_bh, u32 cpos, u32 write_len, u32 max_cpos); typedef int (ocfs2_post_refcount_func)(struct inode *inode, -- cgit v1.2.3-18-g5258 From 7b61cf54a2445ad21df9dd44f0c8bb90154ddea8 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Thu, 17 Jun 2010 14:14:36 +0800 Subject: ocfs2: Add readahead support for CoW. Add a new function ocfs2_readahead_for_cow so that we start readahead before we start our CoW. Signed-off-by: Tao Ma --- fs/ocfs2/refcounttree.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 66dab3c0d49..ad08c1134ba 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -3398,6 +3398,28 @@ static int ocfs2_replace_cow(struct ocfs2_cow_context *context) return ret; } +static void ocfs2_readahead_for_cow(struct inode *inode, + struct file *file, + u32 start, u32 len) +{ + struct address_space *mapping; + pgoff_t index; + unsigned long num_pages; + int cs_bits = OCFS2_SB(inode->i_sb)->s_clustersize_bits; + + if (!file) + return; + + mapping = file->f_mapping; + num_pages = (len << cs_bits) >> PAGE_CACHE_SHIFT; + if (!num_pages) + num_pages = 1; + + index = ((loff_t)start << cs_bits) >> PAGE_CACHE_SHIFT; + page_cache_sync_readahead(mapping, &file->f_ra, file, + index, num_pages); +} + /* * Starting at cpos, try to CoW write_len clusters. Don't CoW * past max_cpos. This will stop when it runs into a hole or an @@ -3433,6 +3455,8 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode, BUG_ON(cow_len == 0); + ocfs2_readahead_for_cow(inode, file, cow_start, cow_len); + context = kzalloc(sizeof(struct ocfs2_cow_context), GFP_NOFS); if (!context) { ret = -ENOMEM; -- cgit v1.2.3-18-g5258 From 6ea4843f53282465f2bdbe5eedde7d8c3081dfdf Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Thu, 12 Aug 2010 10:31:34 +0800 Subject: ocfs2: Add readhead during CoW. In CoW, when we meet with a readahead page, we know it is time to move the readahead window. So carry out a new readahead. Signed-off-by: Tao Ma --- fs/ocfs2/refcounttree.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index ad08c1134ba..47549f64224 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -49,6 +49,7 @@ struct ocfs2_cow_context { struct inode *inode; + struct file *file; u32 cow_start; u32 cow_len; struct ocfs2_extent_tree data_et; @@ -2922,13 +2923,16 @@ static int ocfs2_duplicate_clusters_by_page(handle_t *handle, u64 new_block = ocfs2_clusters_to_blocks(sb, new_cluster); struct page *page; pgoff_t page_index; - unsigned int from, to; + unsigned int from, to, readahead_pages; loff_t offset, end, map_end; struct address_space *mapping = context->inode->i_mapping; mlog(0, "old_cluster %u, new %u, len %u at offset %u\n", old_cluster, new_cluster, new_len, cpos); + readahead_pages = + (ocfs2_cow_contig_clusters(sb) << + OCFS2_SB(sb)->s_clustersize_bits) >> PAGE_CACHE_SHIFT; offset = ((loff_t)cpos) << OCFS2_SB(sb)->s_clustersize_bits; end = offset + (new_len << OCFS2_SB(sb)->s_clustersize_bits); /* @@ -2959,6 +2963,14 @@ static int ocfs2_duplicate_clusters_by_page(handle_t *handle, if (PAGE_CACHE_SIZE <= OCFS2_SB(sb)->s_clustersize) BUG_ON(PageDirty(page)); + if (PageReadahead(page) && context->file) { + page_cache_async_readahead(mapping, + &context->file->f_ra, + context->file, + page, page_index, + readahead_pages); + } + if (!PageUptodate(page)) { ret = block_read_full_page(page, ocfs2_get_block); if (ret) { @@ -3478,6 +3490,7 @@ static int ocfs2_refcount_cow_hunk(struct inode *inode, context->ref_root_bh = ref_root_bh; context->cow_duplicate_clusters = ocfs2_duplicate_clusters_by_page; context->get_clusters = ocfs2_di_get_clusters; + context->file = file; ocfs2_init_dinode_extent_tree(&context->data_et, INODE_CACHE(inode), di_bh); -- cgit v1.2.3-18-g5258 From 5ffef88ffeb730e1bf2da56a39a55e03d57a66c9 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 4 Aug 2010 10:14:47 +0300 Subject: UBIFS: switch to RO mode after synchronizing In 'ubifs_garbage_collect()' on error path, we first switch to R/O mode, and then synchronize write-buffers (to make sure no data are lost). But the GC write-buffer synchronization will fail, because we are already in R/O mode. This patch re-orders this and makes sure we first synchronize the write-buffer, and then switch to R/O mode. Signed-off-by: Artem Bityutskiy --- fs/ubifs/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index 918d1582ca0..f89a422ca39 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -774,8 +774,8 @@ out_unlock: out: ubifs_assert(ret < 0); ubifs_assert(ret != -ENOSPC && ret != -EAGAIN); - ubifs_ro_mode(c, ret); ubifs_wbuf_sync_nolock(wbuf); + ubifs_ro_mode(c, ret); mutex_unlock(&wbuf->io_mutex); ubifs_return_leb(c, lp.lnum); return ret; -- cgit v1.2.3-18-g5258 From efe1881f5482f94f5e5e6cb74bf3ea72f2b5b9ce Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 4 Aug 2010 14:06:06 +0300 Subject: UBIFS: do not treat ENOSPC specially 'ubifs_garbage_collect_leb()' should never return '-ENOSPC', and if it does, this is an error. Thus, do not treat this error code specially. '-EAGAIN' is a special error code, but not '-ENOSPC'. Signed-off-by: Artem Bityutskiy --- fs/ubifs/gc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index f89a422ca39..ee4b05de4d4 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -677,14 +677,12 @@ int ubifs_garbage_collect(struct ubifs_info *c, int anyway) ret = ubifs_garbage_collect_leb(c, &lp); if (ret < 0) { - if (ret == -EAGAIN || ret == -ENOSPC) { + if (ret == -EAGAIN) { /* - * These codes are not errors, so we have to - * return the LEB to lprops. But if the - * 'ubifs_return_leb()' function fails, its - * failure code is propagated to the caller - * instead of the original '-EAGAIN' or - * '-ENOSPC'. + * This is not error, so we have to return the + * LEB to lprops. But if 'ubifs_return_leb()' + * fails, its failure code is propagated to the + * caller instead of the original '-EAGAIN'. */ err = ubifs_return_leb(c, lp.lnum); if (err) -- cgit v1.2.3-18-g5258 From e3408ad4cbed6ec6990efad4c2ef0856bcd3c712 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 4 Aug 2010 13:50:51 +0300 Subject: UBIFS: fix assertion warning This patch fixes the following false assertion warning: UBIFS assert failed in data_nodes_cmp at 130 (pid 15107) The assertion was wrong because it did not take into account that the node can be an xentry. Signed-off-by: Artem Bityutskiy --- fs/ubifs/gc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index ee4b05de4d4..98b2c3c8725 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -178,7 +178,8 @@ int nondata_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) if (typeb == UBIFS_INO_KEY) return 1; - ubifs_assert(typea == UBIFS_DENT_KEY && typeb == UBIFS_DENT_KEY); + ubifs_assert(typea == UBIFS_DENT_KEY || typea == UBIFS_XENT_KEY); + ubifs_assert(typeb == UBIFS_DENT_KEY || typeb == UBIFS_XENT_KEY); inuma = key_inum(c, &sa->key); inumb = key_inum(c, &sb->key); -- cgit v1.2.3-18-g5258 From 44ec83b8bd05d323998031f141c310127721acae Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 7 Aug 2010 08:44:13 +0300 Subject: UBIFS: do not look up truncation nodes When moving nodes in GC, do not try to look up truncation nodes in TNC, because they do not exist there. This would be harmless, because the TNC look-up would fail, if we did not have bug 'ubifs_add_snod()' which reads garbage into 'snod->key'. But in any case, it is less error prone to explicitly ignore everything but inode, data, dentry and xentry nodes. Signed-off-by: Artem Bityutskiy --- fs/ubifs/gc.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index 98b2c3c8725..9dbbc5c8894 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -233,9 +233,26 @@ static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb, list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) { int err; - ubifs_assert(snod->type != UBIFS_IDX_NODE); - ubifs_assert(snod->type != UBIFS_REF_NODE); - ubifs_assert(snod->type != UBIFS_CS_NODE); + ubifs_assert(snod->type == UBIFS_INO_NODE || + snod->type == UBIFS_DATA_NODE || + snod->type == UBIFS_DENT_NODE || + snod->type == UBIFS_XENT_NODE || + snod->type == UBIFS_TRUN_NODE); + + if (snod->type != UBIFS_INO_NODE && + snod->type != UBIFS_DATA_NODE && + snod->type != UBIFS_DENT_NODE && + snod->type != UBIFS_XENT_NODE) { + /* Probably truncation node, zap it */ + list_del(&snod->list); + kfree(snod); + continue; + } + + ubifs_assert(key_type(c, &snod->key) == UBIFS_DATA_KEY || + key_type(c, &snod->key) == UBIFS_INO_KEY || + key_type(c, &snod->key) == UBIFS_DENT_KEY || + key_type(c, &snod->key) == UBIFS_XENT_KEY); err = ubifs_tnc_has_node(c, &snod->key, 0, sleb->lnum, snod->offs, 0); -- cgit v1.2.3-18-g5258 From ab87118d717467cbcd9648692c2a9708d55193bc Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 8 Aug 2010 12:25:33 +0300 Subject: UBIFS: do not use key type in list_sort In comparison function for 'list_sort()' we use key type to distinguish between node types. However, we have a bit simper way to detect node type - 'snod->type'. This more logical to use, comparing to decoding key types. Also allows to get rid of 2 local variables. Signed-off-by: Artem Bityutskiy --- fs/ubifs/gc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index 9dbbc5c8894..27815bb9112 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -157,7 +157,6 @@ int data_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) */ int nondata_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) { - int typea, typeb; ino_t inuma, inumb; struct ubifs_info *c = priv; struct ubifs_scan_node *sa, *sb; @@ -165,21 +164,22 @@ int nondata_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) cond_resched(); sa = list_entry(a, struct ubifs_scan_node, list); sb = list_entry(b, struct ubifs_scan_node, list); - typea = key_type(c, &sa->key); - typeb = key_type(c, &sb->key); - ubifs_assert(typea != UBIFS_DATA_KEY && typeb != UBIFS_DATA_KEY); + ubifs_assert(sa->type != UBIFS_DATA_NODE && + sb->type != UBIFS_DATA_NODE); /* Inodes go before directory entries */ - if (typea == UBIFS_INO_KEY) { - if (typeb == UBIFS_INO_KEY) + if (sa->type == UBIFS_INO_NODE) { + if (sb->type == UBIFS_INO_NODE) return sb->len - sa->len; return -1; } - if (typeb == UBIFS_INO_KEY) + if (sb->type == UBIFS_INO_NODE) return 1; - ubifs_assert(typea == UBIFS_DENT_KEY || typea == UBIFS_XENT_KEY); - ubifs_assert(typeb == UBIFS_DENT_KEY || typeb == UBIFS_XENT_KEY); + ubifs_assert(sa->type == UBIFS_DENT_NODE || + sa->type == UBIFS_XENT_NODE); + ubifs_assert(sb->type == UBIFS_DENT_NODE || + sb->type == UBIFS_XENT_NODE); inuma = key_inum(c, &sa->key); inumb = key_inum(c, &sb->key); -- cgit v1.2.3-18-g5258 From 66576833f5396af34c52160b16d7b8573199282a Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 8 Aug 2010 12:29:58 +0300 Subject: UBIFS: improve assertion in node comparison functions Improve assertions in gc.c in the comparison functions for 'list_sort()': check key types _and_ node types. Signed-off-by: Artem Bityutskiy --- fs/ubifs/gc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'fs') diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index 27815bb9112..dafef3d195d 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -127,8 +127,11 @@ int data_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) cond_resched(); sa = list_entry(a, struct ubifs_scan_node, list); sb = list_entry(b, struct ubifs_scan_node, list); + ubifs_assert(key_type(c, &sa->key) == UBIFS_DATA_KEY); ubifs_assert(key_type(c, &sb->key) == UBIFS_DATA_KEY); + ubifs_assert(sa->type == UBIFS_DATA_NODE); + ubifs_assert(sb->type == UBIFS_DATA_NODE); inuma = key_inum(c, &sa->key); inumb = key_inum(c, &sb->key); @@ -164,6 +167,9 @@ int nondata_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) cond_resched(); sa = list_entry(a, struct ubifs_scan_node, list); sb = list_entry(b, struct ubifs_scan_node, list); + + ubifs_assert(key_type(c, &sa->key) != UBIFS_DATA_KEY && + key_type(c, &sb->key) != UBIFS_DATA_KEY); ubifs_assert(sa->type != UBIFS_DATA_NODE && sb->type != UBIFS_DATA_NODE); @@ -176,10 +182,15 @@ int nondata_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) if (sb->type == UBIFS_INO_NODE) return 1; + ubifs_assert(key_type(c, &sa->key) == UBIFS_DENT_KEY || + key_type(c, &sa->key) == UBIFS_XENT_KEY); + ubifs_assert(key_type(c, &sb->key) == UBIFS_DENT_KEY || + key_type(c, &sb->key) == UBIFS_XENT_KEY); ubifs_assert(sa->type == UBIFS_DENT_NODE || sa->type == UBIFS_XENT_NODE); ubifs_assert(sb->type == UBIFS_DENT_NODE || sb->type == UBIFS_XENT_NODE); + inuma = key_inum(c, &sa->key); inumb = key_inum(c, &sb->key); -- cgit v1.2.3-18-g5258 From 5b7a3a2e1b0cbc7d5410a8da60dac266a3e19268 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 8 Aug 2010 12:32:00 +0300 Subject: UBIFS: do not write rubbish into truncation scanning node In the scanning code, in 'ubifs_add_snod()', we write rubbish into 'snod->key', because we assume that on-flash truncation nodes have a key, but they do not. If the other parts of UBIFS then mistakenly try to look-up the truncation node key (they should not do this, but may do because of a bug), we can succeed and corrupt TNC. It looks like we did have such a situation in 'sort_nodes()' in gc.c. Signed-off-by: Artem Bityutskiy --- fs/ubifs/scan.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/ubifs/scan.c b/fs/ubifs/scan.c index 96c52538419..a0a305ca61a 100644 --- a/fs/ubifs/scan.c +++ b/fs/ubifs/scan.c @@ -212,7 +212,6 @@ int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, case UBIFS_DENT_NODE: case UBIFS_XENT_NODE: case UBIFS_DATA_NODE: - case UBIFS_TRUN_NODE: /* * The key is in the same place in all keyed * nodes. -- cgit v1.2.3-18-g5258 From ba2f48f70efcf4d82deafb2be327ed64b1f043a5 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 22 Aug 2010 07:10:12 +0300 Subject: UBIFS: mark unused key objects as invalid When scanning the flash, UBIFS builds a list of flash nodes of type 'struct ubifs_scan_node'. Each scanned node has a 'snod->key' field. This field is valid for most of the nodes, but invalid for some node type, e.g., truncation nodes. It is safer to explicitly initialize such keys to something invalid, rather than leaving them initialized to all zeros, which has key type of UBIFS_INO_KEY. This patch introduces new "fake" key type UBIFS_INVALID_KEY and initializes unused 'snod->key' objects to this type. It also adds debugging assertions in the TNC code to make sure no one ever tries to look these nodes up in the TNC. Signed-off-by: Artem Bityutskiy --- fs/ubifs/key.h | 14 ++++++++++++++ fs/ubifs/scan.c | 5 ++++- fs/ubifs/tnc.c | 5 ++++- fs/ubifs/ubifs.h | 6 +++++- 4 files changed, 27 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h index 0f530c684f0..92a8491a8f8 100644 --- a/fs/ubifs/key.h +++ b/fs/ubifs/key.h @@ -305,6 +305,20 @@ static inline void trun_key_init(const struct ubifs_info *c, key->u32[1] = UBIFS_TRUN_KEY << UBIFS_S_KEY_BLOCK_BITS; } +/** + * invalid_key_init - initialize invalid node key. + * @c: UBIFS file-system description object + * @key: key to initialize + * + * This is a helper function which marks a @key object as invalid. + */ +static inline void invalid_key_init(const struct ubifs_info *c, + union ubifs_key *key) +{ + key->u32[0] = 0xDEADBEAF; + key->u32[1] = UBIFS_INVALID_KEY; +} + /** * key_type - get key type. * @c: UBIFS file-system description object diff --git a/fs/ubifs/scan.c b/fs/ubifs/scan.c index a0a305ca61a..3e1ee57dbea 100644 --- a/fs/ubifs/scan.c +++ b/fs/ubifs/scan.c @@ -197,7 +197,7 @@ int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, struct ubifs_ino_node *ino = buf; struct ubifs_scan_node *snod; - snod = kzalloc(sizeof(struct ubifs_scan_node), GFP_NOFS); + snod = kmalloc(sizeof(struct ubifs_scan_node), GFP_NOFS); if (!snod) return -ENOMEM; @@ -218,6 +218,9 @@ int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, */ key_read(c, &ino->key, &snod->key); break; + default: + invalid_key_init(c, &snod->key); + break; } list_add_tail(&snod->list, &sleb->nodes); sleb->nodes_cnt += 1; diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index 2194915220e..ad9cf013362 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -1177,6 +1177,7 @@ int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key, unsigned long time = get_seconds(); dbg_tnc("search key %s", DBGKEY(key)); + ubifs_assert(key_type(c, key) < UBIFS_INVALID_KEY); znode = c->zroot.znode; if (unlikely(!znode)) { @@ -2966,7 +2967,7 @@ static struct ubifs_znode *right_znode(struct ubifs_info *c, * * This function searches an indexing node by its first key @key and its * address @lnum:@offs. It looks up the indexing tree by pulling all indexing - * nodes it traverses to TNC. This function is called fro indexing nodes which + * nodes it traverses to TNC. This function is called for indexing nodes which * were found on the media by scanning, for example when garbage-collecting or * when doing in-the-gaps commit. This means that the indexing node which is * looked for does not have to have exactly the same leftmost key @key, because @@ -2988,6 +2989,8 @@ static struct ubifs_znode *lookup_znode(struct ubifs_info *c, struct ubifs_znode *znode, *zn; int n, nn; + ubifs_assert(key_type(c, key) < UBIFS_INVALID_KEY); + /* * The arguments have probably been read off flash, so don't assume * they are valid. diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 0c9876b396d..c4dc9b18f73 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -119,8 +119,12 @@ * in TNC. However, when replaying, it is handy to introduce fake "truncation" * keys for truncation nodes because the code becomes simpler. So we define * %UBIFS_TRUN_KEY type. + * + * But otherwise, out of the journal reply scope, the truncation keys are + * invalid. */ -#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT +#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT +#define UBIFS_INVALID_KEY UBIFS_KEY_TYPES_CNT /* * How much a directory entry/extended attribute entry adds to the parent/host -- cgit v1.2.3-18-g5258 From 1a9476a77083354005750c9df45ba9d71ad12c8c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 8 Aug 2010 12:45:23 +0300 Subject: UBIFS: fix assertion warnings in comparison function When running the integrity test ('integck' from mtd-utils) on current UBIFS on 2.6.35, I see that assertions in UBIFS 'list_sort()' comparison functions trigger sometimes, e.g.: UBIFS assert failed in data_nodes_cmp at 132 (pid 28311) My investigation showed that this happens when 'list_sort()' calls the 'cmp()' function with equivalent arguments. In this case, the 'struct list_head' parameter, passed to 'cmp()' is bogus, and it does not belong to any element in the original list. And this issue seems to be introduced by commit: commit 835cc0c8477fdbc59e0217891d6f11061b1ac4e2 Author: Don Mullis Date: Fri Mar 5 13:43:15 2010 -0800 It is easy to work around the issue by doing: if (a == b) return 0; in UBIFS. It works, but 'lib_sort()' should nevertheless be fixed. Although it is harmless to have this piece of code in UBIFS. This patch adds that code to both UBIFS 'cmp()' functions: 'data_nodes_cmp()' and 'nondata_nodes_cmp()'. Signed-off-by: Artem Bityutskiy --- fs/ubifs/gc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs') diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index dafef3d195d..4fc31ca0e3b 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -125,6 +125,9 @@ int data_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) struct ubifs_scan_node *sa, *sb; cond_resched(); + if (a == b) + return 0; + sa = list_entry(a, struct ubifs_scan_node, list); sb = list_entry(b, struct ubifs_scan_node, list); @@ -165,6 +168,9 @@ int nondata_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) struct ubifs_scan_node *sa, *sb; cond_resched(); + if (a == b) + return 0; + sa = list_entry(a, struct ubifs_scan_node, list); sb = list_entry(b, struct ubifs_scan_node, list); -- cgit v1.2.3-18-g5258 From 3bb66b47a4268a4419594b4c4aec58dbeb6b58d2 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 7 Aug 2010 10:06:11 +0300 Subject: UBIFS: introduce list sorting debugging checks The UBIFS bug in the GC list sorting comparison functions inspired me to write internal debugging check functions which verify that the list of nodes is sorted properly. So, this patch implements 2 new debugging functions: o 'dbg_check_data_nodes_order()' - check order of data nodes list o 'dbg_check_nondata_nodes_order()' - check order of non-data nodes list The debugging functions are executed only if general UBIFS debugging checks are enabled. And they are compiled out if UBIFS debugging is disabled. Signed-off-by: Artem Bityutskiy --- fs/ubifs/debug.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ubifs/debug.h | 4 ++ fs/ubifs/gc.c | 10 +++- 3 files changed, 168 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index c2a68baa782..c664f10aec6 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -2239,6 +2239,162 @@ out_free: return err; } +/** + * dbg_check_data_nodes_order - check that list of data nodes is sorted. + * @c: UBIFS file-system description object + * @head: the list of nodes ('struct ubifs_scan_node' objects) + * + * This function returns zero if the list of data nodes is sorted correctly, + * and %-EINVAL if not. + */ +int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head) +{ + struct list_head *cur; + struct ubifs_scan_node *sa, *sb; + + if (!(ubifs_chk_flags & UBIFS_CHK_GEN)) + return 0; + + for (cur = head->next; cur->next != head; cur = cur->next) { + ino_t inuma, inumb; + uint32_t blka, blkb; + + cond_resched(); + sa = container_of(cur, struct ubifs_scan_node, list); + sb = container_of(cur->next, struct ubifs_scan_node, list); + + if (sa->type != UBIFS_DATA_NODE) { + ubifs_err("bad node type %d", sa->type); + dbg_dump_node(c, sa->node); + return -EINVAL; + } + if (sb->type != UBIFS_DATA_NODE) { + ubifs_err("bad node type %d", sb->type); + dbg_dump_node(c, sb->node); + return -EINVAL; + } + + inuma = key_inum(c, &sa->key); + inumb = key_inum(c, &sb->key); + + if (inuma < inumb) + continue; + if (inuma > inumb) { + ubifs_err("larger inum %lu goes before inum %lu", + (unsigned long)inuma, (unsigned long)inumb); + goto error_dump; + } + + blka = key_block(c, &sa->key); + blkb = key_block(c, &sb->key); + + if (blka > blkb) { + ubifs_err("larger block %u goes before %u", blka, blkb); + goto error_dump; + } + if (blka == blkb) { + ubifs_err("two data nodes for the same block"); + goto error_dump; + } + } + + return 0; + +error_dump: + dbg_dump_node(c, sa->node); + dbg_dump_node(c, sb->node); + return -EINVAL; +} + +/** + * dbg_check_nondata_nodes_order - check that list of data nodes is sorted. + * @c: UBIFS file-system description object + * @head: the list of nodes ('struct ubifs_scan_node' objects) + * + * This function returns zero if the list of non-data nodes is sorted correctly, + * and %-EINVAL if not. + */ +int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head) +{ + struct list_head *cur; + struct ubifs_scan_node *sa, *sb; + + if (!(ubifs_chk_flags & UBIFS_CHK_GEN)) + return 0; + + for (cur = head->next; cur->next != head; cur = cur->next) { + ino_t inuma, inumb; + uint32_t hasha, hashb; + + cond_resched(); + sa = container_of(cur, struct ubifs_scan_node, list); + sb = container_of(cur->next, struct ubifs_scan_node, list); + + if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE && + sa->type != UBIFS_XENT_NODE) { + ubifs_err("bad node type %d", sa->type); + dbg_dump_node(c, sa->node); + return -EINVAL; + } + if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE && + sa->type != UBIFS_XENT_NODE) { + ubifs_err("bad node type %d", sb->type); + dbg_dump_node(c, sb->node); + return -EINVAL; + } + + if (sa->type != UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) { + ubifs_err("non-inode node goes before inode node"); + goto error_dump; + } + + if (sa->type == UBIFS_INO_NODE && sb->type != UBIFS_INO_NODE) + continue; + + if (sa->type == UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) { + /* Inode nodes are sorted in descending size order */ + if (sa->len < sb->len) { + ubifs_err("smaller inode node goes first"); + goto error_dump; + } + continue; + } + + /* + * This is either a dentry or xentry, which should be sorted in + * ascending (parent ino, hash) order. + */ + inuma = key_inum(c, &sa->key); + inumb = key_inum(c, &sb->key); + + if (inuma < inumb) + continue; + if (inuma > inumb) { + ubifs_err("larger inum %lu goes before inum %lu", + (unsigned long)inuma, (unsigned long)inumb); + goto error_dump; + } + + hasha = key_block(c, &sa->key); + hashb = key_block(c, &sb->key); + + if (hasha > hashb) { + ubifs_err("larger hash %u goes before %u", hasha, hashb); + goto error_dump; + } + } + + return 0; + +error_dump: + ubifs_msg("dumping first node"); + dbg_dump_node(c, sa->node); + ubifs_msg("dumping second node"); + dbg_dump_node(c, sb->node); + return -EINVAL; + return 0; +} + static int invocation_cnt; int dbg_force_in_the_gaps(void) diff --git a/fs/ubifs/debug.h b/fs/ubifs/debug.h index 29d960101ea..69ebe472915 100644 --- a/fs/ubifs/debug.h +++ b/fs/ubifs/debug.h @@ -324,6 +324,8 @@ int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, int row, int col); int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode, loff_t size); +int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head); +int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head); /* Force the use of in-the-gaps method for testing */ @@ -465,6 +467,8 @@ void dbg_debugfs_exit_fs(struct ubifs_info *c); #define dbg_check_lprops(c) 0 #define dbg_check_lpt_nodes(c, cnode, row, col) 0 #define dbg_check_inode_size(c, inode, size) 0 +#define dbg_check_data_nodes_order(c, head) 0 +#define dbg_check_nondata_nodes_order(c, head) 0 #define dbg_force_in_the_gaps_enabled 0 #define dbg_force_in_the_gaps() 0 #define dbg_failure_mode 0 diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index 4fc31ca0e3b..396f24a30af 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -242,14 +242,13 @@ int nondata_nodes_cmp(void *priv, struct list_head *a, struct list_head *b) static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb, struct list_head *nondata, int *min) { + int err; struct ubifs_scan_node *snod, *tmp; *min = INT_MAX; /* Separate data nodes and non-data nodes */ list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) { - int err; - ubifs_assert(snod->type == UBIFS_INO_NODE || snod->type == UBIFS_DATA_NODE || snod->type == UBIFS_DENT_NODE || @@ -293,6 +292,13 @@ static int sort_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb, /* Sort data and non-data nodes */ list_sort(c, &sleb->nodes, &data_nodes_cmp); list_sort(c, nondata, &nondata_nodes_cmp); + + err = dbg_check_data_nodes_order(c, &sleb->nodes); + if (err) + return err; + err = dbg_check_nondata_nodes_order(c, nondata); + if (err) + return err; return 0; } -- cgit v1.2.3-18-g5258 From 3a8fa0ed3e1a8dc54f9297d2a2292b87e03e21b5 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 22 Aug 2010 21:27:30 +0300 Subject: UBIFS: improve error reporting when reading bad node When an error happens during validation of read node, the typical situation is that the LEB we read is unmapped (due to some bug). It is handy to include the mapping status into the error message. Signed-off-by: Artem Bityutskiy --- fs/ubifs/io.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c index bcf5a16f30b..9432431bf59 100644 --- a/fs/ubifs/io.c +++ b/fs/ubifs/io.c @@ -815,7 +815,8 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len, return 0; out: - ubifs_err("bad node at LEB %d:%d", lnum, offs); + ubifs_err("bad node at LEB %d:%d, LEB mapping status %d", lnum, offs, + ubi_is_mapped(c->ubi, lnum)); dbg_dump_node(c, buf); dbg_dump_stack(); return -EINVAL; -- cgit v1.2.3-18-g5258 From 314dd2a05340fffbe217c5e40ec6c3bd1d07bf89 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Fri, 3 Sep 2010 10:07:48 -0500 Subject: dlm: Don't send callback to node making lock request when "try 1cb" fails When converting a lock, an lkb is in the granted state and also being used to request a new state. In the case that the conversion was a "try 1cb" type which has failed, and if the new state was incompatible with the old state, a callback was being generated to the requesting node. This is incorrect as callbacks should only be sent to all the other nodes holding blocking locks. The requesting node should receive the normal (failed) response to its "try 1cb" conversion request only. This was discovered while debugging a performance problem on GFS2, however this fix also speeds up GFS as well. In the GFS2 case the performance gain is over 10x for cases of write activity to an inode whose glock is cached on another, idle (wrt that glock) node. (comment added, dct) Signed-off-by: Steven Whitehouse Tested-by: Abhijith Das Signed-off-by: David Teigland --- fs/dlm/lock.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c index 031dbe3a15c..64e5f3efdd8 100644 --- a/fs/dlm/lock.c +++ b/fs/dlm/lock.c @@ -1846,6 +1846,9 @@ static void send_bast_queue(struct dlm_rsb *r, struct list_head *head, struct dlm_lkb *gr; list_for_each_entry(gr, head, lkb_statequeue) { + /* skip self when sending basts to convertqueue */ + if (gr == lkb) + continue; if (gr->lkb_bastfn && modes_require_bast(gr, lkb)) { queue_bast(r, gr, lkb->lkb_rqmode); gr->lkb_highbast = lkb->lkb_rqmode; -- cgit v1.2.3-18-g5258 From 0e54c8992a35ab8126e8a7661007ddc1745a2597 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Sun, 5 Sep 2010 22:33:00 +0400 Subject: UBIFS: check return code of ubifs_lpt_lookup Function ubifs_lpt_lookup may return ERR_PTR(...). Check for it. [Tweaked by Artem Bityutskiy] Signed-off-by: Vasiliy Kulikov Signed-off-by: Artem Bityutskiy --- fs/ubifs/lpt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c index 0084a33c4c6..72775d35b99 100644 --- a/fs/ubifs/lpt.c +++ b/fs/ubifs/lpt.c @@ -1363,6 +1363,7 @@ static int read_lsave(struct ubifs_info *c) goto out; for (i = 0; i < c->lsave_cnt; i++) { int lnum = c->lsave[i]; + struct ubifs_lprops *lprops; /* * Due to automatic resizing, the values in the lsave table @@ -1370,7 +1371,11 @@ static int read_lsave(struct ubifs_info *c) */ if (lnum >= c->leb_cnt) continue; - ubifs_lpt_lookup(c, lnum); + lprops = ubifs_lpt_lookup(c, lnum); + if (IS_ERR(lprops)) { + err = PTR_ERR(lprops); + goto out; + } } out: vfree(buf); -- cgit v1.2.3-18-g5258 From 8c893a5545ca772744376295690723dcb0b47d96 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Sun, 5 Sep 2010 22:33:04 +0400 Subject: UBIFS: check return code of pnode_lookup Function pnode_lookup may return ERR_PTR(...). Check for it. Signed-off-by: Vasiliy Kulikov Signed-off-by: Artem Bityutskiy --- fs/ubifs/lpt_commit.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c index d12535b7fc7..5c90dec5db0 100644 --- a/fs/ubifs/lpt_commit.c +++ b/fs/ubifs/lpt_commit.c @@ -705,6 +705,9 @@ static int make_tree_dirty(struct ubifs_info *c) struct ubifs_pnode *pnode; pnode = pnode_lookup(c, 0); + if (IS_ERR(pnode)) + return PTR_ERR(pnode); + while (pnode) { do_make_pnode_dirty(c, pnode); pnode = next_pnode_to_dirty(c, pnode); -- cgit v1.2.3-18-g5258 From 2cf6d26a354ab6362e301b5a323832b02867df47 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:10 -0400 Subject: block: pass gfp_mask and flags to sb_issue_discard We'll need to get rid of the BLKDEV_IFL_BARRIER flag, and to facilitate that and to make the interface less confusing pass all flags explicitly. Signed-off-by: Christoph Hellwig Acked-by: Mike Snitzer Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/ext4/mballoc.c | 3 ++- fs/fat/fatent.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 4b4ad4b7ce5..df44b345f66 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2566,7 +2566,8 @@ static inline void ext4_issue_discard(struct super_block *sb, discard_block = block + ext4_group_first_block_no(sb, block_group); trace_ext4_discard_blocks(sb, (unsigned long long) discard_block, count); - ret = sb_issue_discard(sb, discard_block, count); + ret = sb_issue_discard(sb, discard_block, count, GFP_NOFS, + BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER); if (ret == EOPNOTSUPP) { ext4_warning(sb, "discard not supported, disabling"); clear_opt(EXT4_SB(sb)->s_mount_opt, DISCARD); diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 81184d3b75a..3a56a82f565 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -577,7 +577,9 @@ int fat_free_clusters(struct inode *inode, int cluster) sb_issue_discard(sb, fat_clus_to_blknr(sbi, first_cl), - nr_clus * sbi->sec_per_clus); + nr_clus * sbi->sec_per_clus, + GFP_NOFS, + BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER); first_cl = cluster; } -- cgit v1.2.3-18-g5258 From 80f6c29d8a758650d5c9eac05074b4b3e8c266df Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:11 -0400 Subject: xfs: replace barriers with explicit flush / FUA usage Switch to the WRITE_FLUSH_FUA flag for log writes and remove the EOPNOTSUPP detection for barriers. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/xfs/linux-2.6/xfs_buf.c | 16 ++-------------- fs/xfs/linux-2.6/xfs_buf.h | 11 +---------- fs/xfs/linux-2.6/xfs_trace.h | 1 - fs/xfs/xfs_log.c | 13 ------------- 4 files changed, 3 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index ea79072f521..b93ea334228 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -929,19 +929,7 @@ xfs_buf_iodone_work( xfs_buf_t *bp = container_of(work, xfs_buf_t, b_iodone_work); - /* - * We can get an EOPNOTSUPP to ordered writes. Here we clear the - * ordered flag and reissue them. Because we can't tell the higher - * layers directly that they should not issue ordered I/O anymore, they - * need to check if the _XFS_BARRIER_FAILED flag was set during I/O completion. - */ - if ((bp->b_error == EOPNOTSUPP) && - (bp->b_flags & (XBF_ORDERED|XBF_ASYNC)) == (XBF_ORDERED|XBF_ASYNC)) { - trace_xfs_buf_ordered_retry(bp, _RET_IP_); - bp->b_flags &= ~XBF_ORDERED; - bp->b_flags |= _XFS_BARRIER_FAILED; - xfs_buf_iorequest(bp); - } else if (bp->b_iodone) + if (bp->b_iodone) (*(bp->b_iodone))(bp); else if (bp->b_flags & XBF_ASYNC) xfs_buf_relse(bp); @@ -1200,7 +1188,7 @@ _xfs_buf_ioapply( if (bp->b_flags & XBF_ORDERED) { ASSERT(!(bp->b_flags & XBF_READ)); - rw = WRITE_BARRIER; + rw = WRITE_FLUSH_FUA; } else if (bp->b_flags & XBF_LOG_BUFFER) { ASSERT(!(bp->b_flags & XBF_READ_AHEAD)); bp->b_flags &= ~_XBF_RUN_QUEUES; diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index d072e5ff923..d533d64e2c3 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -86,14 +86,6 @@ typedef enum { */ #define _XBF_PAGE_LOCKED (1 << 22) -/* - * If we try a barrier write, but it fails we have to communicate - * this to the upper layers. Unfortunately b_error gets overwritten - * when the buffer is re-issued so we have to add another flag to - * keep this information. - */ -#define _XFS_BARRIER_FAILED (1 << 23) - typedef unsigned int xfs_buf_flags_t; #define XFS_BUF_FLAGS \ @@ -114,8 +106,7 @@ typedef unsigned int xfs_buf_flags_t; { _XBF_PAGES, "PAGES" }, \ { _XBF_RUN_QUEUES, "RUN_QUEUES" }, \ { _XBF_DELWRI_Q, "DELWRI_Q" }, \ - { _XBF_PAGE_LOCKED, "PAGE_LOCKED" }, \ - { _XFS_BARRIER_FAILED, "BARRIER_FAILED" } + { _XBF_PAGE_LOCKED, "PAGE_LOCKED" } typedef enum { diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index be5dffd282a..8fe311a456e 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -325,7 +325,6 @@ DEFINE_BUF_EVENT(xfs_buf_lock); DEFINE_BUF_EVENT(xfs_buf_lock_done); DEFINE_BUF_EVENT(xfs_buf_cond_lock); DEFINE_BUF_EVENT(xfs_buf_unlock); -DEFINE_BUF_EVENT(xfs_buf_ordered_retry); DEFINE_BUF_EVENT(xfs_buf_iowait); DEFINE_BUF_EVENT(xfs_buf_iowait_done); DEFINE_BUF_EVENT(xfs_buf_delwri_queue); diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 925d572bf0f..430a8fc02c1 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -916,19 +916,6 @@ xlog_iodone(xfs_buf_t *bp) aborted = 0; l = iclog->ic_log; - /* - * If the _XFS_BARRIER_FAILED flag was set by a lower - * layer, it means the underlying device no longer supports - * barrier I/O. Warn loudly and turn off barriers. - */ - if (bp->b_flags & _XFS_BARRIER_FAILED) { - bp->b_flags &= ~_XFS_BARRIER_FAILED; - l->l_mp->m_flags &= ~XFS_MOUNT_BARRIER; - xfs_fs_cmn_err(CE_WARN, l->l_mp, - "xlog_iodone: Barriers are no longer supported" - " by device. Disabling barriers\n"); - } - /* * Race to shutdown the filesystem if we see an error. */ -- cgit v1.2.3-18-g5258 From c3b9a62c8f932f32a733d6b628f61f3f28345727 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:12 -0400 Subject: btrfs: replace barriers with explicit flush / FUA usage Switch to the WRITE_FLUSH_FUA flag for log writes, remove the EOPNOTSUPP detection for barriers and stop setting the barrier flag for discards. Signed-off-by: Christoph Hellwig Acked-by: Chris Mason Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/btrfs/disk-io.c | 19 ++++--------------- fs/btrfs/extent-tree.c | 2 +- fs/btrfs/volumes.c | 4 ---- fs/btrfs/volumes.h | 1 - 4 files changed, 5 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 64f10082f04..5e789f4a3ed 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2063,7 +2063,7 @@ static void btrfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate) if (uptodate) { set_buffer_uptodate(bh); } else { - if (!buffer_eopnotsupp(bh) && printk_ratelimit()) { + if (printk_ratelimit()) { printk(KERN_WARNING "lost page write due to " "I/O error on %s\n", bdevname(bh->b_bdev, b)); @@ -2200,21 +2200,10 @@ static int write_dev_supers(struct btrfs_device *device, bh->b_end_io = btrfs_end_buffer_write_sync; } - if (i == last_barrier && do_barriers && device->barriers) { - ret = submit_bh(WRITE_BARRIER, bh); - if (ret == -EOPNOTSUPP) { - printk("btrfs: disabling barriers on dev %s\n", - device->name); - set_buffer_uptodate(bh); - device->barriers = 0; - /* one reference for submit_bh */ - get_bh(bh); - lock_buffer(bh); - ret = submit_bh(WRITE_SYNC, bh); - } - } else { + if (i == last_barrier && do_barriers) + ret = submit_bh(WRITE_FLUSH_FUA, bh); + else ret = submit_bh(WRITE_SYNC, bh); - } if (ret) errors++; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 32d094002a5..43dc9ea9aef 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -1696,7 +1696,7 @@ static void btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len) { blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL, - BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER); + BLKDEV_IFL_WAIT); } static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index dd318ff280b..e25e46a8b4e 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -398,7 +398,6 @@ static noinline int device_list_add(const char *path, device->work.func = pending_bios_fn; memcpy(device->uuid, disk_super->dev_item.uuid, BTRFS_UUID_SIZE); - device->barriers = 1; spin_lock_init(&device->io_lock); device->name = kstrdup(path, GFP_NOFS); if (!device->name) { @@ -462,7 +461,6 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig) device->devid = orig_dev->devid; device->work.func = pending_bios_fn; memcpy(device->uuid, orig_dev->uuid, sizeof(device->uuid)); - device->barriers = 1; spin_lock_init(&device->io_lock); INIT_LIST_HEAD(&device->dev_list); INIT_LIST_HEAD(&device->dev_alloc_list); @@ -1489,7 +1487,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) trans = btrfs_start_transaction(root, 0); lock_chunks(root); - device->barriers = 1; device->writeable = 1; device->work.func = pending_bios_fn; generate_random_uuid(device->uuid); @@ -3084,7 +3081,6 @@ static struct btrfs_device *add_missing_dev(struct btrfs_root *root, return NULL; list_add(&device->dev_list, &fs_devices->devices); - device->barriers = 1; device->dev_root = root->fs_info->dev_root; device->devid = devid; device->work.func = pending_bios_fn; diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 31b0fabdd2e..2b638b6e4ee 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -42,7 +42,6 @@ struct btrfs_device { int running_pending; u64 generation; - int barriers; int writeable; int in_fs_metadata; -- cgit v1.2.3-18-g5258 From f1e4d518c3beddf67f7722f3548eda0ec7006204 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:13 -0400 Subject: gfs2: replace barriers with explicit flush / FUA usage Switch to the WRITE_FLUSH_FUA flag for log writes, remove the EOPNOTSUPP detection for barriers and stop setting the barrier flag for discards. Signed-off-by: Christoph Hellwig Acked-by: Steven Whitehouse Acked-by: Bob Peterson Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/gfs2/log.c | 19 +++++-------------- fs/gfs2/rgrp.c | 5 ++--- 2 files changed, 7 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index cde1248a622..9c65170e932 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -592,22 +592,13 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) lh->lh_hash = cpu_to_be32(hash); bh->b_end_io = end_buffer_write_sync; - if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) - goto skip_barrier; get_bh(bh); - submit_bh(WRITE_BARRIER | REQ_META, bh); - wait_on_buffer(bh); - if (buffer_eopnotsupp(bh)) { - clear_buffer_eopnotsupp(bh); - set_buffer_uptodate(bh); - fs_info(sdp, "barrier sync failed - disabling barriers\n"); - set_bit(SDF_NOBARRIERS, &sdp->sd_flags); - lock_buffer(bh); -skip_barrier: - get_bh(bh); + if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) submit_bh(WRITE_SYNC | REQ_META, bh); - wait_on_buffer(bh); - } + else + submit_bh(WRITE_FLUSH_FUA | REQ_META, bh); + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) gfs2_io_error_bh(sdp, bh); brelse(bh); diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 171a744f8e4..37931647291 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -854,8 +854,7 @@ static void gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset, if ((start + nr_sects) != blk) { rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS, - BLKDEV_IFL_WAIT | - BLKDEV_IFL_BARRIER); + BLKDEV_IFL_WAIT); if (rv) goto fail; nr_sects = 0; @@ -870,7 +869,7 @@ start_new_extent: } if (nr_sects) { rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS, - BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER); + BLKDEV_IFL_WAIT); if (rv) goto fail; } -- cgit v1.2.3-18-g5258 From 7cd33ad23ec41d685902159be8b2c6552fab8bd0 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:14 -0400 Subject: reiserfs: replace barriers with explicit flush / FUA usage Switch to the WRITE_FLUSH_FUA flag for log writes and remove the EOPNOTSUPP detection for barriers. Note that reiserfs had a fairly different code path for barriers before as it wa the only filesystem actually making use of them. The new code always uses the old non-barrier codepath and just sets the WRITE_FLUSH_FUA explicitly for the journal commits. Signed-off-by: Christoph Hellwig Acked-by: Jan Kara Acked-by: Chris Mason Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/reiserfs/journal.c | 106 ++++++++++---------------------------------------- 1 file changed, 20 insertions(+), 86 deletions(-) (limited to 'fs') diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 812e2c05aa2..076c8b19468 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -138,13 +138,6 @@ static int reiserfs_clean_and_file_buffer(struct buffer_head *bh) return 0; } -static void disable_barrier(struct super_block *s) -{ - REISERFS_SB(s)->s_mount_opt &= ~(1 << REISERFS_BARRIER_FLUSH); - printk("reiserfs: disabling flush barriers on %s\n", - reiserfs_bdevname(s)); -} - static struct reiserfs_bitmap_node *allocate_bitmap_node(struct super_block *sb) { @@ -677,30 +670,6 @@ static void submit_ordered_buffer(struct buffer_head *bh) submit_bh(WRITE, bh); } -static int submit_barrier_buffer(struct buffer_head *bh) -{ - get_bh(bh); - bh->b_end_io = reiserfs_end_ordered_io; - clear_buffer_dirty(bh); - if (!buffer_uptodate(bh)) - BUG(); - return submit_bh(WRITE_BARRIER, bh); -} - -static void check_barrier_completion(struct super_block *s, - struct buffer_head *bh) -{ - if (buffer_eopnotsupp(bh)) { - clear_buffer_eopnotsupp(bh); - disable_barrier(s); - set_buffer_uptodate(bh); - set_buffer_dirty(bh); - reiserfs_write_unlock(s); - sync_dirty_buffer(bh); - reiserfs_write_lock(s); - } -} - #define CHUNK_SIZE 32 struct buffer_chunk { struct buffer_head *bh[CHUNK_SIZE]; @@ -1009,7 +978,6 @@ static int flush_commit_list(struct super_block *s, struct buffer_head *tbh = NULL; unsigned int trans_id = jl->j_trans_id; struct reiserfs_journal *journal = SB_JOURNAL(s); - int barrier = 0; int retval = 0; int write_len; @@ -1094,24 +1062,6 @@ static int flush_commit_list(struct super_block *s, } atomic_dec(&journal->j_async_throttle); - /* We're skipping the commit if there's an error */ - if (retval || reiserfs_is_journal_aborted(journal)) - barrier = 0; - - /* wait on everything written so far before writing the commit - * if we are in barrier mode, send the commit down now - */ - barrier = reiserfs_barrier_flush(s); - if (barrier) { - int ret; - lock_buffer(jl->j_commit_bh); - ret = submit_barrier_buffer(jl->j_commit_bh); - if (ret == -EOPNOTSUPP) { - set_buffer_uptodate(jl->j_commit_bh); - disable_barrier(s); - barrier = 0; - } - } for (i = 0; i < (jl->j_len + 1); i++) { bn = SB_ONDISK_JOURNAL_1st_BLOCK(s) + (jl->j_start + i) % SB_ONDISK_JOURNAL_SIZE(s); @@ -1143,27 +1093,22 @@ static int flush_commit_list(struct super_block *s, BUG_ON(atomic_read(&(jl->j_commit_left)) != 1); - if (!barrier) { - /* If there was a write error in the journal - we can't commit - * this transaction - it will be invalid and, if successful, - * will just end up propagating the write error out to - * the file system. */ - if (likely(!retval && !reiserfs_is_journal_aborted (journal))) { - if (buffer_dirty(jl->j_commit_bh)) - BUG(); - mark_buffer_dirty(jl->j_commit_bh) ; - reiserfs_write_unlock(s); - sync_dirty_buffer(jl->j_commit_bh) ; - reiserfs_write_lock(s); - } - } else { + /* If there was a write error in the journal - we can't commit + * this transaction - it will be invalid and, if successful, + * will just end up propagating the write error out to + * the file system. */ + if (likely(!retval && !reiserfs_is_journal_aborted (journal))) { + if (buffer_dirty(jl->j_commit_bh)) + BUG(); + mark_buffer_dirty(jl->j_commit_bh) ; reiserfs_write_unlock(s); - wait_on_buffer(jl->j_commit_bh); + if (reiserfs_barrier_flush(s)) + __sync_dirty_buffer(jl->j_commit_bh, WRITE_FLUSH_FUA); + else + sync_dirty_buffer(jl->j_commit_bh); reiserfs_write_lock(s); } - check_barrier_completion(s, jl->j_commit_bh); - /* If there was a write error in the journal - we can't commit this * transaction - it will be invalid and, if successful, will just end * up propagating the write error out to the filesystem. */ @@ -1319,26 +1264,15 @@ static int _update_journal_header_block(struct super_block *sb, jh->j_first_unflushed_offset = cpu_to_le32(offset); jh->j_mount_id = cpu_to_le32(journal->j_mount_id); - if (reiserfs_barrier_flush(sb)) { - int ret; - lock_buffer(journal->j_header_bh); - ret = submit_barrier_buffer(journal->j_header_bh); - if (ret == -EOPNOTSUPP) { - set_buffer_uptodate(journal->j_header_bh); - disable_barrier(sb); - goto sync; - } - reiserfs_write_unlock(sb); - wait_on_buffer(journal->j_header_bh); - reiserfs_write_lock(sb); - check_barrier_completion(sb, journal->j_header_bh); - } else { - sync: - set_buffer_dirty(journal->j_header_bh); - reiserfs_write_unlock(sb); + set_buffer_dirty(journal->j_header_bh); + reiserfs_write_unlock(sb); + + if (reiserfs_barrier_flush(sb)) + __sync_dirty_buffer(journal->j_header_bh, WRITE_FLUSH_FUA); + else sync_dirty_buffer(journal->j_header_bh); - reiserfs_write_lock(sb); - } + + reiserfs_write_lock(sb); if (!buffer_uptodate(journal->j_header_bh)) { reiserfs_warning(sb, "journal-837", "IO error during journal replay"); -- cgit v1.2.3-18-g5258 From f8c131f5b6ffc899a70b30e541f367d47f89691c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:15 -0400 Subject: nilfs2: replace barriers with explicit flush / FUA usage Switch to the WRITE_FLUSH_FUA flag for log writes, remove the EOPNOTSUPP detection for barriers and stop setting the barrier flag for discards. tj: nilfs is now fixed to wait for discard completion. Updated this patch accordingly and dropped warning about it. Signed-off-by: Christoph Hellwig Acked-by: Ryusuke Konishi Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/nilfs2/super.c | 10 +--------- fs/nilfs2/the_nilfs.c | 7 ++----- 2 files changed, 3 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 922263393c7..faa5078ff75 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -178,17 +178,9 @@ static int nilfs_sync_super(struct nilfs_sb_info *sbi, int flag) retry: set_buffer_dirty(nilfs->ns_sbh[0]); - if (nilfs_test_opt(sbi, BARRIER)) { err = __sync_dirty_buffer(nilfs->ns_sbh[0], - WRITE_SYNC | WRITE_BARRIER); - if (err == -EOPNOTSUPP) { - nilfs_warning(sbi->s_super, __func__, - "barrier-based sync failed. " - "disabling barriers\n"); - nilfs_clear_opt(sbi, BARRIER); - goto retry; - } + WRITE_SYNC | WRITE_FLUSH_FUA); } else { err = sync_dirty_buffer(nilfs->ns_sbh[0]); } diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 4317f177ea7..400b2caef4d 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -774,9 +774,7 @@ int nilfs_discard_segments(struct the_nilfs *nilfs, __u64 *segnump, ret = blkdev_issue_discard(nilfs->ns_bdev, start * sects_per_block, nblocks * sects_per_block, - GFP_NOFS, - BLKDEV_IFL_WAIT | - BLKDEV_IFL_BARRIER); + GFP_NOFS, BLKDEV_IFL_WAIT); if (ret < 0) return ret; nblocks = 0; @@ -786,8 +784,7 @@ int nilfs_discard_segments(struct the_nilfs *nilfs, __u64 *segnump, ret = blkdev_issue_discard(nilfs->ns_bdev, start * sects_per_block, nblocks * sects_per_block, - GFP_NOFS, - BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER); + GFP_NOFS, BLKDEV_IFL_WAIT); return ret; } -- cgit v1.2.3-18-g5258 From 4524451ef7e88c64a868a8f5a0b49bda73beb2a3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:16 -0400 Subject: jbd: replace barriers with explicit flush / FUA usage Switch to the WRITE_FLUSH_FUA flag for journal commits and remove the EOPNOTSUPP detection for barriers. Signed-off-by: Christoph Hellwig Acked-by: Jan Kara Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/jbd/commit.c | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) (limited to 'fs') diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 95d8c11c929..484c5e5fa8a 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -137,34 +137,10 @@ static int journal_write_commit_record(journal_t *journal, JBUFFER_TRACE(descriptor, "write commit block"); set_buffer_dirty(bh); - if (journal->j_flags & JFS_BARRIER) { - ret = __sync_dirty_buffer(bh, WRITE_SYNC | WRITE_BARRIER); - - /* - * Is it possible for another commit to fail at roughly - * the same time as this one? If so, we don't want to - * trust the barrier flag in the super, but instead want - * to remember if we sent a barrier request - */ - if (ret == -EOPNOTSUPP) { - char b[BDEVNAME_SIZE]; - - printk(KERN_WARNING - "JBD: barrier-based sync failed on %s - " - "disabling barriers\n", - bdevname(journal->j_dev, b)); - spin_lock(&journal->j_state_lock); - journal->j_flags &= ~JFS_BARRIER; - spin_unlock(&journal->j_state_lock); - - /* And try again, without the barrier */ - set_buffer_uptodate(bh); - set_buffer_dirty(bh); - ret = sync_dirty_buffer(bh); - } - } else { + if (journal->j_flags & JFS_BARRIER) + ret = __sync_dirty_buffer(bh, WRITE_SYNC | WRITE_FLUSH_FUA); + else ret = sync_dirty_buffer(bh); - } put_bh(bh); /* One for getblk() */ journal_put_journal_head(descriptor); -- cgit v1.2.3-18-g5258 From f73bee49855fe968e87af18e44f2d17e1e675e74 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 18 Aug 2010 15:56:56 +0200 Subject: jbd2: Modify ASYNC_COMMIT code to not rely on queue draining on barrier Currently JBD2 relies blkdev_issue_flush() draining the queue when ASYNC_COMMIT feature is set. This property is going away so make JBD2 wait for buffers it needs on its own before submitting the cache flush. Signed-off-by: Jan Kara Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/jbd2/commit.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 7c068c189d8..d6aeb1f6cfe 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -701,6 +701,16 @@ start_journal_io: } } + err = journal_finish_inode_data_buffers(journal, commit_transaction); + if (err) { + printk(KERN_WARNING + "JBD2: Detected IO errors while flushing file data " + "on %s\n", journal->j_devname); + if (journal->j_flags & JBD2_ABORT_ON_SYNCDATA_ERR) + jbd2_journal_abort(journal, err); + err = 0; + } + /* * If the journal is not located on the file system device, * then we must flush the file system device before we issue @@ -719,19 +729,6 @@ start_journal_io: &cbh, crc32_sum); if (err) __jbd2_journal_abort_hard(journal); - if (journal->j_flags & JBD2_BARRIER) - blkdev_issue_flush(journal->j_dev, GFP_KERNEL, NULL, - BLKDEV_IFL_WAIT); - } - - err = journal_finish_inode_data_buffers(journal, commit_transaction); - if (err) { - printk(KERN_WARNING - "JBD2: Detected IO errors while flushing file data " - "on %s\n", journal->j_devname); - if (journal->j_flags & JBD2_ABORT_ON_SYNCDATA_ERR) - jbd2_journal_abort(journal, err); - err = 0; } /* Lo and behold: we have just managed to send a transaction to @@ -845,6 +842,12 @@ wait_for_iobuf: } if (!err && !is_journal_aborted(journal)) err = journal_wait_on_commit_record(journal, cbh); + if (JBD2_HAS_INCOMPAT_FEATURE(journal, + JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT) && + journal->j_flags & JBD2_BARRIER) { + blkdev_issue_flush(journal->j_dev, GFP_KERNEL, NULL, + BLKDEV_IFL_WAIT); + } if (err) jbd2_journal_abort(journal, err); -- cgit v1.2.3-18-g5258 From 9c35575bbe6b1dd4914a5323c8df8b3159edcc75 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:17 -0400 Subject: jbd2: replace barriers with explicit flush / FUA usage Switch to the WRITE_FLUSH_FUA flag for journal commits and remove the EOPNOTSUPP detection for barriers. Signed-off-by: Christoph Hellwig Acked-by: Jan Kara Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/jbd2/commit.c | 43 ++++--------------------------------------- 1 file changed, 4 insertions(+), 39 deletions(-) (limited to 'fs') diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index d6aeb1f6cfe..f204e27f44d 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -134,25 +134,11 @@ static int journal_submit_commit_record(journal_t *journal, if (journal->j_flags & JBD2_BARRIER && !JBD2_HAS_INCOMPAT_FEATURE(journal, - JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) { - ret = submit_bh(WRITE_SYNC_PLUG | WRITE_BARRIER, bh); - if (ret == -EOPNOTSUPP) { - printk(KERN_WARNING - "JBD2: Disabling barriers on %s, " - "not supported by device\n", journal->j_devname); - write_lock(&journal->j_state_lock); - journal->j_flags &= ~JBD2_BARRIER; - write_unlock(&journal->j_state_lock); - - /* And try again, without the barrier */ - lock_buffer(bh); - set_buffer_uptodate(bh); - clear_buffer_dirty(bh); - ret = submit_bh(WRITE_SYNC_PLUG, bh); - } - } else { + JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)) + ret = submit_bh(WRITE_SYNC_PLUG | WRITE_FLUSH_FUA, bh); + else ret = submit_bh(WRITE_SYNC_PLUG, bh); - } + *cbh = bh; return ret; } @@ -166,29 +152,8 @@ static int journal_wait_on_commit_record(journal_t *journal, { int ret = 0; -retry: clear_buffer_dirty(bh); wait_on_buffer(bh); - if (buffer_eopnotsupp(bh) && (journal->j_flags & JBD2_BARRIER)) { - printk(KERN_WARNING - "JBD2: %s: disabling barries on %s - not supported " - "by device\n", __func__, journal->j_devname); - write_lock(&journal->j_state_lock); - journal->j_flags &= ~JBD2_BARRIER; - write_unlock(&journal->j_state_lock); - - lock_buffer(bh); - clear_buffer_dirty(bh); - set_buffer_uptodate(bh); - bh->b_end_io = journal_end_buffer_io_sync; - - ret = submit_bh(WRITE_SYNC_PLUG, bh); - if (ret) { - unlock_buffer(bh); - return ret; - } - goto retry; - } if (unlikely(!buffer_uptodate(bh))) ret = -EIO; -- cgit v1.2.3-18-g5258 From 61002f7db33c7d064cddcdab680fb750fa43d8fd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:18 -0400 Subject: ext4: do not send discards as barriers ext4 already uses synchronous discards, no need to add I/O barriers. Signed-off-by: Christoph Hellwig Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/ext4/mballoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index df44b345f66..a22bfef3da9 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2567,7 +2567,7 @@ static inline void ext4_issue_discard(struct super_block *sb, trace_ext4_discard_blocks(sb, (unsigned long long) discard_block, count); ret = sb_issue_discard(sb, discard_block, count, GFP_NOFS, - BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER); + BLKDEV_IFL_WAIT); if (ret == EOPNOTSUPP) { ext4_warning(sb, "discard not supported, disabling"); clear_opt(EXT4_SB(sb)->s_mount_opt, DISCARD); -- cgit v1.2.3-18-g5258 From e045db80d07250312500b2ed707b84dc703189d6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:19 -0400 Subject: fat: do not send discards as barriers fat already uses synchronous discards, no need to add I/O barriers. Signed-off-by: Christoph Hellwig Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/fat/fatent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 3a56a82f565..f9a0b7ae864 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -579,7 +579,7 @@ int fat_free_clusters(struct inode *inode, int cluster) fat_clus_to_blknr(sbi, first_cl), nr_clus * sbi->sec_per_clus, GFP_NOFS, - BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER); + BLKDEV_IFL_WAIT); first_cl = cluster; } -- cgit v1.2.3-18-g5258 From 0edd55faea7c8081bc826234b917501738a6218f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 18 Aug 2010 05:29:23 -0400 Subject: block: remove the BH_Eopnotsupp flag This flag was only set for barrier buffers, which we don't submit anymore. Signed-off-by: Christoph Hellwig Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/buffer.c | 7 +------ fs/fat/misc.c | 5 +---- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/buffer.c b/fs/buffer.c index 3e7dca279d1..7f0b9b083f7 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -156,7 +156,7 @@ void end_buffer_write_sync(struct buffer_head *bh, int uptodate) if (uptodate) { set_buffer_uptodate(bh); } else { - if (!buffer_eopnotsupp(bh) && !quiet_error(bh)) { + if (!quiet_error(bh)) { buffer_io_error(bh); printk(KERN_WARNING "lost page write due to " "I/O error on %s\n", @@ -2891,7 +2891,6 @@ static void end_bio_bh_io_sync(struct bio *bio, int err) if (err == -EOPNOTSUPP) { set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); - set_bit(BH_Eopnotsupp, &bh->b_state); } if (unlikely (test_bit(BIO_QUIET,&bio->bi_flags))) @@ -3031,10 +3030,6 @@ int __sync_dirty_buffer(struct buffer_head *bh, int rw) bh->b_end_io = end_buffer_write_sync; ret = submit_bh(rw, bh); wait_on_buffer(bh); - if (buffer_eopnotsupp(bh)) { - clear_buffer_eopnotsupp(bh); - ret = -EOPNOTSUPP; - } if (!ret && !buffer_uptodate(bh)) ret = -EIO; } else { diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 1736f235638..970e682ea75 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -255,10 +255,7 @@ int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) for (i = 0; i < nr_bhs; i++) { wait_on_buffer(bhs[i]); - if (buffer_eopnotsupp(bhs[i])) { - clear_buffer_eopnotsupp(bhs[i]); - err = -EOPNOTSUPP; - } else if (!err && !buffer_uptodate(bhs[i])) + if (!err && !buffer_uptodate(bhs[i])) err = -EIO; } return err; -- cgit v1.2.3-18-g5258 From ddee5cdb70e6f87de2fc696b87bd7bd184a51eb8 Mon Sep 17 00:00:00 2001 From: Tristan Ye Date: Sat, 22 May 2010 16:26:33 +0800 Subject: Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v8. The reason why we need this ioctl is to offer the none-privileged end-user a possibility to get filesys info gathering. We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a structure to kernel containing an array of request pointers and request count, such as, * From userspace: struct ocfs2_info_blocksize oib = { .ib_req = { .ir_magic = OCFS2_INFO_MAGIC, .ir_code = OCFS2_INFO_BLOCKSIZE, ... } ... } struct ocfs2_info_clustersize oic = { ... } uint64_t reqs[2] = {(unsigned long)&oib, (unsigned long)&oic}; struct ocfs2_info info = { .oi_requests = reqs, .oi_count = 2, } ret = ioctl(fd, OCFS2_IOC_INFO, &info); * In kernel: Get the request pointers from *info*, then handle each request one bye one. Idea here is to make the spearated request small enough to guarantee a better backward&forward compatibility since a small piece of request would be less likely to be broken if filesys on raw disk get changed. Currently, the following 7 requests are supported per the requirement from userspace tool o2info, and I believe it will grow over time:-) OCFS2_INFO_CLUSTERSIZE OCFS2_INFO_BLOCKSIZE OCFS2_INFO_MAXSLOTS OCFS2_INFO_LABEL OCFS2_INFO_UUID OCFS2_INFO_FS_FEATURES OCFS2_INFO_JOURNAL_SIZE This ioctl is only specific to OCFS2. Signed-off-by: Tristan Ye Signed-off-by: Joel Becker --- fs/ocfs2/ioctl.c | 356 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/ocfs2_ioctl.h | 95 +++++++++++++ 2 files changed, 451 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index 7d9d9c132ce..7a486819615 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c @@ -26,6 +26,26 @@ #include +#define o2info_from_user(a, b) \ + copy_from_user(&(a), (b), sizeof(a)) +#define o2info_to_user(a, b) \ + copy_to_user((typeof(a) __user *)b, &(a), sizeof(a)) + +/* + * This call is void because we are already reporting an error that may + * be -EFAULT. The error will be returned from the ioctl(2) call. It's + * just a best-effort to tell userspace that this request caused the error. + */ +static inline void __o2info_set_request_error(struct ocfs2_info_request *kreq, + struct ocfs2_info_request __user *req) +{ + kreq->ir_flags |= OCFS2_INFO_FL_ERROR; + (void)put_user(kreq->ir_flags, (__u32 __user *)&(req->ir_flags)); +} + +#define o2info_set_request_error(a, b) \ + __o2info_set_request_error((struct ocfs2_info_request *)&(a), b) + static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags) { int status; @@ -109,6 +129,328 @@ bail: return status; } +int ocfs2_info_handle_blocksize(struct inode *inode, + struct ocfs2_info_request __user *req) +{ + int status = -EFAULT; + struct ocfs2_info_blocksize oib; + + if (o2info_from_user(oib, req)) + goto bail; + + oib.ib_blocksize = inode->i_sb->s_blocksize; + oib.ib_req.ir_flags |= OCFS2_INFO_FL_FILLED; + + if (o2info_to_user(oib, req)) + goto bail; + + status = 0; +bail: + if (status) + o2info_set_request_error(oib, req); + + return status; +} + +int ocfs2_info_handle_clustersize(struct inode *inode, + struct ocfs2_info_request __user *req) +{ + int status = -EFAULT; + struct ocfs2_info_clustersize oic; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (o2info_from_user(oic, req)) + goto bail; + + oic.ic_clustersize = osb->s_clustersize; + oic.ic_req.ir_flags |= OCFS2_INFO_FL_FILLED; + + if (o2info_to_user(oic, req)) + goto bail; + + status = 0; +bail: + if (status) + o2info_set_request_error(oic, req); + + return status; +} + +int ocfs2_info_handle_maxslots(struct inode *inode, + struct ocfs2_info_request __user *req) +{ + int status = -EFAULT; + struct ocfs2_info_maxslots oim; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (o2info_from_user(oim, req)) + goto bail; + + oim.im_max_slots = osb->max_slots; + oim.im_req.ir_flags |= OCFS2_INFO_FL_FILLED; + + if (o2info_to_user(oim, req)) + goto bail; + + status = 0; +bail: + if (status) + o2info_set_request_error(oim, req); + + return status; +} + +int ocfs2_info_handle_label(struct inode *inode, + struct ocfs2_info_request __user *req) +{ + int status = -EFAULT; + struct ocfs2_info_label oil; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (o2info_from_user(oil, req)) + goto bail; + + memcpy(oil.il_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN); + oil.il_req.ir_flags |= OCFS2_INFO_FL_FILLED; + + if (o2info_to_user(oil, req)) + goto bail; + + status = 0; +bail: + if (status) + o2info_set_request_error(oil, req); + + return status; +} + +int ocfs2_info_handle_uuid(struct inode *inode, + struct ocfs2_info_request __user *req) +{ + int status = -EFAULT; + struct ocfs2_info_uuid oiu; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (o2info_from_user(oiu, req)) + goto bail; + + memcpy(oiu.iu_uuid_str, osb->uuid_str, OCFS2_TEXT_UUID_LEN + 1); + oiu.iu_req.ir_flags |= OCFS2_INFO_FL_FILLED; + + if (o2info_to_user(oiu, req)) + goto bail; + + status = 0; +bail: + if (status) + o2info_set_request_error(oiu, req); + + return status; +} + +int ocfs2_info_handle_fs_features(struct inode *inode, + struct ocfs2_info_request __user *req) +{ + int status = -EFAULT; + struct ocfs2_info_fs_features oif; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (o2info_from_user(oif, req)) + goto bail; + + oif.if_compat_features = osb->s_feature_compat; + oif.if_incompat_features = osb->s_feature_incompat; + oif.if_ro_compat_features = osb->s_feature_ro_compat; + oif.if_req.ir_flags |= OCFS2_INFO_FL_FILLED; + + if (o2info_to_user(oif, req)) + goto bail; + + status = 0; +bail: + if (status) + o2info_set_request_error(oif, req); + + return status; +} + +int ocfs2_info_handle_journal_size(struct inode *inode, + struct ocfs2_info_request __user *req) +{ + int status = -EFAULT; + struct ocfs2_info_journal_size oij; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (o2info_from_user(oij, req)) + goto bail; + + oij.ij_journal_size = osb->journal->j_inode->i_size; + + oij.ij_req.ir_flags |= OCFS2_INFO_FL_FILLED; + + if (o2info_to_user(oij, req)) + goto bail; + + status = 0; +bail: + if (status) + o2info_set_request_error(oij, req); + + return status; +} + +int ocfs2_info_handle_unknown(struct inode *inode, + struct ocfs2_info_request __user *req) +{ + int status = -EFAULT; + struct ocfs2_info_request oir; + + if (o2info_from_user(oir, req)) + goto bail; + + oir.ir_flags &= ~OCFS2_INFO_FL_FILLED; + + if (o2info_to_user(oir, req)) + goto bail; + + status = 0; +bail: + if (status) + o2info_set_request_error(oir, req); + + return status; +} + +/* + * Validate and distinguish OCFS2_IOC_INFO requests. + * + * - validate the magic number. + * - distinguish different requests. + * - validate size of different requests. + */ +int ocfs2_info_handle_request(struct inode *inode, + struct ocfs2_info_request __user *req) +{ + int status = -EFAULT; + struct ocfs2_info_request oir; + + if (o2info_from_user(oir, req)) + goto bail; + + status = -EINVAL; + if (oir.ir_magic != OCFS2_INFO_MAGIC) + goto bail; + + switch (oir.ir_code) { + case OCFS2_INFO_BLOCKSIZE: + if (oir.ir_size == sizeof(struct ocfs2_info_blocksize)) + status = ocfs2_info_handle_blocksize(inode, req); + break; + case OCFS2_INFO_CLUSTERSIZE: + if (oir.ir_size == sizeof(struct ocfs2_info_clustersize)) + status = ocfs2_info_handle_clustersize(inode, req); + break; + case OCFS2_INFO_MAXSLOTS: + if (oir.ir_size == sizeof(struct ocfs2_info_maxslots)) + status = ocfs2_info_handle_maxslots(inode, req); + break; + case OCFS2_INFO_LABEL: + if (oir.ir_size == sizeof(struct ocfs2_info_label)) + status = ocfs2_info_handle_label(inode, req); + break; + case OCFS2_INFO_UUID: + if (oir.ir_size == sizeof(struct ocfs2_info_uuid)) + status = ocfs2_info_handle_uuid(inode, req); + break; + case OCFS2_INFO_FS_FEATURES: + if (oir.ir_size == sizeof(struct ocfs2_info_fs_features)) + status = ocfs2_info_handle_fs_features(inode, req); + break; + case OCFS2_INFO_JOURNAL_SIZE: + if (oir.ir_size == sizeof(struct ocfs2_info_journal_size)) + status = ocfs2_info_handle_journal_size(inode, req); + break; + default: + status = ocfs2_info_handle_unknown(inode, req); + break; + } + +bail: + return status; +} + +int ocfs2_get_request_ptr(struct ocfs2_info *info, int idx, + u64 *req_addr, int compat_flag) +{ + int status = -EFAULT; + u64 __user *bp = NULL; + + if (compat_flag) { +#ifdef CONFIG_COMPAT + /* + * pointer bp stores the base address of a pointers array, + * which collects all addresses of separate request. + */ + bp = (u64 __user *)(unsigned long)compat_ptr(info->oi_requests); +#else + BUG(); +#endif + } else + bp = (u64 __user *)(unsigned long)(info->oi_requests); + + if (o2info_from_user(*req_addr, bp + idx)) + goto bail; + + status = 0; +bail: + return status; +} + +/* + * OCFS2_IOC_INFO handles an array of requests passed from userspace. + * + * ocfs2_info_handle() recevies a large info aggregation, grab and + * validate the request count from header, then break it into small + * pieces, later specific handlers can handle them one by one. + * + * Idea here is to make each separate request small enough to ensure + * a better backward&forward compatibility, since a small piece of + * request will be less likely to be broken if disk layout get changed. + */ +int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info, + int compat_flag) +{ + int i, status = 0; + u64 req_addr; + struct ocfs2_info_request __user *reqp; + + if ((info->oi_count > OCFS2_INFO_MAX_REQUEST) || + (!info->oi_requests)) { + status = -EINVAL; + goto bail; + } + + for (i = 0; i < info->oi_count; i++) { + + status = ocfs2_get_request_ptr(info, i, &req_addr, compat_flag); + if (status) + break; + + reqp = (struct ocfs2_info_request *)(unsigned long)req_addr; + if (!reqp) { + status = -EINVAL; + goto bail; + } + + status = ocfs2_info_handle_request(inode, reqp); + if (status) + break; + } + +bail: + return status; +} + long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_path.dentry->d_inode; @@ -120,6 +462,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct reflink_arguments args; const char *old_path, *new_path; bool preserve; + struct ocfs2_info info; switch (cmd) { case OCFS2_IOC_GETFLAGS: @@ -174,6 +517,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) preserve = (args.preserve != 0); return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve); + case OCFS2_IOC_INFO: + if (copy_from_user(&info, (struct ocfs2_info __user *)arg, + sizeof(struct ocfs2_info))) + return -EFAULT; + + return ocfs2_info_handle(inode, &info, 0); default: return -ENOTTY; } @@ -185,6 +534,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg) bool preserve; struct reflink_arguments args; struct inode *inode = file->f_path.dentry->d_inode; + struct ocfs2_info info; switch (cmd) { case OCFS2_IOC32_GETFLAGS: @@ -209,6 +559,12 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg) return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path), compat_ptr(args.new_path), preserve); + case OCFS2_IOC_INFO: + if (copy_from_user(&info, (struct ocfs2_info __user *)arg, + sizeof(struct ocfs2_info))) + return -EFAULT; + + return ocfs2_info_handle(inode, &info, 1); default: return -ENOIOCTLCMD; } diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h index 2d3420af1a8..9bc53549986 100644 --- a/fs/ocfs2/ocfs2_ioctl.h +++ b/fs/ocfs2/ocfs2_ioctl.h @@ -76,4 +76,99 @@ struct reflink_arguments { }; #define OCFS2_IOC_REFLINK _IOW('o', 4, struct reflink_arguments) +/* Following definitions dedicated for ocfs2_info_request ioctls. */ +#define OCFS2_INFO_MAX_REQUEST (50) +#define OCFS2_TEXT_UUID_LEN (OCFS2_VOL_UUID_LEN * 2) + +/* Magic number of all requests */ +#define OCFS2_INFO_MAGIC (0x4F32494E) + +/* + * Always try to separate info request into small pieces to + * guarantee the backward&forward compatibility. + */ +struct ocfs2_info { + __u64 oi_requests; /* Array of __u64 pointers to requests */ + __u32 oi_count; /* Number of requests in info_requests */ + __u32 oi_pad; +}; + +struct ocfs2_info_request { +/*00*/ __u32 ir_magic; /* Magic number */ + __u32 ir_code; /* Info request code */ + __u32 ir_size; /* Size of request */ + __u32 ir_flags; /* Request flags */ +/*10*/ /* Request specific fields */ +}; + +struct ocfs2_info_clustersize { + struct ocfs2_info_request ic_req; + __u32 ic_clustersize; + __u32 ic_pad; +}; + +struct ocfs2_info_blocksize { + struct ocfs2_info_request ib_req; + __u32 ib_blocksize; + __u32 ib_pad; +}; + +struct ocfs2_info_maxslots { + struct ocfs2_info_request im_req; + __u32 im_max_slots; + __u32 im_pad; +}; + +struct ocfs2_info_label { + struct ocfs2_info_request il_req; + __u8 il_label[OCFS2_MAX_VOL_LABEL_LEN]; +} __attribute__ ((packed)); + +struct ocfs2_info_uuid { + struct ocfs2_info_request iu_req; + __u8 iu_uuid_str[OCFS2_TEXT_UUID_LEN + 1]; +} __attribute__ ((packed)); + +struct ocfs2_info_fs_features { + struct ocfs2_info_request if_req; + __u32 if_compat_features; + __u32 if_incompat_features; + __u32 if_ro_compat_features; + __u32 if_pad; +}; + +struct ocfs2_info_journal_size { + struct ocfs2_info_request ij_req; + __u64 ij_journal_size; +}; + +/* Codes for ocfs2_info_request */ +enum ocfs2_info_type { + OCFS2_INFO_CLUSTERSIZE = 1, + OCFS2_INFO_BLOCKSIZE, + OCFS2_INFO_MAXSLOTS, + OCFS2_INFO_LABEL, + OCFS2_INFO_UUID, + OCFS2_INFO_FS_FEATURES, + OCFS2_INFO_JOURNAL_SIZE, + OCFS2_INFO_NUM_TYPES +}; + +/* Flags for struct ocfs2_info_request */ +/* Filled by the caller */ +#define OCFS2_INFO_FL_NON_COHERENT (0x00000001) /* Cluster coherency not + required. This is a hint. + It is up to ocfs2 whether + the request can be fulfilled + without locking. */ +/* Filled by ocfs2 */ +#define OCFS2_INFO_FL_FILLED (0x40000000) /* Filesystem understood + this request and + filled in the answer */ + +#define OCFS2_INFO_FL_ERROR (0x80000000) /* Error happened during + request handling. */ + +#define OCFS2_IOC_INFO _IOR('o', 5, struct ocfs2_info) + #endif /* OCFS2_IOCTL_H */ -- cgit v1.2.3-18-g5258 From 3c3f20c9813391ba4004764072989744395cf405 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Tue, 1 Jun 2010 13:58:13 +0800 Subject: ocfs2: Add some trace log for orphan scan. Now orphan scan worker has no trace log, so it is very hard to tell whether it is finished or blocked. So add 2 mlog trace log so that we can tell whether the current orphan scan worker is blocked or not. It does help when I analyzed a orphan scan bug. Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/journal.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 9b57c0350ff..04d41dfeab9 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -1888,6 +1888,8 @@ void ocfs2_queue_orphan_scan(struct ocfs2_super *osb) os = &osb->osb_orphan_scan; + mlog(0, "Begin orphan scan\n"); + if (atomic_read(&os->os_state) == ORPHAN_SCAN_INACTIVE) goto out; @@ -1920,6 +1922,7 @@ void ocfs2_queue_orphan_scan(struct ocfs2_super *osb) unlock: ocfs2_orphan_scan_unlock(osb, seqno); out: + mlog(0, "Orphan scan completed\n"); return; } -- cgit v1.2.3-18-g5258 From 95fa859a268fd7d9bae6f2d4d095e3f61100ac1b Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Wed, 9 Jun 2010 16:48:59 +0800 Subject: ocfs2: Remove obscure error handling in direct_write. In ocfs2, actually we don't allow any direct write pass i_size, see the function ocfs2_prepare_inode_for_write. So we don't need the bogus simple_setsize. Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/file.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 9a03c151b5c..4f9133f6368 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2312,17 +2312,6 @@ relock: written = generic_file_direct_write(iocb, iov, &nr_segs, *ppos, ppos, count, ocount); if (written < 0) { - /* - * direct write may have instantiated a few - * blocks outside i_size. Trim these off again. - * Don't need i_size_read because we hold i_mutex. - * - * XXX(truncate): this looks buggy because ocfs2 did not - * actually implement ->truncate. Take a look at - * the new truncate sequence and update this accordingly - */ - if (*ppos + count > inode->i_size) - truncate_setsize(inode, inode->i_size); ret = written; goto out_dio; } -- cgit v1.2.3-18-g5258 From 83fd9c7f65634ac440a6b9b7a63ba562f213ac60 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Thu, 10 Jun 2010 17:21:36 -0500 Subject: Reorganize data elements to reduce struct sizes Thanks for the comments. I have incorportated them all. CONFIG_OCFS2_FS_STATS is enabled and CONFIG_DEBUG_LOCK_ALLOC is disabled. Statistics now look like - ocfs2_write_ctxt: 2144 - 2136 = 8 ocfs2_inode_info: 1960 - 1848 = 112 ocfs2_journal: 168 - 160 = 8 ocfs2_lock_res: 336 - 304 = 32 ocfs2_refcount_tree: 512 - 472 = 40 Signed-off-by: Goldwyn Rodrigues Signed-off-by: Joel Becker --- fs/ocfs2/aops.c | 2 +- fs/ocfs2/inode.h | 11 ++++------- fs/ocfs2/journal.h | 3 ++- fs/ocfs2/ocfs2.h | 23 +++++++++++++++-------- fs/ocfs2/refcounttree.h | 4 ++-- 5 files changed, 24 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 0de69c9a08b..f477f18b35d 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -883,8 +883,8 @@ struct ocfs2_write_ctxt { * out in so that future reads from that region will get * zero's. */ - struct page *w_pages[OCFS2_MAX_CTXT_PAGES]; unsigned int w_num_pages; + struct page *w_pages[OCFS2_MAX_CTXT_PAGES]; struct page *w_target_page; /* diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 6de5a869db3..0bc477a3aeb 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -46,27 +46,24 @@ struct ocfs2_inode_info /* These fields are protected by ip_lock */ spinlock_t ip_lock; u32 ip_open_count; - u32 ip_clusters; struct list_head ip_io_markers; + u32 ip_clusters; + u16 ip_dyn_features; struct mutex ip_io_mutex; - u32 ip_flags; /* see below */ u32 ip_attr; /* inode attributes */ - u16 ip_dyn_features; /* protected by recovery_lock. */ struct inode *ip_next_orphan; - u32 ip_dir_start_lookup; - struct ocfs2_caching_info ip_metadata_cache; - struct ocfs2_extent_map ip_extent_map; - struct inode vfs_inode; struct jbd2_inode ip_jinode; + u32 ip_dir_start_lookup; + /* Only valid if the inode is the dir. */ u32 ip_last_used_slot; u64 ip_last_used_group; diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index b5baaa8e710..43e56b97f9c 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -67,11 +67,12 @@ struct ocfs2_journal { struct buffer_head *j_bh; /* Journal disk inode block */ atomic_t j_num_trans; /* Number of transactions * currently in the system. */ + spinlock_t j_lock; unsigned long j_trans_id; struct rw_semaphore j_trans_barrier; wait_queue_head_t j_checkpointed; - spinlock_t j_lock; + /* both fields protected by j_lock*/ struct list_head j_la_cleanups; struct work_struct j_recovery_work; }; diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index c67003b6b5a..65739b3b327 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -150,26 +150,33 @@ typedef void (*ocfs2_lock_callback)(int status, unsigned long data); struct ocfs2_lock_res { void *l_priv; struct ocfs2_lock_res_ops *l_ops; - spinlock_t l_lock; + struct list_head l_blocked_list; struct list_head l_mask_waiters; - enum ocfs2_lock_type l_type; unsigned long l_flags; char l_name[OCFS2_LOCK_ID_MAX_LEN]; - int l_level; unsigned int l_ro_holders; unsigned int l_ex_holders; - struct ocfs2_dlm_lksb l_lksb; + unsigned char l_level; + + /* Data packed - type enum ocfs2_lock_type */ + unsigned char l_type; /* used from AST/BAST funcs. */ - enum ocfs2_ast_action l_action; - enum ocfs2_unlock_action l_unlock_action; - int l_requested; - int l_blocking; + /* Data packed - enum type ocfs2_ast_action */ + unsigned char l_action; + /* Data packed - enum type ocfs2_unlock_action */ + unsigned char l_unlock_action; + unsigned char l_requested; + unsigned char l_blocking; unsigned int l_pending_gen; + spinlock_t l_lock; + + struct ocfs2_dlm_lksb l_lksb; + wait_queue_head_t l_event; struct list_head l_debug_list; diff --git a/fs/ocfs2/refcounttree.h b/fs/ocfs2/refcounttree.h index 9983ba1570e..f04892d6175 100644 --- a/fs/ocfs2/refcounttree.h +++ b/fs/ocfs2/refcounttree.h @@ -21,14 +21,14 @@ struct ocfs2_refcount_tree { struct rb_node rf_node; u64 rf_blkno; u32 rf_generation; + struct kref rf_getcnt; struct rw_semaphore rf_sem; struct ocfs2_lock_res rf_lockres; - struct kref rf_getcnt; int rf_removed; /* the following 4 fields are used by caching_info. */ - struct ocfs2_caching_info rf_ci; spinlock_t rf_lock; + struct ocfs2_caching_info rf_ci; struct mutex rf_io_mutex; struct super_block *rf_sb; }; -- cgit v1.2.3-18-g5258 From 4c38881f87c21ada5609a4065cb10e3e74da0d6e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 5 Aug 2010 20:32:46 +0200 Subject: ocfs2: Remove ocfs2_sync_inode() ocfs2_sync_inode() is used only from ocfs2_sync_file(). But all data has already been written before calling ocfs2_sync_file() and ocfs2 doesn't use inode's private_list for tracking metadata buffers thus sync_mapping_buffers() is superfluous as well. Signed-off-by: Jan Kara Acked-by: Mark Fasheh Signed-off-by: Joel Becker --- fs/ocfs2/file.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 4f9133f6368..b03f6601fd7 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -64,12 +64,6 @@ #include "buffer_head_io.h" -static int ocfs2_sync_inode(struct inode *inode) -{ - filemap_fdatawrite(inode->i_mapping); - return sync_mapping_buffers(inode->i_mapping); -} - static int ocfs2_init_file_private(struct inode *inode, struct file *file) { struct ocfs2_file_private *fp; @@ -187,10 +181,6 @@ static int ocfs2_sync_file(struct file *file, int datasync) mlog_entry("(0x%p, 0x%p, %d, '%.*s')\n", file, dentry, datasync, dentry->d_name.len, dentry->d_name.name); - err = ocfs2_sync_inode(dentry->d_inode); - if (err) - goto bail; - if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) { /* * We still have to flush drive's caches to get data to the -- cgit v1.2.3-18-g5258 From f9c57ada32ea3f2e12600cf274035fff063b2e0f Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Mon, 2 Aug 2010 11:02:13 +0800 Subject: ocfs2: Remove unused old_id in ocfs2_commit_cache. Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/journal.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 04d41dfeab9..5787802c2d6 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -301,7 +301,6 @@ static int ocfs2_commit_cache(struct ocfs2_super *osb) { int status = 0; unsigned int flushed; - unsigned long old_id; struct ocfs2_journal *journal = NULL; mlog_entry_void(); @@ -326,7 +325,7 @@ static int ocfs2_commit_cache(struct ocfs2_super *osb) goto finally; } - old_id = ocfs2_inc_trans_id(journal); + ocfs2_inc_trans_id(journal); flushed = atomic_read(&journal->j_num_trans); atomic_set(&journal->j_num_trans, 0); -- cgit v1.2.3-18-g5258 From 17ae521158d6fe89cfdd00a9990aa40d02e81391 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Mon, 2 Aug 2010 11:02:14 +0800 Subject: ocfs2: Remove obsolete comments before ocfs2_start_trans. Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/journal.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 5787802c2d6..faa2303dbf0 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -341,9 +341,6 @@ finally: return status; } -/* pass it NULL and it will allocate a new handle object for you. If - * you pass it a handle however, it may still return error, in which - * case it has free'd the passed handle for you. */ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs) { journal_t *journal = osb->journal->j_journal; -- cgit v1.2.3-18-g5258 From 30ca22c70e3ef0a96ff84de69cd7e8561b416cb2 Mon Sep 17 00:00:00 2001 From: "Patrick J. LoPresti" Date: Thu, 22 Jul 2010 15:03:41 -0700 Subject: ext3/ext4: Factor out disk addressability check As part of adding support for OCFS2 to mount huge volumes, we need to check that the sector_t and page cache of the system are capable of addressing the entire volume. An identical check already appears in ext3 and ext4. This patch moves the addressability check into its own function in fs/libfs.c and modifies ext3 and ext4 to invoke it. [Edited to -EINVAL instead of BUG_ON() for bad blocksize_bits -- Joel] Signed-off-by: Patrick LoPresti Cc: linux-ext4@vger.kernel.org Acked-by: Andreas Dilger Signed-off-by: Joel Becker --- fs/ext3/super.c | 4 ++-- fs/ext4/super.c | 8 +++----- fs/libfs.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 5dbf4dba03c..a367dd04428 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -1849,8 +1849,8 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) goto failed_mount; } - if (le32_to_cpu(es->s_blocks_count) > - (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) { + if (generic_check_addressable(sb->s_blocksize_bits, + le32_to_cpu(es->s_blocks_count))) { ext3_msg(sb, KERN_ERR, "error: filesystem is too large to mount safely"); if (sizeof(sector_t) < 8) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 26147746c27..7f47c366bf1 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2831,15 +2831,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) * Test whether we have more sectors than will fit in sector_t, * and whether the max offset is addressable by the page cache. */ - if ((ext4_blocks_count(es) > - (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) || - (ext4_blocks_count(es) > - (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - sb->s_blocksize_bits))) { + ret = generic_check_addressable(sb->s_blocksize_bits, + ext4_blocks_count(es)); + if (ret) { ext4_msg(sb, KERN_ERR, "filesystem" " too large to mount safely on this system"); if (sizeof(sector_t) < 8) ext4_msg(sb, KERN_WARNING, "CONFIG_LBDAF not enabled"); - ret = -EFBIG; goto failed_mount; } diff --git a/fs/libfs.c b/fs/libfs.c index 0a9da95317f..8debe7b3376 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -913,6 +913,35 @@ int generic_file_fsync(struct file *file, int datasync) } EXPORT_SYMBOL(generic_file_fsync); +/** + * generic_check_addressable - Check addressability of file system + * @blocksize_bits: log of file system block size + * @num_blocks: number of blocks in file system + * + * Determine whether a file system with @num_blocks blocks (and a + * block size of 2**@blocksize_bits) is addressable by the sector_t + * and page cache of the system. Return 0 if so and -EFBIG otherwise. + */ +int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) +{ + u64 last_fs_block = num_blocks - 1; + + if (unlikely(num_blocks == 0)) + return 0; + + if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT)) + return -EINVAL; + + if ((last_fs_block > + (sector_t)(~0ULL) >> (blocksize_bits - 9)) || + (last_fs_block > + (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { + return -EFBIG; + } + return 0; +} +EXPORT_SYMBOL(generic_check_addressable); + /* * No-op implementation of ->fsync for in-memory filesystems. */ -- cgit v1.2.3-18-g5258 From 1113e1b504f6e8d4364c0b73c9097828067d4617 Mon Sep 17 00:00:00 2001 From: "Patrick J. LoPresti" Date: Thu, 22 Jul 2010 15:04:16 -0700 Subject: JBD2: Allow feature checks before journal recovery Before we start accessing a huge (> 16 TiB) OCFS2 volume, we need to confirm that its journal supports 64-bit offsets. In particular, we need to check the journal's feature bits before recovering the journal. This is not possible with JBD2 at present, because the journal superblock (where the feature bits reside) is not loaded from disk until the journal is recovered. This patch loads the journal superblock in jbd2_journal_check_used_features() if it has not already been loaded, allowing us to check the feature bits before journal recovery. Signed-off-by: Patrick LoPresti Cc: linux-ext4@vger.kernel.org Acked-by: "Theodore Ts'o" Signed-off-by: Joel Becker --- fs/jbd2/journal.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 0e8014ea6b9..262419f83d8 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1371,6 +1371,10 @@ int jbd2_journal_check_used_features (journal_t *journal, unsigned long compat, if (!compat && !ro && !incompat) return 1; + /* Load journal superblock if it is not loaded yet. */ + if (journal->j_format_version == 0 && + journal_get_superblock(journal) != 0) + return 0; if (journal->j_format_version == 1) return 0; -- cgit v1.2.3-18-g5258 From 3bdb8efd94a73bb137e3315cd831cbc874052b4b Mon Sep 17 00:00:00 2001 From: "Patrick J. LoPresti" Date: Thu, 22 Jul 2010 15:05:57 -0700 Subject: OCFS2: Allow huge (> 16 TiB) volumes to mount The OCFS2 developers have already done all of the hard work to allow volumes larger than 16 TiB. But there is still a "sanity check" in fs/ocfs2/super.c that prevents the mounting of such volumes, even when the cluster size and journal options would allow it. This patch replaces that sanity check with a more sophisticated one to mount a huge volume provided that (a) it is addressable by the raw word/address size of the system (borrowing a test from ext4); (b) the volume is using JBD2; and (c) the JBD2_FEATURE_INCOMPAT_64BIT flag is set on the journal. I factored out the sanity check into its own function. I also moved it from ocfs2_initialize_super() down to ocfs2_check_volume(); any earlier, and the journal will not have been initialized yet. This patch is one of a pair, and it depends on the other ("JBD2: Allow feature checks before journal recovery"). I have tested this patch on small volumes, huge volumes, and huge volumes without 64-bit block support in the journal. All of them appear to work or to fail gracefully, as appropriate. Signed-off-by: Patrick LoPresti Signed-off-by: Joel Becker --- fs/ocfs2/super.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index fa1be1b304d..47415398d56 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1990,6 +1990,36 @@ static int ocfs2_setup_osb_uuid(struct ocfs2_super *osb, const unsigned char *uu return 0; } +/* Make sure entire volume is addressable by our journal. Requires + osb_clusters_at_boot to be valid and for the journal to have been + initialized by ocfs2_journal_init(). */ +static int ocfs2_journal_addressable(struct ocfs2_super *osb) +{ + int status = 0; + u64 max_block = + ocfs2_clusters_to_blocks(osb->sb, + osb->osb_clusters_at_boot) - 1; + + /* 32-bit block number is always OK. */ + if (max_block <= (u32)~0ULL) + goto out; + + /* Volume is "huge", so see if our journal is new enough to + support it. */ + if (!(OCFS2_HAS_COMPAT_FEATURE(osb->sb, + OCFS2_FEATURE_COMPAT_JBD2_SB) && + jbd2_journal_check_used_features(osb->journal->j_journal, 0, 0, + JBD2_FEATURE_INCOMPAT_64BIT))) { + mlog(ML_ERROR, "The journal cannot address the entire volume. " + "Enable the 'block64' journal option with tunefs.ocfs2"); + status = -EFBIG; + goto out; + } + + out: + return status; +} + static int ocfs2_initialize_super(struct super_block *sb, struct buffer_head *bh, int sector_size, @@ -2002,6 +2032,7 @@ static int ocfs2_initialize_super(struct super_block *sb, struct ocfs2_journal *journal; __le32 uuid_net_key; struct ocfs2_super *osb; + u64 total_blocks; mlog_entry_void(); @@ -2214,11 +2245,15 @@ static int ocfs2_initialize_super(struct super_block *sb, goto bail; } - if (ocfs2_clusters_to_blocks(osb->sb, le32_to_cpu(di->i_clusters) - 1) - > (u32)~0UL) { - mlog(ML_ERROR, "Volume might try to write to blocks beyond " - "what jbd can address in 32 bits.\n"); - status = -EINVAL; + total_blocks = ocfs2_clusters_to_blocks(osb->sb, + le32_to_cpu(di->i_clusters)); + + status = generic_check_addressable(osb->sb->s_blocksize_bits, + total_blocks); + if (status) { + mlog(ML_ERROR, "Volume too large " + "to mount safely on this system"); + status = -EFBIG; goto bail; } @@ -2380,6 +2415,12 @@ static int ocfs2_check_volume(struct ocfs2_super *osb) goto finally; } + /* Now that journal has been initialized, check to make sure + entire volume is addressable. */ + status = ocfs2_journal_addressable(osb); + if (status) + goto finally; + /* If the journal was unmounted cleanly then we don't want to * recover anything. Otherwise, journal_load will do that * dirty work for us :) */ -- cgit v1.2.3-18-g5258 From a33f13efe05192e7a805018a2ce2b2afddd04057 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Mon, 16 Aug 2010 12:10:17 -0700 Subject: libfs: Fix shift bug in generic_check_addressable() generic_check_addressable() erroneously shifts pages down by a block factor when it should be shifting up. To prevent overflow, we shift blocks down to pages. Signed-off-by: Joel Becker --- fs/libfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/libfs.c b/fs/libfs.c index 8debe7b3376..62baa0387d6 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -925,6 +925,8 @@ EXPORT_SYMBOL(generic_file_fsync); int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) { u64 last_fs_block = num_blocks - 1; + u64 last_fs_page = + last_fs_block >> (PAGE_CACHE_SHIFT - blocksize_bits); if (unlikely(num_blocks == 0)) return 0; @@ -932,10 +934,8 @@ int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks) if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT)) return -EINVAL; - if ((last_fs_block > - (sector_t)(~0ULL) >> (blocksize_bits - 9)) || - (last_fs_block > - (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - blocksize_bits))) { + if ((last_fs_block > (sector_t)(~0ULL) >> (blocksize_bits - 9)) || + (last_fs_page > (pgoff_t)(~0ULL))) { return -EFBIG; } return 0; -- cgit v1.2.3-18-g5258 From b4d693fcc5fe99ed211addb5c6a0f8398f0b266e Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Mon, 16 Aug 2010 16:58:21 +0800 Subject: ocfs2: Cache system inodes of other slots. Durring orphan scan, if we are slot 0, and we are replaying orphan_dir:0001, the general process is that for every file in this dir: 1. we will iget orphan_dir:0001, since there is no inode for it. we will have to create an inode and read it from the disk. 2. do the normal work, such as delete_inode and remove it from the dir if it is allowed. 3. call iput orphan_dir:0001 when we are done. In this case, since we have no dcache for this inode, i_count will reach 0, and VFS will have to call clear_inode and in ocfs2_clear_inode we will checkpoint the inode which will let ocfs2_cmt and journald begin to work. 4. We loop back to 1 for the next file. So you see, actually for every deleted file, we have to read the orphan dir from the disk and checkpoint the journal. It is very time consuming and cause a lot of journal checkpoint I/O. A better solution is that we can have another reference for these inodes in ocfs2_super. So if there is no other race among nodes(which will let dlmglue to checkpoint the inode), for step 3, clear_inode won't be called and for step 1, we may only need to read the inode for the 1st time. This is a big win for us. So this patch will try to cache system inodes of other slots so that we will have one more reference for these inodes and avoid the extra inode read and journal checkpoint. Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/ocfs2.h | 3 ++- fs/ocfs2/ocfs2_fs.h | 5 +++++ fs/ocfs2/super.c | 20 +++++++++++++++--- fs/ocfs2/sysfile.c | 60 +++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 73 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 65739b3b327..687e291d73f 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -284,7 +284,8 @@ struct ocfs2_super struct super_block *sb; struct inode *root_inode; struct inode *sys_root_inode; - struct inode *system_inodes[NUM_SYSTEM_INODES]; + struct inode *global_system_inodes[NUM_GLOBAL_SYSTEM_INODES]; + struct inode **local_system_inodes; struct ocfs2_slot_info *slot_info; diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index 33f1c9a8258..723b20dac41 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -309,6 +309,7 @@ enum { USER_QUOTA_SYSTEM_INODE, GROUP_QUOTA_SYSTEM_INODE, #define OCFS2_LAST_GLOBAL_SYSTEM_INODE GROUP_QUOTA_SYSTEM_INODE +#define OCFS2_FIRST_LOCAL_SYSTEM_INODE ORPHAN_DIR_SYSTEM_INODE ORPHAN_DIR_SYSTEM_INODE, EXTENT_ALLOC_SYSTEM_INODE, INODE_ALLOC_SYSTEM_INODE, @@ -317,8 +318,12 @@ enum { TRUNCATE_LOG_SYSTEM_INODE, LOCAL_USER_QUOTA_SYSTEM_INODE, LOCAL_GROUP_QUOTA_SYSTEM_INODE, +#define OCFS2_LAST_LOCAL_SYSTEM_INODE LOCAL_GROUP_QUOTA_SYSTEM_INODE NUM_SYSTEM_INODES }; +#define NUM_GLOBAL_SYSTEM_INODES OCFS2_LAST_GLOBAL_SYSTEM_INODE +#define NUM_LOCAL_SYSTEM_INODES \ + (NUM_SYSTEM_INODES - OCFS2_FIRST_LOCAL_SYSTEM_INODE) static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = { /* Global system inodes (single copy) */ diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 47415398d56..350e8b5a939 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -514,11 +514,11 @@ static void ocfs2_release_system_inodes(struct ocfs2_super *osb) mlog_entry_void(); - for (i = 0; i < NUM_SYSTEM_INODES; i++) { - inode = osb->system_inodes[i]; + for (i = 0; i < NUM_GLOBAL_SYSTEM_INODES; i++) { + inode = osb->global_system_inodes[i]; if (inode) { iput(inode); - osb->system_inodes[i] = NULL; + osb->global_system_inodes[i] = NULL; } } @@ -534,6 +534,20 @@ static void ocfs2_release_system_inodes(struct ocfs2_super *osb) osb->root_inode = NULL; } + if (!osb->local_system_inodes) + goto out; + + for (i = 0; i < NUM_LOCAL_SYSTEM_INODES * osb->max_slots; i++) { + if (osb->local_system_inodes[i]) { + iput(osb->local_system_inodes[i]); + osb->local_system_inodes[i] = NULL; + } + } + + kfree(osb->local_system_inodes); + osb->local_system_inodes = NULL; + +out: mlog_exit(0); } diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c index bfe7190cdbf..902efb23b6a 100644 --- a/fs/ocfs2/sysfile.c +++ b/fs/ocfs2/sysfile.c @@ -44,11 +44,6 @@ static struct inode * _ocfs2_get_system_file_inode(struct ocfs2_super *osb, int type, u32 slot); -static inline int is_global_system_inode(int type); -static inline int is_in_system_inode_array(struct ocfs2_super *osb, - int type, - u32 slot); - #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key ocfs2_sysfile_cluster_lock_key[NUM_SYSTEM_INODES]; #endif @@ -59,11 +54,52 @@ static inline int is_global_system_inode(int type) type <= OCFS2_LAST_GLOBAL_SYSTEM_INODE; } -static inline int is_in_system_inode_array(struct ocfs2_super *osb, - int type, - u32 slot) +static struct inode **get_local_system_inode(struct ocfs2_super *osb, + int type, + u32 slot) { - return slot == osb->slot_num || is_global_system_inode(type); + int index; + struct inode **local_system_inodes, **free = NULL; + + BUG_ON(slot == OCFS2_INVALID_SLOT); + BUG_ON(type < OCFS2_FIRST_LOCAL_SYSTEM_INODE || + type > OCFS2_LAST_LOCAL_SYSTEM_INODE); + + spin_lock(&osb->osb_lock); + local_system_inodes = osb->local_system_inodes; + spin_unlock(&osb->osb_lock); + + if (unlikely(!local_system_inodes)) { + local_system_inodes = kzalloc(sizeof(struct inode *) * + NUM_LOCAL_SYSTEM_INODES * + osb->max_slots, + GFP_NOFS); + if (!local_system_inodes) { + mlog_errno(-ENOMEM); + /* + * return NULL here so that ocfs2_get_sytem_file_inodes + * will try to create an inode and use it. We will try + * to initialize local_system_inodes next time. + */ + return NULL; + } + + spin_lock(&osb->osb_lock); + if (osb->local_system_inodes) { + /* Someone has initialized it for us. */ + free = local_system_inodes; + local_system_inodes = osb->local_system_inodes; + } else + osb->local_system_inodes = local_system_inodes; + spin_unlock(&osb->osb_lock); + if (unlikely(free)) + kfree(free); + } + + index = (slot * NUM_LOCAL_SYSTEM_INODES) + + (type - OCFS2_FIRST_LOCAL_SYSTEM_INODE); + + return &local_system_inodes[index]; } struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, @@ -74,8 +110,10 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, struct inode **arr = NULL; /* avoid the lookup if cached in local system file array */ - if (is_in_system_inode_array(osb, type, slot)) - arr = &(osb->system_inodes[type]); + if (is_global_system_inode(type)) { + arr = &(osb->global_system_inodes[type]); + } else + arr = get_local_system_inode(osb, type, slot); if (arr && ((inode = *arr) != NULL)) { /* get a ref in addition to the array ref */ -- cgit v1.2.3-18-g5258 From 5e98d492406818e6a94c0ba54c61f59d40cefa4a Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Mon, 28 Jun 2010 10:04:32 -0500 Subject: Track negative entries v3 Track negative dentries by recording the generation number of the parent directory in d_fsdata. The generation number for the parent directory is recorded in the inode_info, which increments every time the lock on the directory is dropped. If the generation number of the parent directory and the negative dentry matches, there is no need to perform the revalidate, else a revalidate is forced. This improves performance in situations where nodes look for the same non-existent file multiple times. Thanks Mark for explaining the DLM sequence. Signed-off-by: Goldwyn Rodrigues Signed-off-by: Joel Becker --- fs/ocfs2/dcache.c | 33 +++++++++++++++++++++++++++++---- fs/ocfs2/dcache.h | 1 + fs/ocfs2/dlmglue.c | 8 ++++++++ fs/ocfs2/inode.c | 1 + fs/ocfs2/inode.h | 1 + fs/ocfs2/namei.c | 3 ++- 6 files changed, 42 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c index b4957c7d9fe..edaded48e7e 100644 --- a/fs/ocfs2/dcache.c +++ b/fs/ocfs2/dcache.c @@ -40,6 +40,14 @@ #include "inode.h" #include "super.h" +void ocfs2_dentry_attach_gen(struct dentry *dentry) +{ + unsigned long gen = + OCFS2_I(dentry->d_parent->d_inode)->ip_dir_lock_gen; + BUG_ON(dentry->d_inode); + dentry->d_fsdata = (void *)gen; +} + static int ocfs2_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) @@ -51,11 +59,20 @@ static int ocfs2_dentry_revalidate(struct dentry *dentry, mlog_entry("(0x%p, '%.*s')\n", dentry, dentry->d_name.len, dentry->d_name.name); - /* Never trust a negative dentry - force a new lookup. */ + /* For a negative dentry - + * check the generation number of the parent and compare with the + * one stored in the inode. + */ if (inode == NULL) { - mlog(0, "negative dentry: %.*s\n", dentry->d_name.len, - dentry->d_name.name); - goto bail; + unsigned long gen = (unsigned long) dentry->d_fsdata; + unsigned long pgen = + OCFS2_I(dentry->d_parent->d_inode)->ip_dir_lock_gen; + mlog(0, "negative dentry: %.*s parent gen: %lu " + "dentry gen: %lu\n", + dentry->d_name.len, dentry->d_name.name, pgen, gen); + if (gen != pgen) + goto bail; + goto valid; } BUG_ON(!osb); @@ -96,6 +113,7 @@ static int ocfs2_dentry_revalidate(struct dentry *dentry, goto bail; } +valid: ret = 1; bail: @@ -227,6 +245,12 @@ int ocfs2_dentry_attach_lock(struct dentry *dentry, if (!inode) return 0; + if (!dentry->d_inode && dentry->d_fsdata) { + /* Converting a negative dentry to positive + Clear dentry->d_fsdata */ + dentry->d_fsdata = dl = NULL; + } + if (dl) { mlog_bug_on_msg(dl->dl_parent_blkno != parent_blkno, " \"%.*s\": old parent: %llu, new: %llu\n", @@ -452,6 +476,7 @@ static void ocfs2_dentry_iput(struct dentry *dentry, struct inode *inode) out: iput(inode); + ocfs2_dentry_attach_gen(dentry); } /* diff --git a/fs/ocfs2/dcache.h b/fs/ocfs2/dcache.h index f5dd1789acf..b79eff70995 100644 --- a/fs/ocfs2/dcache.h +++ b/fs/ocfs2/dcache.h @@ -64,5 +64,6 @@ void ocfs2_dentry_move(struct dentry *dentry, struct dentry *target, struct inode *old_dir, struct inode *new_dir); extern spinlock_t dentry_attach_lock; +void ocfs2_dentry_attach_gen(struct dentry *dentry); #endif /* OCFS2_DCACHE_H */ diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 5e02a893f46..e8d94d722ec 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -3635,10 +3635,18 @@ static int ocfs2_data_convert_worker(struct ocfs2_lock_res *lockres, { struct inode *inode; struct address_space *mapping; + struct ocfs2_inode_info *oi; inode = ocfs2_lock_res_inode(lockres); mapping = inode->i_mapping; + if (S_ISDIR(inode->i_mode)) { + oi = OCFS2_I(inode); + oi->ip_dir_lock_gen++; + mlog(0, "generation: %u\n", oi->ip_dir_lock_gen); + goto out; + } + if (!S_ISREG(inode->i_mode)) goto out; diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index eece3e05d9d..f935fd6600d 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -335,6 +335,7 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, else inode->i_fop = &ocfs2_dops_no_plocks; i_size_write(inode, le64_to_cpu(fe->i_size)); + OCFS2_I(inode)->ip_dir_lock_gen = 1; break; case S_IFLNK: if (ocfs2_inode_is_fast_symlink(inode)) diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 0bc477a3aeb..1c508b149b3 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -67,6 +67,7 @@ struct ocfs2_inode_info /* Only valid if the inode is the dir. */ u32 ip_last_used_slot; u64 ip_last_used_group; + u32 ip_dir_lock_gen; struct ocfs2_alloc_reservation ip_la_data_resv; }; diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index a00dda2e4f1..e7bde21149a 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -171,7 +171,8 @@ bail_add: ret = ERR_PTR(status); goto bail_unlock; } - } + } else + ocfs2_dentry_attach_gen(dentry); bail_unlock: /* Don't drop the cluster lock until *after* the d_add -- -- cgit v1.2.3-18-g5258 From 6d1d8050b4bc89d0165d29b58e894aeba2564a97 Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Tue, 31 Aug 2010 15:47:05 -0500 Subject: block, partition: add partition_meta_info to hd_struct I'm reposting this patch series as v4 since there have been no additional comments, and I cleaned up one extra bit of unneeded code (in 3/3). The patches are against Linus's tree: 2bfc96a127bc1cc94d26bfaa40159966064f9c8c (2.6.36-rc3). Would this patchset be suitable for inclusion in an mm branch? This changes adds a partition_meta_info struct which itself contains a union of structures that provide partition table specific metadata. This change leaves the union empty. The subsequent patch includes an implementation for CONFIG_EFI_PARTITION-based metadata. Signed-off-by: Will Drewry Signed-off-by: Jens Axboe --- fs/partitions/check.c | 23 ++++++++++++++++++++--- fs/partitions/check.h | 3 +++ 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 79fbf3f390f..6dfbee03ccc 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -352,6 +352,7 @@ static void part_release(struct device *dev) { struct hd_struct *p = dev_to_part(dev); free_part_stats(p); + free_part_info(p); kfree(p); } @@ -401,7 +402,8 @@ static DEVICE_ATTR(whole_disk, S_IRUSR | S_IRGRP | S_IROTH, whole_disk_show, NULL); struct hd_struct *add_partition(struct gendisk *disk, int partno, - sector_t start, sector_t len, int flags) + sector_t start, sector_t len, int flags, + struct partition_meta_info *info) { struct hd_struct *p; dev_t devt = MKDEV(0, 0); @@ -438,6 +440,14 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, p->partno = partno; p->policy = get_disk_ro(disk); + if (info) { + struct partition_meta_info *pinfo = alloc_part_info(disk); + if (!pinfo) + goto out_free_stats; + memcpy(pinfo, info, sizeof(*info)); + p->info = pinfo; + } + dname = dev_name(ddev); if (isdigit(dname[strlen(dname) - 1])) dev_set_name(pdev, "%sp%d", dname, partno); @@ -451,7 +461,7 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, err = blk_alloc_devt(p, &devt); if (err) - goto out_free_stats; + goto out_free_info; pdev->devt = devt; /* delay uevent until 'holders' subdir is created */ @@ -481,6 +491,8 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, return p; +out_free_info: + free_part_info(p); out_free_stats: free_part_stats(p); out_free: @@ -642,6 +654,7 @@ rescan: /* add partitions */ for (p = 1; p < state->limit; p++) { sector_t size, from; + struct partition_meta_info *info = NULL; size = state->parts[p].size; if (!size) @@ -675,8 +688,12 @@ rescan: size = get_capacity(disk) - from; } } + + if (state->parts[p].has_info) + info = &state->parts[p].info; part = add_partition(disk, p, from, size, - state->parts[p].flags); + state->parts[p].flags, + &state->parts[p].info); if (IS_ERR(part)) { printk(KERN_ERR " %s: p%d could not be added: %ld\n", disk->disk_name, p, -PTR_ERR(part)); diff --git a/fs/partitions/check.h b/fs/partitions/check.h index 8e4e103ba21..d68bf4dc3bc 100644 --- a/fs/partitions/check.h +++ b/fs/partitions/check.h @@ -1,5 +1,6 @@ #include #include +#include /* * add_gd_partition adds a partitions details to the devices partition @@ -12,6 +13,8 @@ struct parsed_partitions { sector_t from; sector_t size; int flags; + bool has_info; + struct partition_meta_info info; } parts[DISK_MAX_PARTS]; int next; int limit; -- cgit v1.2.3-18-g5258 From eec7ecfede74bb996060efefd5c157acd5794e8a Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Tue, 31 Aug 2010 15:47:06 -0500 Subject: genhd, efi: add efi partition metadata to hd_structs This change extends the partition_meta_info structure to support EFI GPT-specific metadata and ensures that data is copied in on partition scanning. Signed-off-by: Will Drewry Signed-off-by: Jens Axboe --- fs/partitions/efi.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'fs') diff --git a/fs/partitions/efi.c b/fs/partitions/efi.c index dbb44d4bb8a..ac0ccb5026a 100644 --- a/fs/partitions/efi.c +++ b/fs/partitions/efi.c @@ -94,6 +94,7 @@ * ************************************************************/ #include +#include #include #include #include "check.h" @@ -604,6 +605,7 @@ int efi_partition(struct parsed_partitions *state) gpt_entry *ptes = NULL; u32 i; unsigned ssz = bdev_logical_block_size(state->bdev) / 512; + u8 unparsed_guid[37]; if (!find_valid_gpt(state, &gpt, &ptes) || !gpt || !ptes) { kfree(gpt); @@ -614,6 +616,9 @@ int efi_partition(struct parsed_partitions *state) pr_debug("GUID Partition Table is valid! Yea!\n"); for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < state->limit-1; i++) { + struct partition_meta_info *info; + unsigned label_count = 0; + unsigned label_max; u64 start = le64_to_cpu(ptes[i].starting_lba); u64 size = le64_to_cpu(ptes[i].ending_lba) - le64_to_cpu(ptes[i].starting_lba) + 1ULL; @@ -627,6 +632,26 @@ int efi_partition(struct parsed_partitions *state) if (!efi_guidcmp(ptes[i].partition_type_guid, PARTITION_LINUX_RAID_GUID)) state->parts[i + 1].flags = ADDPART_FLAG_RAID; + + info = &state->parts[i + 1].info; + /* Instead of doing a manual swap to big endian, reuse the + * common ASCII hex format as the interim. + */ + efi_guid_unparse(&ptes[i].unique_partition_guid, unparsed_guid); + part_pack_uuid(unparsed_guid, info->uuid); + + /* Naively convert UTF16-LE to 7 bits. */ + label_max = min(sizeof(info->volname) - 1, + sizeof(ptes[i].partition_name)); + info->volname[label_max] = 0; + while (label_count < label_max) { + u8 c = ptes[i].partition_name[label_count] & 0xff; + if (c && !isprint(c)) + c = '!'; + info->volname[label_count] = c; + label_count++; + } + state->parts[i + 1].has_info = true; } kfree(ptes); kfree(gpt); -- cgit v1.2.3-18-g5258 From c0e1a3e80dc030cd54f06416efbf9e439bf6ecb7 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Wed, 15 Sep 2010 16:56:54 -0700 Subject: ocfs2: Silence unused warning. When CONFIG_OCFS2_DEBUG_MASKLOG is undefined, we don't use the dentry variable in ocfs2_sync_file(). Let's just move all access to the dentry inside the logging call. Signed-off-by: Joel Becker --- fs/ocfs2/file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 9a74542e1a0..13af9937bdd 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -174,12 +174,12 @@ static int ocfs2_sync_file(struct file *file, int datasync) { int err = 0; journal_t *journal; - struct dentry *dentry = file->f_path.dentry; struct inode *inode = file->f_mapping->host; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - mlog_entry("(0x%p, 0x%p, %d, '%.*s')\n", file, dentry, datasync, - dentry->d_name.len, dentry->d_name.name); + mlog_entry("(0x%p, %d, 0x%p, '%.*s')\n", file, datasync, + file->f_path.dentry, file->f_path.dentry->d_name.len, + file->f_path.dentry->d_name.name); if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) { /* -- cgit v1.2.3-18-g5258 From 93f3b86fb1bd0ad7b4a5eb1ad1fdae2b290633b7 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Wed, 15 Sep 2010 17:00:58 -0700 Subject: ocfs2: Initialize the bktcnt variable properly, and call it bucket_count Signed-off-by: Joel Becker --- fs/ocfs2/dlm/dlmdebug.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index 5efdd37dfe4..26ff2e185b1 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -493,7 +493,7 @@ static int debug_mle_print(struct dlm_ctxt *dlm, struct debug_buffer *db) struct hlist_head *bucket; struct hlist_node *list; int i, out = 0; - unsigned long total = 0, longest = 0, bktcnt; + unsigned long total = 0, longest = 0, bucket_count = 0; out += snprintf(db->buf + out, db->len - out, "Dumping MLEs for Domain: %s\n", dlm->name); @@ -505,13 +505,13 @@ static int debug_mle_print(struct dlm_ctxt *dlm, struct debug_buffer *db) mle = hlist_entry(list, struct dlm_master_list_entry, master_hash_node); ++total; - ++bktcnt; + ++bucket_count; if (db->len - out < 200) continue; out += dump_mle(mle, db->buf + out, db->len - out); } - longest = max(longest, bktcnt); - bktcnt = 0; + longest = max(longest, bucket_count); + bucket_count = 0; } spin_unlock(&dlm->master_lock); -- cgit v1.2.3-18-g5258 From dd3932eddf428571762596e17b65f5dc92ca361b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 16 Sep 2010 20:51:46 +0200 Subject: block: remove BLKDEV_IFL_WAIT All the blkdev_issue_* helpers can only sanely be used for synchronous caller. To issue cache flushes or barriers asynchronously the caller needs to set up a bio by itself with a completion callback to move the asynchronous state machine ahead. So drop the BLKDEV_IFL_WAIT flag that is always specified when calling blkdev_issue_* and also remove the now unused flags argument to blkdev_issue_flush and blkdev_issue_zeroout. For blkdev_issue_discard we need to keep it for the secure discard flag, which gains a more descriptive name and loses the bitops vs flag confusion. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/block_dev.c | 2 +- fs/btrfs/extent-tree.c | 3 +-- fs/ext3/fsync.c | 3 +-- fs/ext4/fsync.c | 5 ++--- fs/ext4/mballoc.c | 3 +-- fs/fat/fatent.c | 3 +-- fs/gfs2/rgrp.c | 5 ++--- fs/jbd2/checkpoint.c | 3 +-- fs/jbd2/commit.c | 6 ++---- fs/nilfs2/the_nilfs.c | 4 ++-- fs/reiserfs/file.c | 3 +-- fs/xfs/linux-2.6/xfs_super.c | 3 +-- 12 files changed, 16 insertions(+), 27 deletions(-) (limited to 'fs') diff --git a/fs/block_dev.c b/fs/block_dev.c index 50e8c8582fa..b737451e2e9 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -370,7 +370,7 @@ int blkdev_fsync(struct file *filp, int datasync) */ mutex_unlock(&bd_inode->i_mutex); - error = blkdev_issue_flush(bdev, GFP_KERNEL, NULL, BLKDEV_IFL_WAIT); + error = blkdev_issue_flush(bdev, GFP_KERNEL, NULL); if (error == -EOPNOTSUPP) error = 0; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 43dc9ea9aef..0b81ecdb101 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -1695,8 +1695,7 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans, static void btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len) { - blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL, - BLKDEV_IFL_WAIT); + blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL, 0); } static int btrfs_discard_extent(struct btrfs_root *root, u64 bytenr, diff --git a/fs/ext3/fsync.c b/fs/ext3/fsync.c index d7e9f74dc3a..09b13bb34c9 100644 --- a/fs/ext3/fsync.c +++ b/fs/ext3/fsync.c @@ -90,7 +90,6 @@ int ext3_sync_file(struct file *file, int datasync) * storage */ if (needs_barrier) - blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL, - BLKDEV_IFL_WAIT); + blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); return ret; } diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c index 592adf2e546..3f3ff5ee8f9 100644 --- a/fs/ext4/fsync.c +++ b/fs/ext4/fsync.c @@ -128,10 +128,9 @@ int ext4_sync_file(struct file *file, int datasync) (journal->j_fs_dev != journal->j_dev) && (journal->j_flags & JBD2_BARRIER)) blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, - NULL, BLKDEV_IFL_WAIT); + NULL); ret = jbd2_log_wait_commit(journal, commit_tid); } else if (journal->j_flags & JBD2_BARRIER) - blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL, - BLKDEV_IFL_WAIT); + blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); return ret; } diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index a22bfef3da9..19aa0d44d82 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2566,8 +2566,7 @@ static inline void ext4_issue_discard(struct super_block *sb, discard_block = block + ext4_group_first_block_no(sb, block_group); trace_ext4_discard_blocks(sb, (unsigned long long) discard_block, count); - ret = sb_issue_discard(sb, discard_block, count, GFP_NOFS, - BLKDEV_IFL_WAIT); + ret = sb_issue_discard(sb, discard_block, count, GFP_NOFS, 0); if (ret == EOPNOTSUPP) { ext4_warning(sb, "discard not supported, disabling"); clear_opt(EXT4_SB(sb)->s_mount_opt, DISCARD); diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index f9a0b7ae864..b47d2c9f4fa 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -578,8 +578,7 @@ int fat_free_clusters(struct inode *inode, int cluster) sb_issue_discard(sb, fat_clus_to_blknr(sbi, first_cl), nr_clus * sbi->sec_per_clus, - GFP_NOFS, - BLKDEV_IFL_WAIT); + GFP_NOFS, 0); first_cl = cluster; } diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 37931647291..38b3ea1abac 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -854,7 +854,7 @@ static void gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset, if ((start + nr_sects) != blk) { rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS, - BLKDEV_IFL_WAIT); + 0); if (rv) goto fail; nr_sects = 0; @@ -868,8 +868,7 @@ start_new_extent: } } if (nr_sects) { - rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS, - BLKDEV_IFL_WAIT); + rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS, 0); if (rv) goto fail; } diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 5247e7ffdcb..6571a056e55 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -532,8 +532,7 @@ int jbd2_cleanup_journal_tail(journal_t *journal) */ if ((journal->j_fs_dev != journal->j_dev) && (journal->j_flags & JBD2_BARRIER)) - blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL, - BLKDEV_IFL_WAIT); + blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL); if (!(journal->j_flags & JBD2_ABORT)) jbd2_journal_update_superblock(journal, 1); return 0; diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index f204e27f44d..cb43c605cfa 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -684,8 +684,7 @@ start_journal_io: if (commit_transaction->t_flushed_data_blocks && (journal->j_fs_dev != journal->j_dev) && (journal->j_flags & JBD2_BARRIER)) - blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL, - BLKDEV_IFL_WAIT); + blkdev_issue_flush(journal->j_fs_dev, GFP_KERNEL, NULL); /* Done it all: now write the commit record asynchronously. */ if (JBD2_HAS_INCOMPAT_FEATURE(journal, @@ -810,8 +809,7 @@ wait_for_iobuf: if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT) && journal->j_flags & JBD2_BARRIER) { - blkdev_issue_flush(journal->j_dev, GFP_KERNEL, NULL, - BLKDEV_IFL_WAIT); + blkdev_issue_flush(journal->j_dev, GFP_KERNEL, NULL); } if (err) diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 400b2caef4d..d97310f07be 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -774,7 +774,7 @@ int nilfs_discard_segments(struct the_nilfs *nilfs, __u64 *segnump, ret = blkdev_issue_discard(nilfs->ns_bdev, start * sects_per_block, nblocks * sects_per_block, - GFP_NOFS, BLKDEV_IFL_WAIT); + GFP_NOFS, 0); if (ret < 0) return ret; nblocks = 0; @@ -784,7 +784,7 @@ int nilfs_discard_segments(struct the_nilfs *nilfs, __u64 *segnump, ret = blkdev_issue_discard(nilfs->ns_bdev, start * sects_per_block, nblocks * sects_per_block, - GFP_NOFS, BLKDEV_IFL_WAIT); + GFP_NOFS, 0); return ret; } diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index 6846371498b..91f080cc76c 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -152,8 +152,7 @@ static int reiserfs_sync_file(struct file *filp, int datasync) barrier_done = reiserfs_commit_for_inode(inode); reiserfs_write_unlock(inode->i_sb); if (barrier_done != 1 && reiserfs_barrier_flush(inode->i_sb)) - blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL, - BLKDEV_IFL_WAIT); + blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); if (barrier_done < 0) return barrier_done; return (err < 0) ? -EIO : 0; diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 15c35b62ff1..5fa7a30cc3f 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -693,8 +693,7 @@ void xfs_blkdev_issue_flush( xfs_buftarg_t *buftarg) { - blkdev_issue_flush(buftarg->bt_bdev, GFP_KERNEL, NULL, - BLKDEV_IFL_WAIT); + blkdev_issue_flush(buftarg->bt_bdev, GFP_KERNEL, NULL); } STATIC void -- cgit v1.2.3-18-g5258 From 2680d722bf2c5f75225dd9acb3ec9e5a9e2652f4 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 17 Sep 2010 16:44:28 +0300 Subject: UBIFS: introduce new flag for RO due to errors The R/O state may have various reasons: 1. The UBI volume is R/O 2. The FS is mounted R/O 3. The FS switched to R/O mode because of an error However, in UBIFS we have only one variable which represents cases 1 and 3 - 'c->ro_media'. Indeed, we set this to 1 if we switch to R/O mode due to an error, and then we test it in many places to make sure that we stop writing as soon as the error happens. But this is very unclean. One consequence of this, for example, is that in 'ubifs_remount_fs()' we use 'c->ro_media' to check whether we are in R/O mode because on an error, and we print a message in this case. However, if we are in R/O mode because the media is R/O, our message is bogus. This patch introduces new flag - 'c->ro_error' which is set when we switch to R/O mode because of an error. It also changes all "if (c->ro_media)" checks to "if (c->ro_error)" checks, because this is what the checks actually mean. We do not need to check for 'c->ro_media' because if the UBI volume is in R/O mode, we do not allow R/W mounting, and now writes can happen. This is guaranteed by VFS. But it is good to double-check this, so this patch also adds many "ubifs_assert(!c->ro_media)" checks. In the 'ubifs_remount_fs()' function this patch makes a bit more changes - it fixes the error messages as well. Signed-off-by: Artem Bityutskiy --- fs/ubifs/commit.c | 4 +++- fs/ubifs/file.c | 6 ++++-- fs/ubifs/gc.c | 3 ++- fs/ubifs/io.c | 16 ++++++++++------ fs/ubifs/journal.c | 3 ++- fs/ubifs/log.c | 4 ++-- fs/ubifs/master.c | 3 ++- fs/ubifs/misc.h | 9 ++++++--- fs/ubifs/shrinker.c | 2 +- fs/ubifs/super.c | 14 +++++++++----- fs/ubifs/ubifs.h | 4 +++- 11 files changed, 44 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/commit.c b/fs/ubifs/commit.c index 37fa7ed062d..712432789fb 100644 --- a/fs/ubifs/commit.c +++ b/fs/ubifs/commit.c @@ -63,7 +63,9 @@ static int do_commit(struct ubifs_info *c) struct ubifs_lp_stats lst; dbg_cmt("start"); - if (c->ro_media) { + ubifs_assert(!c->ro_media); + + if (c->ro_error) { err = -EROFS; goto out_up; } diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 03ae894c45d..c6bc51c9f07 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -433,8 +433,9 @@ static int ubifs_write_begin(struct file *file, struct address_space *mapping, struct page *page; ubifs_assert(ubifs_inode(inode)->ui_size == inode->i_size); + ubifs_assert(!c->ro_media); - if (unlikely(c->ro_media)) + if (unlikely(c->ro_error)) return -EROFS; /* Try out the fast-path part first */ @@ -1440,8 +1441,9 @@ static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vm dbg_gen("ino %lu, pg %lu, i_size %lld", inode->i_ino, page->index, i_size_read(inode)); ubifs_assert(!(inode->i_sb->s_flags & MS_RDONLY)); + ubifs_assert(!c->ro_media); - if (unlikely(c->ro_media)) + if (unlikely(c->ro_error)) return VM_FAULT_SIGBUS; /* -EROFS */ /* diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index 396f24a30af..d927196d730 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -616,13 +616,14 @@ int ubifs_garbage_collect(struct ubifs_info *c, int anyway) struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; ubifs_assert_cmt_locked(c); + ubifs_assert(!c->ro_media); if (ubifs_gc_should_commit(c)) return -EAGAIN; mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); - if (c->ro_media) { + if (c->ro_error) { ret = -EROFS; goto out_unlock; } diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c index 9432431bf59..18a4b8d7c84 100644 --- a/fs/ubifs/io.c +++ b/fs/ubifs/io.c @@ -61,8 +61,8 @@ */ void ubifs_ro_mode(struct ubifs_info *c, int err) { - if (!c->ro_media) { - c->ro_media = 1; + if (!c->ro_error) { + c->ro_error = 1; c->no_chk_data_crc = 0; c->vfs_sb->s_flags |= MS_RDONLY; ubifs_warn("switched to read-only mode, error %d", err); @@ -359,8 +359,9 @@ int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf) ubifs_assert(!(c->vfs_sb->s_flags & MS_RDONLY)); ubifs_assert(!(wbuf->avail & 7)); ubifs_assert(wbuf->offs + c->min_io_size <= c->leb_size); + ubifs_assert(!c->ro_media); - if (c->ro_media) + if (c->ro_error) return -EROFS; ubifs_pad(c, wbuf->buf + wbuf->used, wbuf->avail); @@ -440,11 +441,12 @@ int ubifs_bg_wbufs_sync(struct ubifs_info *c) { int err, i; + ubifs_assert(!c->ro_media); if (!c->need_wbuf_sync) return 0; c->need_wbuf_sync = 0; - if (c->ro_media) { + if (c->ro_error) { err = -EROFS; goto out_timers; } @@ -519,6 +521,7 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len) ubifs_assert(!(wbuf->offs & 7) && wbuf->offs <= c->leb_size); ubifs_assert(wbuf->avail > 0 && wbuf->avail <= c->min_io_size); ubifs_assert(mutex_is_locked(&wbuf->io_mutex)); + ubifs_assert(!c->ro_media); if (c->leb_size - wbuf->offs - wbuf->used < aligned_len) { err = -ENOSPC; @@ -527,7 +530,7 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len) cancel_wbuf_timer_nolock(wbuf); - if (c->ro_media) + if (c->ro_error) return -EROFS; if (aligned_len <= wbuf->avail) { @@ -663,8 +666,9 @@ int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum, buf_len); ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0); ubifs_assert(offs % c->min_io_size == 0 && offs < c->leb_size); + ubifs_assert(!c->ro_media); - if (c->ro_media) + if (c->ro_error) return -EROFS; ubifs_prepare_node(c, buf, len, 1); diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index d321baeca68..a6da8aa68f3 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -122,11 +122,12 @@ static int reserve_space(struct ubifs_info *c, int jhead, int len) * better to try to allocate space at the ends of eraseblocks. This is * what the squeeze parameter does. */ + ubifs_assert(!c->ro_media); squeeze = (jhead == BASEHD); again: mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); - if (c->ro_media) { + if (c->ro_error) { err = -EROFS; goto out_unlock; } diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c index c345e125f42..a41713e2fbb 100644 --- a/fs/ubifs/log.c +++ b/fs/ubifs/log.c @@ -223,8 +223,8 @@ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs) } mutex_lock(&c->log_mutex); - - if (c->ro_media) { + ubifs_assert(!c->ro_media); + if (c->ro_error) { err = -EROFS; goto out_unlock; } diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c index 28beaeedadc..0c818e855f5 100644 --- a/fs/ubifs/master.c +++ b/fs/ubifs/master.c @@ -361,7 +361,8 @@ int ubifs_write_master(struct ubifs_info *c) { int err, lnum, offs, len; - if (c->ro_media) + ubifs_assert(!c->ro_media); + if (c->ro_error) return -EROFS; lnum = UBIFS_MST_LNUM; diff --git a/fs/ubifs/misc.h b/fs/ubifs/misc.h index 4fa81d867e4..5d476ba184e 100644 --- a/fs/ubifs/misc.h +++ b/fs/ubifs/misc.h @@ -132,7 +132,8 @@ static inline int ubifs_leb_unmap(const struct ubifs_info *c, int lnum) { int err; - if (c->ro_media) + ubifs_assert(!c->ro_media); + if (c->ro_error) return -EROFS; err = ubi_leb_unmap(c->ubi, lnum); if (err) { @@ -159,7 +160,8 @@ static inline int ubifs_leb_write(const struct ubifs_info *c, int lnum, { int err; - if (c->ro_media) + ubifs_assert(!c->ro_media); + if (c->ro_error) return -EROFS; err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype); if (err) { @@ -186,7 +188,8 @@ static inline int ubifs_leb_change(const struct ubifs_info *c, int lnum, { int err; - if (c->ro_media) + ubifs_assert(!c->ro_media); + if (c->ro_error) return -EROFS; err = ubi_leb_change(c->ubi, lnum, buf, len, dtype); if (err) { diff --git a/fs/ubifs/shrinker.c b/fs/ubifs/shrinker.c index 0b201114a5a..10eec877843 100644 --- a/fs/ubifs/shrinker.c +++ b/fs/ubifs/shrinker.c @@ -250,7 +250,7 @@ static int kick_a_thread(void) dirty_zn_cnt = atomic_long_read(&c->dirty_zn_cnt); if (!dirty_zn_cnt || c->cmt_state == COMMIT_BROKEN || - c->ro_media) { + c->ro_media || c->ro_error) { mutex_unlock(&c->umount_mutex); continue; } diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index cd5900b85d3..1cfeec56df9 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1751,10 +1751,10 @@ static void ubifs_put_super(struct super_block *sb) ubifs_wbuf_sync(&c->jheads[i].wbuf); /* - * On fatal errors c->ro_media is set to 1, in which case we do + * On fatal errors c->ro_error is set to 1, in which case we do * not write the master node. */ - if (!c->ro_media) { + if (!c->ro_error) { /* * We are being cleanly unmounted which means the * orphans were killed - indicate this in the master @@ -1798,16 +1798,20 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) } if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) { + if (c->ro_error) { + ubifs_msg("cannot re-mount R/W due to prior errors"); + return -EROFS; + } if (c->ro_media) { - ubifs_msg("cannot re-mount due to prior errors"); + ubifs_msg("cannot re-mount R/W - UBI volume is R/O"); return -EROFS; } err = ubifs_remount_rw(c); if (err) return err; } else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) { - if (c->ro_media) { - ubifs_msg("cannot re-mount due to prior errors"); + if (c->ro_error) { + ubifs_msg("cannot re-mount R/O due to prior errors"); return -EROFS; } ubifs_remount_ro(c); diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index c4dc9b18f73..f47ebb442d1 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1032,6 +1032,7 @@ struct ubifs_debug_info; * @max_leb_cnt: maximum count of logical eraseblocks * @old_leb_cnt: count of logical eraseblocks before re-size * @ro_media: the underlying UBI volume is read-only + * @ro_error: UBIFS switched to R/O mode because an error happened * * @dirty_pg_cnt: number of dirty pages (not used) * @dirty_zn_cnt: number of dirty znodes @@ -1272,7 +1273,8 @@ struct ubifs_info { int leb_cnt; int max_leb_cnt; int old_leb_cnt; - int ro_media; + unsigned int ro_media:1; + unsigned int ro_error:1; atomic_long_t dirty_pg_cnt; atomic_long_t dirty_zn_cnt; -- cgit v1.2.3-18-g5258 From 2ef13294d29bcfb306e0d360f1b97f37b647b0c0 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 19 Sep 2010 18:34:26 +0300 Subject: UBIFS: introduce new flags for RO mounts Commit 2fde99cb55fb9d9b88180512a5e8a5d939d27fec "UBIFS: mark VFS SB RO too" introduced regression. This commit made UBIFS set the 'MS_RDONLY' flag in the VFS superblock when it switches to R/O mode due to an error. This was done to make VFS show the R/O UBIFS flag in /proc/mounts. However, several places in UBIFS relied on the 'MS_RDONLY' flag and assume this flag can only change when we re-mount. For example, 'ubifs_put_super()'. This patch introduces new UBIFS flag - 'c->ro_mount' which changes only when we re-mount, and preserves the way UBIFS was originally mounted (R/W or R/O). This allows us to de-initialize UBIFS cleanly in 'ubifs_put_super()'. This patch also changes all 'ubifs_assert(!c->ro_media)' assertions to 'ubifs_assert(!c->ro_media && !c->ro_mount)', because we never should write anything if the FS was mounter R/O. All the places where we test for 'MS_RDONLY' flag in the VFS SB were changed and now we test the 'c->ro_mount' flag instead, because it preserves the original UBIFS mount type, unlike the 'MS_RDONLY' flag. Signed-off-by: Artem Bityutskiy --- fs/ubifs/commit.c | 2 +- fs/ubifs/file.c | 5 ++--- fs/ubifs/gc.c | 2 +- fs/ubifs/io.c | 9 ++++----- fs/ubifs/journal.c | 2 +- fs/ubifs/log.c | 4 ++-- fs/ubifs/master.c | 2 +- fs/ubifs/misc.h | 6 +++--- fs/ubifs/recovery.c | 8 ++++---- fs/ubifs/replay.c | 3 +-- fs/ubifs/sb.c | 9 +++------ fs/ubifs/shrinker.c | 2 +- fs/ubifs/super.c | 55 ++++++++++++++++++++++++++++------------------------- fs/ubifs/ubifs.h | 13 +++++++++---- 14 files changed, 62 insertions(+), 60 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/commit.c b/fs/ubifs/commit.c index 712432789fb..02429d81ca3 100644 --- a/fs/ubifs/commit.c +++ b/fs/ubifs/commit.c @@ -63,7 +63,7 @@ static int do_commit(struct ubifs_info *c) struct ubifs_lp_stats lst; dbg_cmt("start"); - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) { err = -EROFS; diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index c6bc51c9f07..d77db7e3648 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -433,7 +433,7 @@ static int ubifs_write_begin(struct file *file, struct address_space *mapping, struct page *page; ubifs_assert(ubifs_inode(inode)->ui_size == inode->i_size); - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (unlikely(c->ro_error)) return -EROFS; @@ -1440,8 +1440,7 @@ static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vm dbg_gen("ino %lu, pg %lu, i_size %lld", inode->i_ino, page->index, i_size_read(inode)); - ubifs_assert(!(inode->i_sb->s_flags & MS_RDONLY)); - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (unlikely(c->ro_error)) return VM_FAULT_SIGBUS; /* -EROFS */ diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index d927196d730..151f1088282 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -616,7 +616,7 @@ int ubifs_garbage_collect(struct ubifs_info *c, int anyway) struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; ubifs_assert_cmt_locked(c); - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (ubifs_gc_should_commit(c)) return -EAGAIN; diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c index 18a4b8d7c84..d82173182ee 100644 --- a/fs/ubifs/io.c +++ b/fs/ubifs/io.c @@ -356,10 +356,9 @@ int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf) dbg_io("LEB %d:%d, %d bytes, jhead %s", wbuf->lnum, wbuf->offs, wbuf->used, dbg_jhead(wbuf->jhead)); - ubifs_assert(!(c->vfs_sb->s_flags & MS_RDONLY)); ubifs_assert(!(wbuf->avail & 7)); ubifs_assert(wbuf->offs + c->min_io_size <= c->leb_size); - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) return -EROFS; @@ -441,7 +440,7 @@ int ubifs_bg_wbufs_sync(struct ubifs_info *c) { int err, i; - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (!c->need_wbuf_sync) return 0; c->need_wbuf_sync = 0; @@ -521,7 +520,7 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len) ubifs_assert(!(wbuf->offs & 7) && wbuf->offs <= c->leb_size); ubifs_assert(wbuf->avail > 0 && wbuf->avail <= c->min_io_size); ubifs_assert(mutex_is_locked(&wbuf->io_mutex)); - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (c->leb_size - wbuf->offs - wbuf->used < aligned_len) { err = -ENOSPC; @@ -666,7 +665,7 @@ int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum, buf_len); ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0); ubifs_assert(offs % c->min_io_size == 0 && offs < c->leb_size); - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) return -EROFS; diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index a6da8aa68f3..914f1bd89e5 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -122,7 +122,7 @@ static int reserve_space(struct ubifs_info *c, int jhead, int len) * better to try to allocate space at the ends of eraseblocks. This is * what the squeeze parameter does. */ - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); squeeze = (jhead == BASEHD); again: mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c index a41713e2fbb..4d0cb124146 100644 --- a/fs/ubifs/log.c +++ b/fs/ubifs/log.c @@ -159,7 +159,7 @@ void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud) jhead = &c->jheads[bud->jhead]; list_add_tail(&bud->list, &jhead->buds_list); } else - ubifs_assert(c->replaying && (c->vfs_sb->s_flags & MS_RDONLY)); + ubifs_assert(c->replaying && c->ro_mount); /* * Note, although this is a new bud, we anyway account this space now, @@ -223,7 +223,7 @@ int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs) } mutex_lock(&c->log_mutex); - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) { err = -EROFS; goto out_unlock; diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c index 0c818e855f5..21f47afdacf 100644 --- a/fs/ubifs/master.c +++ b/fs/ubifs/master.c @@ -361,7 +361,7 @@ int ubifs_write_master(struct ubifs_info *c) { int err, lnum, offs, len; - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) return -EROFS; diff --git a/fs/ubifs/misc.h b/fs/ubifs/misc.h index 5d476ba184e..c3de04dc952 100644 --- a/fs/ubifs/misc.h +++ b/fs/ubifs/misc.h @@ -132,7 +132,7 @@ static inline int ubifs_leb_unmap(const struct ubifs_info *c, int lnum) { int err; - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) return -EROFS; err = ubi_leb_unmap(c->ubi, lnum); @@ -160,7 +160,7 @@ static inline int ubifs_leb_write(const struct ubifs_info *c, int lnum, { int err; - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) return -EROFS; err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype); @@ -188,7 +188,7 @@ static inline int ubifs_leb_change(const struct ubifs_info *c, int lnum, { int err; - ubifs_assert(!c->ro_media); + ubifs_assert(!c->ro_media && !c->ro_mount); if (c->ro_error) return -EROFS; err = ubi_leb_change(c->ubi, lnum, buf, len, dtype); diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index daae9e1f538..c902a5de90a 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c @@ -292,7 +292,7 @@ int ubifs_recover_master_node(struct ubifs_info *c) memcpy(c->mst_node, mst, UBIFS_MST_NODE_SZ); - if ((c->vfs_sb->s_flags & MS_RDONLY)) { + if (c->ro_mount) { /* Read-only mode. Keep a copy for switching to rw mode */ c->rcvrd_mst_node = kmalloc(sz, GFP_KERNEL); if (!c->rcvrd_mst_node) { @@ -469,7 +469,7 @@ static int fix_unclean_leb(struct ubifs_info *c, struct ubifs_scan_leb *sleb, endpt = snod->offs + snod->len; } - if ((c->vfs_sb->s_flags & MS_RDONLY) && !c->remounting_rw) { + if (c->ro_mount && !c->remounting_rw) { /* Add to recovery list */ struct ubifs_unclean_leb *ucleb; @@ -883,7 +883,7 @@ int ubifs_recover_inl_heads(const struct ubifs_info *c, void *sbuf) { int err; - ubifs_assert(!(c->vfs_sb->s_flags & MS_RDONLY) || c->remounting_rw); + ubifs_assert(!c->ro_mount || c->remounting_rw); dbg_rcvry("checking index head at %d:%d", c->ihead_lnum, c->ihead_offs); err = recover_head(c, c->ihead_lnum, c->ihead_offs, sbuf); @@ -1461,7 +1461,7 @@ int ubifs_recover_size(struct ubifs_info *c) } } if (e->exists && e->i_size < e->d_size) { - if (!e->inode && (c->vfs_sb->s_flags & MS_RDONLY)) { + if (!e->inode && c->ro_mount) { /* Fix the inode size and pin it in memory */ struct inode *inode; diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index 5c2d6d759a3..730598cf634 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -627,8 +627,7 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, ubifs_assert(sleb->endpt - offs >= used); ubifs_assert(sleb->endpt % c->min_io_size == 0); - if (sleb->endpt + c->min_io_size <= c->leb_size && - !(c->vfs_sb->s_flags & MS_RDONLY)) + if (sleb->endpt + c->min_io_size <= c->leb_size && !c->ro_mount) err = ubifs_wbuf_seek_nolock(&c->jheads[jhead].wbuf, lnum, sleb->endpt, UBI_SHORTTERM); diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c index 96cb62c8a9d..bf31b4729e5 100644 --- a/fs/ubifs/sb.c +++ b/fs/ubifs/sb.c @@ -542,11 +542,8 @@ int ubifs_read_superblock(struct ubifs_info *c) * due to the unavailability of time-travelling equipment. */ if (c->fmt_version > UBIFS_FORMAT_VERSION) { - struct super_block *sb = c->vfs_sb; - int mounting_ro = sb->s_flags & MS_RDONLY; - - ubifs_assert(!c->ro_media || mounting_ro); - if (!mounting_ro || + ubifs_assert(!c->ro_media || c->ro_mount); + if (!c->ro_mount || c->ro_compat_version > UBIFS_RO_COMPAT_VERSION) { ubifs_err("on-flash format version is w%d/r%d, but " "software only supports up to version " @@ -624,7 +621,7 @@ int ubifs_read_superblock(struct ubifs_info *c) c->old_leb_cnt = c->leb_cnt; if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) { c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size); - if (c->vfs_sb->s_flags & MS_RDONLY) + if (c->ro_mount) dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs", c->old_leb_cnt, c->leb_cnt); else { diff --git a/fs/ubifs/shrinker.c b/fs/ubifs/shrinker.c index 10eec877843..46961c00323 100644 --- a/fs/ubifs/shrinker.c +++ b/fs/ubifs/shrinker.c @@ -250,7 +250,7 @@ static int kick_a_thread(void) dirty_zn_cnt = atomic_long_read(&c->dirty_zn_cnt); if (!dirty_zn_cnt || c->cmt_state == COMMIT_BROKEN || - c->ro_media || c->ro_error) { + c->ro_mount || c->ro_error) { mutex_unlock(&c->umount_mutex); continue; } diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 1cfeec56df9..4e5bf3fbf78 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1137,11 +1137,11 @@ static int check_free_space(struct ubifs_info *c) */ static int mount_ubifs(struct ubifs_info *c) { - struct super_block *sb = c->vfs_sb; - int err, mounted_read_only = (sb->s_flags & MS_RDONLY); + int err; long long x; size_t sz; + c->ro_mount = !!(c->vfs_sb->s_flags & MS_RDONLY); err = init_constants_early(c); if (err) return err; @@ -1154,7 +1154,7 @@ static int mount_ubifs(struct ubifs_info *c) if (err) goto out_free; - if (c->empty && (mounted_read_only || c->ro_media)) { + if (c->empty && (c->ro_mount || c->ro_media)) { /* * This UBI volume is empty, and read-only, or the file system * is mounted read-only - we cannot format it. @@ -1165,7 +1165,7 @@ static int mount_ubifs(struct ubifs_info *c) goto out_free; } - if (c->ro_media && !mounted_read_only) { + if (c->ro_media && !c->ro_mount) { ubifs_err("cannot mount read-write - read-only media"); err = -EROFS; goto out_free; @@ -1185,7 +1185,7 @@ static int mount_ubifs(struct ubifs_info *c) if (!c->sbuf) goto out_free; - if (!mounted_read_only) { + if (!c->ro_mount) { c->ileb_buf = vmalloc(c->leb_size); if (!c->ileb_buf) goto out_free; @@ -1228,7 +1228,7 @@ static int mount_ubifs(struct ubifs_info *c) } sprintf(c->bgt_name, BGT_NAME_PATTERN, c->vi.ubi_num, c->vi.vol_id); - if (!mounted_read_only) { + if (!c->ro_mount) { err = alloc_wbufs(c); if (err) goto out_cbuf; @@ -1254,12 +1254,12 @@ static int mount_ubifs(struct ubifs_info *c) if ((c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY)) != 0) { ubifs_msg("recovery needed"); c->need_recovery = 1; - if (!mounted_read_only) { + if (!c->ro_mount) { err = ubifs_recover_inl_heads(c, c->sbuf); if (err) goto out_master; } - } else if (!mounted_read_only) { + } else if (!c->ro_mount) { /* * Set the "dirty" flag so that if we reboot uncleanly we * will notice this immediately on the next mount. @@ -1270,7 +1270,7 @@ static int mount_ubifs(struct ubifs_info *c) goto out_master; } - err = ubifs_lpt_init(c, 1, !mounted_read_only); + err = ubifs_lpt_init(c, 1, !c->ro_mount); if (err) goto out_lpt; @@ -1285,11 +1285,11 @@ static int mount_ubifs(struct ubifs_info *c) /* Calculate 'min_idx_lebs' after journal replay */ c->min_idx_lebs = ubifs_calc_min_idx_lebs(c); - err = ubifs_mount_orphans(c, c->need_recovery, mounted_read_only); + err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount); if (err) goto out_orphans; - if (!mounted_read_only) { + if (!c->ro_mount) { int lnum; err = check_free_space(c); @@ -1351,7 +1351,7 @@ static int mount_ubifs(struct ubifs_info *c) spin_unlock(&ubifs_infos_lock); if (c->need_recovery) { - if (mounted_read_only) + if (c->ro_mount) ubifs_msg("recovery deferred"); else { c->need_recovery = 0; @@ -1378,7 +1378,7 @@ static int mount_ubifs(struct ubifs_info *c) ubifs_msg("mounted UBI device %d, volume %d, name \"%s\"", c->vi.ubi_num, c->vi.vol_id, c->vi.name); - if (mounted_read_only) + if (c->ro_mount) ubifs_msg("mounted read-only"); x = (long long)c->main_lebs * c->leb_size; ubifs_msg("file system size: %lld bytes (%lld KiB, %lld MiB, %d " @@ -1640,7 +1640,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) } dbg_gen("re-mounted read-write"); - c->vfs_sb->s_flags &= ~MS_RDONLY; + c->ro_mount = 0; c->remounting_rw = 0; c->always_chk_crc = 0; err = dbg_check_space_info(c); @@ -1676,7 +1676,7 @@ static void ubifs_remount_ro(struct ubifs_info *c) int i, err; ubifs_assert(!c->need_recovery); - ubifs_assert(!(c->vfs_sb->s_flags & MS_RDONLY)); + ubifs_assert(!c->ro_mount); mutex_lock(&c->umount_mutex); if (c->bgt) { @@ -1704,6 +1704,7 @@ static void ubifs_remount_ro(struct ubifs_info *c) vfree(c->ileb_buf); c->ileb_buf = NULL; ubifs_lpt_free(c, 1); + c->ro_mount = 1; err = dbg_check_space_info(c); if (err) ubifs_ro_mode(c, err); @@ -1735,7 +1736,7 @@ static void ubifs_put_super(struct super_block *sb) * the mutex is locked. */ mutex_lock(&c->umount_mutex); - if (!(c->vfs_sb->s_flags & MS_RDONLY)) { + if (!c->ro_mount) { /* * First of all kill the background thread to make sure it does * not interfere with un-mounting and freeing resources. @@ -1745,23 +1746,23 @@ static void ubifs_put_super(struct super_block *sb) c->bgt = NULL; } - /* Synchronize write-buffers */ - if (c->jheads) - for (i = 0; i < c->jhead_cnt; i++) - ubifs_wbuf_sync(&c->jheads[i].wbuf); - /* * On fatal errors c->ro_error is set to 1, in which case we do * not write the master node. */ if (!c->ro_error) { + int err; + + /* Synchronize write-buffers */ + if (c->jheads) + for (i = 0; i < c->jhead_cnt; i++) + ubifs_wbuf_sync(&c->jheads[i].wbuf); + /* * We are being cleanly unmounted which means the * orphans were killed - indicate this in the master * node. Also save the reserved GC LEB number. */ - int err; - c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY); c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum); @@ -1797,7 +1798,7 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) return err; } - if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) { + if (c->ro_mount && !(*flags & MS_RDONLY)) { if (c->ro_error) { ubifs_msg("cannot re-mount R/W due to prior errors"); return -EROFS; @@ -1809,7 +1810,7 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) err = ubifs_remount_rw(c); if (err) return err; - } else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) { + } else if (!c->ro_mount && (*flags & MS_RDONLY)) { if (c->ro_error) { ubifs_msg("cannot re-mount R/O due to prior errors"); return -EROFS; @@ -2068,9 +2069,11 @@ static int ubifs_get_sb(struct file_system_type *fs_type, int flags, } if (sb->s_root) { + struct ubifs_info *c1 = sb->s_fs_info; + /* A new mount point for already mounted UBIFS */ dbg_gen("this ubi volume is already mounted"); - if ((flags ^ sb->s_flags) & MS_RDONLY) { + if (!!(flags & MS_RDONLY) != c1->ro_mount) { err = -EBUSY; goto out_deact; } diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index f47ebb442d1..381d6b207a5 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1032,6 +1032,7 @@ struct ubifs_debug_info; * @max_leb_cnt: maximum count of logical eraseblocks * @old_leb_cnt: count of logical eraseblocks before re-size * @ro_media: the underlying UBI volume is read-only + * @ro_mount: the file-system was mounted as read-only * @ro_error: UBIFS switched to R/O mode because an error happened * * @dirty_pg_cnt: number of dirty pages (not used) @@ -1173,11 +1174,14 @@ struct ubifs_debug_info; * @replay_sqnum: sequence number of node currently being replayed * @need_recovery: file-system needs recovery * @replaying: set to %1 during journal replay - * @unclean_leb_list: LEBs to recover when mounting ro to rw - * @rcvrd_mst_node: recovered master node to write when mounting ro to rw + * @unclean_leb_list: LEBs to recover when re-mounting R/O mounted FS to R/W + * mode + * @rcvrd_mst_node: recovered master node to write when re-mounting R/O mounted + * FS to R/W mode * @size_tree: inode size information for recovery - * @remounting_rw: set while remounting from ro to rw (sb flags have MS_RDONLY) - * @always_chk_crc: always check CRCs (while mounting and remounting rw) + * @remounting_rw: set while re-mounting from R/O mode to R/W mode + * @always_chk_crc: always check CRCs (while mounting and remounting to R/W + * mode) * @mount_opts: UBIFS-specific mount options * * @dbg: debugging-related information @@ -1274,6 +1278,7 @@ struct ubifs_info { int max_leb_cnt; int old_leb_cnt; unsigned int ro_media:1; + unsigned int ro_mount:1; unsigned int ro_error:1; atomic_long_t dirty_pg_cnt; -- cgit v1.2.3-18-g5258 From ff8f33c8b30d7b7efdcf2548c7f6e64db6a89b29 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Wed, 11 Aug 2010 09:37:53 +0100 Subject: GFS2: New truncate sequence This updates GFS2's truncate code to use the new truncate sequence correctly. This is a stepping stone to being able to remove ip->i_disksize in favour of using i_size everywhere now that the two sizes are always identical. Signed-off-by: Steven Whitehouse Cc: Nick Piggin Cc: Christoph Hellwig --- fs/gfs2/aops.c | 10 +-- fs/gfs2/bmap.c | 247 +++++++++++++++++++++++++--------------------------- fs/gfs2/bmap.h | 20 +++-- fs/gfs2/ops_inode.c | 26 +----- 4 files changed, 135 insertions(+), 168 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 194fe16d841..f687f25fb7f 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -696,13 +696,11 @@ out: page_cache_release(page); - /* - * XXX(truncate): the call below should probably be replaced with - * a call to the gfs2-specific truncate blocks helper to actually - * release disk blocks.. - */ + gfs2_trans_end(sdp); if (pos + len > ip->i_inode.i_size) - truncate_setsize(&ip->i_inode, ip->i_inode.i_size); + gfs2_trim_blocks(&ip->i_inode); + goto out_trans_fail; + out_endtrans: gfs2_trans_end(sdp); out_trans_fail: diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 6f482809d1a..20b971ad497 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -50,7 +50,7 @@ struct strip_mine { * @ip: the inode * @dibh: the dinode buffer * @block: the block number that was allocated - * @private: any locked page held by the caller process + * @page: The (optional) page. This is looked up if @page is NULL * * Returns: errno */ @@ -109,8 +109,7 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh, /** * gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big * @ip: The GFS2 inode to unstuff - * @unstuffer: the routine that handles unstuffing a non-zero length file - * @private: private data for the unstuffer + * @page: The (optional) page. This is looked up if the @page is NULL * * This routine unstuffs a dinode and returns it to a "normal" state such * that the height can be grown in the traditional way. @@ -884,84 +883,15 @@ out: return error; } -/** - * do_grow - Make a file look bigger than it is - * @ip: the inode - * @size: the size to set the file to - * - * Called with an exclusive lock on @ip. - * - * Returns: errno - */ - -static int do_grow(struct gfs2_inode *ip, u64 size) -{ - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_alloc *al; - struct buffer_head *dibh; - int error; - - al = gfs2_alloc_get(ip); - if (!al) - return -ENOMEM; - - error = gfs2_quota_lock_check(ip); - if (error) - goto out; - - al->al_requested = sdp->sd_max_height + RES_DATA; - - error = gfs2_inplace_reserve(ip); - if (error) - goto out_gunlock_q; - - error = gfs2_trans_begin(sdp, - sdp->sd_max_height + al->al_rgd->rd_length + - RES_JDATA + RES_DINODE + RES_STATFS + RES_QUOTA, 0); - if (error) - goto out_ipres; - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out_end_trans; - - if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) { - if (gfs2_is_stuffed(ip)) { - error = gfs2_unstuff_dinode(ip, NULL); - if (error) - goto out_brelse; - } - } - - ip->i_disksize = size; - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); - -out_brelse: - brelse(dibh); -out_end_trans: - gfs2_trans_end(sdp); -out_ipres: - gfs2_inplace_release(ip); -out_gunlock_q: - gfs2_quota_unlock(ip); -out: - gfs2_alloc_put(ip); - return error; -} - - /** * gfs2_block_truncate_page - Deal with zeroing out data for truncate * * This is partly borrowed from ext3. */ -static int gfs2_block_truncate_page(struct address_space *mapping) +static int gfs2_block_truncate_page(struct address_space *mapping, loff_t from) { struct inode *inode = mapping->host; struct gfs2_inode *ip = GFS2_I(inode); - loff_t from = inode->i_size; unsigned long index = from >> PAGE_CACHE_SHIFT; unsigned offset = from & (PAGE_CACHE_SIZE-1); unsigned blocksize, iblock, length, pos; @@ -1023,9 +953,11 @@ unlock: return err; } -static int trunc_start(struct gfs2_inode *ip, u64 size) +static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize) { - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct address_space *mapping = inode->i_mapping; struct buffer_head *dibh; int journaled = gfs2_is_jdata(ip); int error; @@ -1039,31 +971,27 @@ static int trunc_start(struct gfs2_inode *ip, u64 size) if (error) goto out; + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + if (gfs2_is_stuffed(ip)) { - u64 dsize = size + sizeof(struct gfs2_dinode); - ip->i_disksize = size; - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); - if (dsize > dibh->b_size) - dsize = dibh->b_size; - gfs2_buffer_clear_tail(dibh, dsize); - error = 1; + gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + newsize); } else { - if (size & (u64)(sdp->sd_sb.sb_bsize - 1)) - error = gfs2_block_truncate_page(ip->i_inode.i_mapping); - - if (!error) { - ip->i_disksize = size; - ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; - ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG; - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); + if (newsize & (u64)(sdp->sd_sb.sb_bsize - 1)) { + error = gfs2_block_truncate_page(mapping, newsize); + if (error) + goto out_brelse; } + ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG; } - brelse(dibh); + i_size_write(inode, newsize); + ip->i_disksize = newsize; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; + gfs2_dinode_out(ip, dibh->b_data); + truncate_pagecache(inode, oldsize, newsize); +out_brelse: + brelse(dibh); out: gfs2_trans_end(sdp); return error; @@ -1143,86 +1071,149 @@ out: /** * do_shrink - make a file smaller - * @ip: the inode - * @size: the size to make the file - * @truncator: function to truncate the last partial block + * @inode: the inode + * @oldsize: the current inode size + * @newsize: the size to make the file * - * Called with an exclusive lock on @ip. + * Called with an exclusive lock on @inode. The @size must + * be equal to or smaller than the current inode size. * * Returns: errno */ -static int do_shrink(struct gfs2_inode *ip, u64 size) +static int do_shrink(struct inode *inode, u64 oldsize, u64 newsize) { + struct gfs2_inode *ip = GFS2_I(inode); int error; - error = trunc_start(ip, size); + error = trunc_start(inode, oldsize, newsize); if (error < 0) return error; - if (error > 0) + if (gfs2_is_stuffed(ip)) return 0; - error = trunc_dealloc(ip, size); - if (!error) + error = trunc_dealloc(ip, newsize); + if (error == 0) error = trunc_end(ip); return error; } -static int do_touch(struct gfs2_inode *ip, u64 size) +void gfs2_trim_blocks(struct inode *inode) { - struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + u64 size = inode->i_size; + int ret; + + ret = do_shrink(inode, size, size); + WARN_ON(ret != 0); +} + +/** + * do_grow - Touch and update inode size + * @inode: The inode + * @size: The new size + * + * This function updates the timestamps on the inode and + * may also increase the size of the inode. This function + * must not be called with @size any smaller than the current + * inode size. + * + * Although it is not strictly required to unstuff files here, + * earlier versions of GFS2 have a bug in the stuffed file reading + * code which will result in a buffer overrun if the size is larger + * than the max stuffed file size. In order to prevent this from + * occuring, such files are unstuffed, but in other cases we can + * just update the inode size directly. + * + * Returns: 0 on success, or -ve on error + */ + +static int do_grow(struct inode *inode, u64 size) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); struct buffer_head *dibh; + struct gfs2_alloc *al = NULL; int error; - error = gfs2_trans_begin(sdp, RES_DINODE, 0); + if (gfs2_is_stuffed(ip) && + (size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) { + al = gfs2_alloc_get(ip); + if (al == NULL) + return -ENOMEM; + + error = gfs2_quota_lock_check(ip); + if (error) + goto do_grow_alloc_put; + + al->al_requested = 1; + error = gfs2_inplace_reserve(ip); + if (error) + goto do_grow_qunlock; + } + + error = gfs2_trans_begin(sdp, RES_DINODE + 1, 0); if (error) - return error; + goto do_grow_release; - down_write(&ip->i_rw_mutex); + if (al) { + error = gfs2_unstuff_dinode(ip, NULL); + if (error) + goto do_end_trans; + } error = gfs2_meta_inode_buffer(ip, &dibh); if (error) - goto do_touch_out; + goto do_end_trans; + i_size_write(inode, size); + ip->i_disksize = size; ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); -do_touch_out: - up_write(&ip->i_rw_mutex); +do_end_trans: gfs2_trans_end(sdp); +do_grow_release: + if (al) { + gfs2_inplace_release(ip); +do_grow_qunlock: + gfs2_quota_unlock(ip); +do_grow_alloc_put: + gfs2_alloc_put(ip); + } return error; } /** - * gfs2_truncatei - make a file a given size - * @ip: the inode - * @size: the size to make the file - * @truncator: function to truncate the last partial block + * gfs2_setattr_size - make a file a given size + * @inode: the inode + * @newsize: the size to make the file * - * The file size can grow, shrink, or stay the same size. + * The file size can grow, shrink, or stay the same size. This + * is called holding i_mutex and an exclusive glock on the inode + * in question. * * Returns: errno */ -int gfs2_truncatei(struct gfs2_inode *ip, u64 size) +int gfs2_setattr_size(struct inode *inode, u64 newsize) { - int error; + int ret; + u64 oldsize; - if (gfs2_assert_warn(GFS2_SB(&ip->i_inode), S_ISREG(ip->i_inode.i_mode))) - return -EINVAL; + BUG_ON(!S_ISREG(inode->i_mode)); - if (size > ip->i_disksize) - error = do_grow(ip, size); - else if (size < ip->i_disksize) - error = do_shrink(ip, size); - else - /* update time stamps */ - error = do_touch(ip, size); + ret = inode_newsize_ok(inode, newsize); + if (ret) + return ret; - return error; + oldsize = inode->i_size; + if (newsize >= oldsize) + return do_grow(inode, newsize); + + return do_shrink(inode, oldsize, newsize); } int gfs2_truncatei_resume(struct gfs2_inode *ip) diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index a20a5213135..42fea03e2bd 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -44,14 +44,16 @@ static inline void gfs2_write_calc_reserv(const struct gfs2_inode *ip, } } -int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page); -int gfs2_block_map(struct inode *inode, sector_t lblock, struct buffer_head *bh, int create); -int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen); - -int gfs2_truncatei(struct gfs2_inode *ip, u64 size); -int gfs2_truncatei_resume(struct gfs2_inode *ip); -int gfs2_file_dealloc(struct gfs2_inode *ip); -int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, - unsigned int len); +extern int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page); +extern int gfs2_block_map(struct inode *inode, sector_t lblock, + struct buffer_head *bh, int create); +extern int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, + u64 *dblock, unsigned *extlen); +extern int gfs2_setattr_size(struct inode *inode, u64 size); +extern void gfs2_trim_blocks(struct inode *inode); +extern int gfs2_truncatei_resume(struct gfs2_inode *ip); +extern int gfs2_file_dealloc(struct gfs2_inode *ip); +extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, + unsigned int len); #endif /* __BMAP_DOT_H__ */ diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 1009be2c973..1d3f2fb466b 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -1071,30 +1071,6 @@ int gfs2_permission(struct inode *inode, int mask) return error; } -/* - * XXX(truncate): the truncate_setsize calls should be moved to the end. - */ -static int setattr_size(struct inode *inode, struct iattr *attr) -{ - struct gfs2_inode *ip = GFS2_I(inode); - struct gfs2_sbd *sdp = GFS2_SB(inode); - int error; - - if (attr->ia_size != ip->i_disksize) { - error = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); - if (error) - return error; - truncate_setsize(inode, attr->ia_size); - gfs2_trans_end(sdp); - } - - error = gfs2_truncatei(ip, attr->ia_size); - if (error && (inode->i_size != ip->i_disksize)) - i_size_write(inode, ip->i_disksize); - - return error; -} - static int setattr_chown(struct inode *inode, struct iattr *attr) { struct gfs2_inode *ip = GFS2_I(inode); @@ -1195,7 +1171,7 @@ static int gfs2_setattr(struct dentry *dentry, struct iattr *attr) goto out; if (attr->ia_valid & ATTR_SIZE) - error = setattr_size(inode, attr); + error = gfs2_setattr_size(inode, attr->ia_size); else if (attr->ia_valid & (ATTR_UID | ATTR_GID)) error = setattr_chown(inode, attr); else if ((attr->ia_valid & ATTR_MODE) && IS_POSIXACL(inode)) -- cgit v1.2.3-18-g5258 From a2e0f79939e09e74698564b88dee709db208e1e2 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Wed, 11 Aug 2010 09:53:11 +0100 Subject: GFS2: Remove i_disksize With the update of the truncate code, ip->i_disksize and inode->i_size are merely copies of each other. This means we can remove ip->i_disksize and use inode->i_size exclusively reducing the size of a GFS2 inode by 8 bytes. Signed-off-by: Steven Whitehouse --- fs/gfs2/aops.c | 6 +----- fs/gfs2/bmap.c | 12 +++++------- fs/gfs2/dir.c | 28 +++++++++++++++------------- fs/gfs2/file.c | 2 +- fs/gfs2/glops.c | 5 ++--- fs/gfs2/incore.h | 1 - fs/gfs2/inode.c | 9 ++++----- fs/gfs2/inode.h | 13 +++++++++++++ fs/gfs2/ops_fstype.c | 2 +- fs/gfs2/ops_inode.c | 10 +++++----- fs/gfs2/quota.c | 13 +++++-------- fs/gfs2/rgrp.c | 6 +++--- fs/gfs2/super.c | 11 +++++------ 13 files changed, 60 insertions(+), 58 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index f687f25fb7f..c92f36ba3fc 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -800,10 +800,8 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh, page_cache_release(page); if (copied) { - if (inode->i_size < to) { + if (inode->i_size < to) i_size_write(inode, to); - ip->i_disksize = inode->i_size; - } gfs2_dinode_out(ip, di); mark_inode_dirty(inode); } @@ -874,8 +872,6 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping, ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); if (ret > 0) { - if (inode->i_size > ip->i_disksize) - ip->i_disksize = inode->i_size; gfs2_dinode_out(ip, dibh->b_data); mark_inode_dirty(inode); } diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 20b971ad497..04513e997df 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -131,7 +131,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page) if (error) goto out; - if (ip->i_disksize) { + if (i_size_read(&ip->i_inode)) { /* Get a free block, fill it with the stuffed data, and write it out to disk */ @@ -160,7 +160,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page) di = (struct gfs2_dinode *)dibh->b_data; gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - if (ip->i_disksize) { + if (i_size_read(&ip->i_inode)) { *(__be64 *)(di + 1) = cpu_to_be64(block); gfs2_add_inode_blocks(&ip->i_inode, 1); di->di_blocks = cpu_to_be64(gfs2_get_inode_blocks(&ip->i_inode)); @@ -985,7 +985,6 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize) } i_size_write(inode, newsize); - ip->i_disksize = newsize; ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(ip, dibh->b_data); @@ -1051,7 +1050,7 @@ static int trunc_end(struct gfs2_inode *ip) if (error) goto out; - if (!ip->i_disksize) { + if (!i_size_read(&ip->i_inode)) { ip->i_height = 0; ip->i_goal = ip->i_no_addr; gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); @@ -1167,7 +1166,6 @@ static int do_grow(struct inode *inode, u64 size) goto do_end_trans; i_size_write(inode, size); - ip->i_disksize = size; ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); @@ -1219,7 +1217,7 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize) int gfs2_truncatei_resume(struct gfs2_inode *ip) { int error; - error = trunc_dealloc(ip, ip->i_disksize); + error = trunc_dealloc(ip, i_size_read(&ip->i_inode)); if (!error) error = trunc_end(ip); return error; @@ -1260,7 +1258,7 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, shift = sdp->sd_sb.sb_bsize_shift; BUG_ON(gfs2_is_dir(ip)); - end_of_file = (ip->i_disksize + sdp->sd_sb.sb_bsize - 1) >> shift; + end_of_file = (i_size_read(&ip->i_inode) + sdp->sd_sb.sb_bsize - 1) >> shift; lblock = offset >> shift; lblock_stop = (offset + len + sdp->sd_sb.sb_bsize - 1) >> shift; if (lblock_stop > end_of_file) diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index b9dd88a78dd..c1042ae438c 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -127,8 +127,8 @@ static int gfs2_dir_write_stuffed(struct gfs2_inode *ip, const char *buf, gfs2_trans_add_bh(ip->i_gl, dibh, 1); memcpy(dibh->b_data + offset + sizeof(struct gfs2_dinode), buf, size); - if (ip->i_disksize < offset + size) - ip->i_disksize = offset + size; + if (ip->i_inode.i_size < offset + size) + i_size_write(&ip->i_inode, offset + size); ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_dinode_out(ip, dibh->b_data); @@ -225,8 +225,8 @@ out: if (error) return error; - if (ip->i_disksize < offset + copied) - ip->i_disksize = offset + copied; + if (ip->i_inode.i_size < offset + copied) + i_size_write(&ip->i_inode, offset + copied); ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); @@ -275,12 +275,13 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, char *buf, u64 offset, unsigned int o; int copied = 0; int error = 0; + u64 disksize = i_size_read(&ip->i_inode); - if (offset >= ip->i_disksize) + if (offset >= disksize) return 0; - if (offset + size > ip->i_disksize) - size = ip->i_disksize - offset; + if (offset + size > disksize) + size = disksize - offset; if (!size) return 0; @@ -727,7 +728,7 @@ static struct gfs2_dirent *gfs2_dirent_search(struct inode *inode, unsigned hsize = 1 << ip->i_depth; unsigned index; u64 ln; - if (hsize * sizeof(u64) != ip->i_disksize) { + if (hsize * sizeof(u64) != i_size_read(inode)) { gfs2_consist_inode(ip); return ERR_PTR(-EIO); } @@ -879,7 +880,7 @@ static int dir_make_exhash(struct inode *inode) for (x = sdp->sd_hash_ptrs; x--; lp++) *lp = cpu_to_be64(bn); - dip->i_disksize = sdp->sd_sb.sb_bsize / 2; + i_size_write(inode, sdp->sd_sb.sb_bsize / 2); gfs2_add_inode_blocks(&dip->i_inode, 1); dip->i_diskflags |= GFS2_DIF_EXHASH; @@ -1057,11 +1058,12 @@ static int dir_double_exhash(struct gfs2_inode *dip) u64 *buf; u64 *from, *to; u64 block; + u64 disksize = i_size_read(&dip->i_inode); int x; int error = 0; hsize = 1 << dip->i_depth; - if (hsize * sizeof(u64) != dip->i_disksize) { + if (hsize * sizeof(u64) != disksize) { gfs2_consist_inode(dip); return -EIO; } @@ -1072,7 +1074,7 @@ static int dir_double_exhash(struct gfs2_inode *dip) if (!buf) return -ENOMEM; - for (block = dip->i_disksize >> sdp->sd_hash_bsize_shift; block--;) { + for (block = disksize >> sdp->sd_hash_bsize_shift; block--;) { error = gfs2_dir_read_data(dip, (char *)buf, block * sdp->sd_hash_bsize, sdp->sd_hash_bsize, 1); @@ -1370,7 +1372,7 @@ static int dir_e_read(struct inode *inode, u64 *offset, void *opaque, unsigned depth = 0; hsize = 1 << dip->i_depth; - if (hsize * sizeof(u64) != dip->i_disksize) { + if (hsize * sizeof(u64) != i_size_read(inode)) { gfs2_consist_inode(dip); return -EIO; } @@ -1784,7 +1786,7 @@ static int foreach_leaf(struct gfs2_inode *dip, leaf_call_t lc, void *data) int error = 0; hsize = 1 << dip->i_depth; - if (hsize * sizeof(u64) != dip->i_disksize) { + if (hsize * sizeof(u64) != i_size_read(&dip->i_inode)) { gfs2_consist_inode(dip); return -EIO; } diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 4edd662c823..daadcd2e755 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -491,7 +491,7 @@ static int gfs2_open(struct inode *inode, struct file *file) goto fail; if (!(file->f_flags & O_LARGEFILE) && - ip->i_disksize > MAX_NON_LFS) { + i_size_read(inode) > MAX_NON_LFS) { error = -EOVERFLOW; goto fail_gunlock; } diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 49f97d3bb69..621d80e8fb2 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -262,13 +262,12 @@ static int inode_go_dump(struct seq_file *seq, const struct gfs2_glock *gl) const struct gfs2_inode *ip = gl->gl_object; if (ip == NULL) return 0; - gfs2_print_dbg(seq, " I: n:%llu/%llu t:%u f:0x%02lx d:0x%08x s:%llu/%llu\n", + gfs2_print_dbg(seq, " I: n:%llu/%llu t:%u f:0x%02lx d:0x%08x s:%llu\n", (unsigned long long)ip->i_no_formal_ino, (unsigned long long)ip->i_no_addr, IF2DT(ip->i_inode.i_mode), ip->i_flags, (unsigned int)ip->i_diskflags, - (unsigned long long)ip->i_inode.i_size, - (unsigned long long)ip->i_disksize); + (unsigned long long)i_size_read(&ip->i_inode)); return 0; } diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index fdbf4b366fa..c1197177527 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -267,7 +267,6 @@ struct gfs2_inode { u64 i_no_formal_ino; u64 i_generation; u64 i_eattr; - loff_t i_disksize; unsigned long i_flags; /* GIF_... */ struct gfs2_glock *i_gl; /* Move into i_gh? */ struct gfs2_holder i_iopen_gh; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 08140f185a3..06370f8bd8c 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -359,8 +359,7 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) * to do that. */ ip->i_inode.i_nlink = be32_to_cpu(str->di_nlink); - ip->i_disksize = be64_to_cpu(str->di_size); - i_size_write(&ip->i_inode, ip->i_disksize); + i_size_write(&ip->i_inode, be64_to_cpu(str->di_size)); gfs2_set_inode_blocks(&ip->i_inode, be64_to_cpu(str->di_blocks)); atime.tv_sec = be64_to_cpu(str->di_atime); atime.tv_nsec = be32_to_cpu(str->di_atime_nsec); @@ -1055,7 +1054,7 @@ void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf) str->di_uid = cpu_to_be32(ip->i_inode.i_uid); str->di_gid = cpu_to_be32(ip->i_inode.i_gid); str->di_nlink = cpu_to_be32(ip->i_inode.i_nlink); - str->di_size = cpu_to_be64(ip->i_disksize); + str->di_size = cpu_to_be64(i_size_read(&ip->i_inode)); str->di_blocks = cpu_to_be64(gfs2_get_inode_blocks(&ip->i_inode)); str->di_atime = cpu_to_be64(ip->i_inode.i_atime.tv_sec); str->di_mtime = cpu_to_be64(ip->i_inode.i_mtime.tv_sec); @@ -1085,8 +1084,8 @@ void gfs2_dinode_print(const struct gfs2_inode *ip) (unsigned long long)ip->i_no_formal_ino); printk(KERN_INFO " no_addr = %llu\n", (unsigned long long)ip->i_no_addr); - printk(KERN_INFO " i_disksize = %llu\n", - (unsigned long long)ip->i_disksize); + printk(KERN_INFO " i_size = %llu\n", + (unsigned long long)i_size_read(&ip->i_inode)); printk(KERN_INFO " blocks = %llu\n", (unsigned long long)gfs2_get_inode_blocks(&ip->i_inode)); printk(KERN_INFO " i_goal = %llu\n", diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index 300ada3f21d..15ff4df20aa 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h @@ -80,6 +80,19 @@ static inline void gfs2_inum_out(const struct gfs2_inode *ip, dent->de_inum.no_addr = cpu_to_be64(ip->i_no_addr); } +static inline int gfs2_check_internal_file_size(struct inode *inode, + u64 minsize, u64 maxsize) +{ + u64 size = i_size_read(inode); + if (size < minsize || size > maxsize) + goto err; + if (size & ((1 << inode->i_blkbits) - 1)) + goto err; + return 0; +err: + gfs2_consist_inode(GFS2_I(inode)); + return -EIO; +} extern void gfs2_set_iop(struct inode *inode); extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 4d4b1e8ac64..5b5c87dfbfe 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -586,7 +586,7 @@ static int map_journal_extents(struct gfs2_sbd *sdp) prev_db = 0; - for (lb = 0; lb < ip->i_disksize >> sdp->sd_sb.sb_bsize_shift; lb++) { + for (lb = 0; lb < i_size_read(jd->jd_inode) >> sdp->sd_sb.sb_bsize_shift; lb++) { bh.b_state = 0; bh.b_blocknr = 0; bh.b_size = 1 << ip->i_inode.i_blkbits; diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 1d3f2fb466b..ee6ffd59041 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -406,7 +406,6 @@ static int gfs2_symlink(struct inode *dir, struct dentry *dentry, ip = ghs[1].gh_gl->gl_object; - ip->i_disksize = size; i_size_write(inode, size); error = gfs2_meta_inode_buffer(ip, &dibh); @@ -461,7 +460,7 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode) ip = ghs[1].gh_gl->gl_object; ip->i_inode.i_nlink = 2; - ip->i_disksize = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode); + i_size_write(inode, sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)); ip->i_diskflags |= GFS2_DIF_JDATA; ip->i_entries = 2; @@ -990,7 +989,7 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) struct gfs2_inode *ip = GFS2_I(dentry->d_inode); struct gfs2_holder i_gh; struct buffer_head *dibh; - unsigned int x; + unsigned int x, size; char *buf; int error; @@ -1002,7 +1001,8 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) return NULL; } - if (!ip->i_disksize) { + size = (unsigned int)i_size_read(&ip->i_inode); + if (size == 0) { gfs2_consist_inode(ip); buf = ERR_PTR(-EIO); goto out; @@ -1014,7 +1014,7 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) goto out; } - x = ip->i_disksize + 1; + x = size + 1; buf = kmalloc(x, GFP_NOFS); if (!buf) buf = ERR_PTR(-ENOMEM); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 1bc6b5695e6..9bc6dd9a544 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -735,10 +735,8 @@ get_a_page: goto out; size = loc + sizeof(struct gfs2_quota); - if (size > inode->i_size) { - ip->i_disksize = size; + if (size > inode->i_size) i_size_write(inode, size); - } inode->i_mtime = inode->i_atime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); @@ -1190,18 +1188,17 @@ static void gfs2_quota_change_in(struct gfs2_quota_change_host *qc, const void * int gfs2_quota_init(struct gfs2_sbd *sdp) { struct gfs2_inode *ip = GFS2_I(sdp->sd_qc_inode); - unsigned int blocks = ip->i_disksize >> sdp->sd_sb.sb_bsize_shift; + u64 size = i_size_read(sdp->sd_qc_inode); + unsigned int blocks = size >> sdp->sd_sb.sb_bsize_shift; unsigned int x, slot = 0; unsigned int found = 0; u64 dblock; u32 extlen = 0; int error; - if (!ip->i_disksize || ip->i_disksize > (64 << 20) || - ip->i_disksize & (sdp->sd_sb.sb_bsize - 1)) { - gfs2_consist_inode(ip); + if (gfs2_check_internal_file_size(sdp->sd_qc_inode, 1, 64 << 20)) return -EIO; - } + sdp->sd_quota_slots = blocks * sdp->sd_qc_per_block; sdp->sd_quota_chunks = DIV_ROUND_UP(sdp->sd_quota_slots, 8 * PAGE_SIZE); diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 171a744f8e4..370c29b536e 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -500,7 +500,7 @@ u64 gfs2_ri_total(struct gfs2_sbd *sdp) for (rgrps = 0;; rgrps++) { loff_t pos = rgrps * sizeof(struct gfs2_rindex); - if (pos + sizeof(struct gfs2_rindex) >= ip->i_disksize) + if (pos + sizeof(struct gfs2_rindex) >= i_size_read(inode)) break; error = gfs2_internal_read(ip, &ra_state, buf, &pos, sizeof(struct gfs2_rindex)); @@ -588,7 +588,7 @@ static int gfs2_ri_update(struct gfs2_inode *ip) struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct inode *inode = &ip->i_inode; struct file_ra_state ra_state; - u64 rgrp_count = ip->i_disksize; + u64 rgrp_count = i_size_read(inode); int error; do_div(rgrp_count, sizeof(struct gfs2_rindex)); @@ -628,7 +628,7 @@ static int gfs2_ri_update_special(struct gfs2_inode *ip) for (sdp->sd_rgrps = 0;; sdp->sd_rgrps++) { /* Ignore partials */ if ((sdp->sd_rgrps + 1) * sizeof(struct gfs2_rindex) > - ip->i_disksize) + i_size_read(inode)) break; error = read_rindex_entry(ip, &ra_state); if (error) { diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 77cb9f830ee..e031fa4965a 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -342,15 +342,14 @@ int gfs2_jdesc_check(struct gfs2_jdesc *jd) { struct gfs2_inode *ip = GFS2_I(jd->jd_inode); struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); + u64 size = i_size_read(jd->jd_inode); - if (ip->i_disksize < (8 << 20) || ip->i_disksize > (1 << 30) || - (ip->i_disksize & (sdp->sd_sb.sb_bsize - 1))) { - gfs2_consist_inode(ip); + if (gfs2_check_internal_file_size(jd->jd_inode, 8 << 20, 1 << 30)) return -EIO; - } - jd->jd_blocks = ip->i_disksize >> sdp->sd_sb.sb_bsize_shift; - if (gfs2_write_alloc_required(ip, 0, ip->i_disksize)) { + jd->jd_blocks = size >> sdp->sd_sb.sb_bsize_shift; + + if (gfs2_write_alloc_required(ip, 0, size)) { gfs2_consist_inode(ip); return -EIO; } -- cgit v1.2.3-18-g5258 From 820969f353587281d645735c83c7f07d606e67ba Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Wed, 11 Aug 2010 09:53:47 +0100 Subject: GFS2: No longer experimental I think the time has arrvied to remove the experimental tag from GFS2. Signed-off-by: Steven Whitehouse --- fs/gfs2/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/gfs2/Kconfig b/fs/gfs2/Kconfig index cc966552214..c465ae066c6 100644 --- a/fs/gfs2/Kconfig +++ b/fs/gfs2/Kconfig @@ -1,6 +1,6 @@ config GFS2_FS tristate "GFS2 file system support" - depends on EXPERIMENTAL && (64BIT || LBDAF) + depends on (64BIT || LBDAF) select DLM if GFS2_FS_LOCKING_DLM select CONFIGFS_FS if GFS2_FS_LOCKING_DLM select SYSFS if GFS2_FS_LOCKING_DLM -- cgit v1.2.3-18-g5258 From 9a3f236d40a99ea8dca3df40d8ef67631057cad6 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 23 Aug 2010 11:49:34 +0100 Subject: GFS2: Add a bug trap in allocation code This adds a check to ensure that if we reach the block allocator that we don't try and proceed if there is no alloc structure hanging off the inode. This should only happen if there is a bug in GFS2. The error return code is distinctive in order that it will be easily spotted. Signed-off-by: Steven Whitehouse --- fs/gfs2/rgrp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 370c29b536e..66b6d4d8b1d 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1496,11 +1496,19 @@ int gfs2_alloc_block(struct gfs2_inode *ip, u64 *bn, unsigned int *n) struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct buffer_head *dibh; struct gfs2_alloc *al = ip->i_alloc; - struct gfs2_rgrpd *rgd = al->al_rgd; + struct gfs2_rgrpd *rgd; u32 goal, blk; u64 block; int error; + /* Only happens if there is a bug in gfs2, return something distinctive + * to ensure that it is noticed. + */ + if (al == NULL) + return -ECANCELED; + + rgd = al->al_rgd; + if (rgrp_contains_block(rgd, ip->i_goal)) goal = ip->i_goal - rgd->rd_data0; else -- cgit v1.2.3-18-g5258 From 3921120e757f9167f3fcd3a1781239824471b14d Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Fri, 20 Aug 2010 00:21:02 -0500 Subject: GFS2: fallocate support This patch adds support for fallocate to gfs2. Since the gfs2 does not support uninitialized data blocks, it must write out zeros to all the blocks. However, since it does not need to lock any pages to read from, gfs2 can write out the zero blocks much more efficiently. On a moderately full filesystem, fallocate works around 5 times faster on average. The fallocate call also allows gfs2 to add blocks to the file without changing the filesize, which will make it possible for gfs2 to preallocate space for the rindex file, so that gfs2 can grow a completely full filesystem. Signed-off-by: Benjamin Marzinski Signed-off-by: Steven Whitehouse --- fs/gfs2/aops.c | 4 +- fs/gfs2/incore.h | 1 + fs/gfs2/inode.h | 2 + fs/gfs2/ops_inode.c | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/gfs2/rgrp.c | 12 +++ fs/gfs2/trans.h | 1 + 6 files changed, 272 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index c92f36ba3fc..180ef8a6de6 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -36,8 +36,8 @@ #include "glops.h" -static void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page, - unsigned int from, unsigned int to) +void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page, + unsigned int from, unsigned int to) { struct buffer_head *head = page_buffers(page); unsigned int bsize = head->b_size; diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index c1197177527..578234bb03f 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -571,6 +571,7 @@ struct gfs2_sbd { struct list_head sd_rindex_mru_list; struct gfs2_rgrpd *sd_rindex_forward; unsigned int sd_rgrps; + unsigned int sd_max_rg_data; /* Journal index stuff */ diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index 15ff4df20aa..6720d7d5fbc 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h @@ -19,6 +19,8 @@ extern int gfs2_releasepage(struct page *page, gfp_t gfp_mask); extern int gfs2_internal_read(struct gfs2_inode *ip, struct file_ra_state *ra_state, char *buf, loff_t *pos, unsigned size); +extern void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page, + unsigned int from, unsigned int to); extern void gfs2_set_aops(struct inode *inode); static inline int gfs2_is_stuffed(const struct gfs2_inode *ip) diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index ee6ffd59041..f6da0d7676e 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include "gfs2.h" @@ -1277,6 +1279,257 @@ static int gfs2_removexattr(struct dentry *dentry, const char *name) return ret; } +static void empty_write_end(struct page *page, unsigned from, + unsigned to) +{ + struct gfs2_inode *ip = GFS2_I(page->mapping->host); + + page_zero_new_buffers(page, from, to); + flush_dcache_page(page); + mark_page_accessed(page); + + if (!gfs2_is_writeback(ip)) + gfs2_page_add_databufs(ip, page, from, to); + + block_commit_write(page, from, to); +} + + +static int write_empty_blocks(struct page *page, unsigned from, unsigned to) +{ + unsigned start, end, next; + struct buffer_head *bh, *head; + int error; + + if (!page_has_buffers(page)) { + error = block_prepare_write(page, from, to, gfs2_block_map); + if (unlikely(error)) + return error; + + empty_write_end(page, from, to); + return 0; + } + + bh = head = page_buffers(page); + next = end = 0; + while (next < from) { + next += bh->b_size; + bh = bh->b_this_page; + } + start = next; + do { + next += bh->b_size; + if (buffer_mapped(bh)) { + if (end) { + error = block_prepare_write(page, start, end, + gfs2_block_map); + if (unlikely(error)) + return error; + empty_write_end(page, start, end); + end = 0; + } + start = next; + } + else + end = next; + bh = bh->b_this_page; + } while (next < to); + + if (end) { + error = block_prepare_write(page, start, end, gfs2_block_map); + if (unlikely(error)) + return error; + empty_write_end(page, start, end); + } + + return 0; +} + +static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len, + int mode) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct buffer_head *dibh; + int error; + u64 start = offset >> PAGE_CACHE_SHIFT; + unsigned int start_offset = offset & ~PAGE_CACHE_MASK; + u64 end = (offset + len - 1) >> PAGE_CACHE_SHIFT; + pgoff_t curr; + struct page *page; + unsigned int end_offset = (offset + len) & ~PAGE_CACHE_MASK; + unsigned int from, to; + + if (!end_offset) + end_offset = PAGE_CACHE_SIZE; + + error = gfs2_meta_inode_buffer(ip, &dibh); + if (unlikely(error)) + goto out; + + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + + if (gfs2_is_stuffed(ip)) { + error = gfs2_unstuff_dinode(ip, NULL); + if (unlikely(error)) + goto out; + } + + curr = start; + offset = start << PAGE_CACHE_SHIFT; + from = start_offset; + to = PAGE_CACHE_SIZE; + while (curr <= end) { + page = grab_cache_page_write_begin(inode->i_mapping, curr, + AOP_FLAG_NOFS); + if (unlikely(!page)) { + error = -ENOMEM; + goto out; + } + + if (curr == end) + to = end_offset; + error = write_empty_blocks(page, from, to); + if (!error && offset + to > inode->i_size && + !(mode & FALLOC_FL_KEEP_SIZE)) { + i_size_write(inode, offset + to); + } + unlock_page(page); + page_cache_release(page); + if (error) + goto out; + curr++; + offset += PAGE_CACHE_SIZE; + from = 0; + } + + gfs2_dinode_out(ip, dibh->b_data); + mark_inode_dirty(inode); + + brelse(dibh); + +out: + return error; +} + +static void calc_max_reserv(struct gfs2_inode *ip, loff_t max, loff_t *len, + unsigned int *data_blocks, unsigned int *ind_blocks) +{ + const struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + unsigned int max_blocks = ip->i_alloc->al_rgd->rd_free_clone; + unsigned int tmp, max_data = max_blocks - 3 * (sdp->sd_max_height - 1); + + for (tmp = max_data; tmp > sdp->sd_diptrs;) { + tmp = DIV_ROUND_UP(tmp, sdp->sd_inptrs); + max_data -= tmp; + } + /* This calculation isn't the exact reverse of gfs2_write_calc_reserve, + so it might end up with fewer data blocks */ + if (max_data <= *data_blocks) + return; + *data_blocks = max_data; + *ind_blocks = max_blocks - max_data; + *len = ((loff_t)max_data - 3) << sdp->sd_sb.sb_bsize_shift; + if (*len > max) { + *len = max; + gfs2_write_calc_reserv(ip, max, data_blocks, ind_blocks); + } +} + +static long gfs2_fallocate(struct inode *inode, int mode, loff_t offset, + loff_t len) +{ + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct gfs2_inode *ip = GFS2_I(inode); + unsigned int data_blocks = 0, ind_blocks = 0, rblocks; + loff_t bytes, max_bytes; + struct gfs2_alloc *al; + int error; + loff_t next = (offset + len - 1) >> sdp->sd_sb.sb_bsize_shift; + next = (next + 1) << sdp->sd_sb.sb_bsize_shift; + + offset = (offset >> sdp->sd_sb.sb_bsize_shift) << + sdp->sd_sb.sb_bsize_shift; + + len = next - offset; + bytes = sdp->sd_max_rg_data * sdp->sd_sb.sb_bsize / 2; + if (!bytes) + bytes = UINT_MAX; + + gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ip->i_gh); + error = gfs2_glock_nq(&ip->i_gh); + if (unlikely(error)) + goto out_uninit; + + if (!gfs2_write_alloc_required(ip, offset, len)) + goto out_unlock; + + while (len > 0) { + if (len < bytes) + bytes = len; + al = gfs2_alloc_get(ip); + if (!al) { + error = -ENOMEM; + goto out_unlock; + } + + error = gfs2_quota_lock_check(ip); + if (error) + goto out_alloc_put; + +retry: + gfs2_write_calc_reserv(ip, bytes, &data_blocks, &ind_blocks); + + al->al_requested = data_blocks + ind_blocks; + error = gfs2_inplace_reserve(ip); + if (error) { + if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) { + bytes >>= 1; + goto retry; + } + goto out_qunlock; + } + max_bytes = bytes; + calc_max_reserv(ip, len, &max_bytes, &data_blocks, &ind_blocks); + al->al_requested = data_blocks + ind_blocks; + + rblocks = RES_DINODE + ind_blocks + RES_STATFS + RES_QUOTA + + RES_RG_HDR + ip->i_alloc->al_rgd->rd_length; + if (gfs2_is_jdata(ip)) + rblocks += data_blocks ? data_blocks : 1; + + error = gfs2_trans_begin(sdp, rblocks, + PAGE_CACHE_SIZE/sdp->sd_sb.sb_bsize); + if (error) + goto out_trans_fail; + + error = fallocate_chunk(inode, offset, max_bytes, mode); + gfs2_trans_end(sdp); + + if (error) + goto out_trans_fail; + + len -= max_bytes; + offset += max_bytes; + gfs2_inplace_release(ip); + gfs2_quota_unlock(ip); + gfs2_alloc_put(ip); + } + goto out_unlock; + +out_trans_fail: + gfs2_inplace_release(ip); +out_qunlock: + gfs2_quota_unlock(ip); +out_alloc_put: + gfs2_alloc_put(ip); +out_unlock: + gfs2_glock_dq(&ip->i_gh); +out_uninit: + gfs2_holder_uninit(&ip->i_gh); + return error; +} + + static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { @@ -1327,6 +1580,7 @@ const struct inode_operations gfs2_file_iops = { .getxattr = gfs2_getxattr, .listxattr = gfs2_listxattr, .removexattr = gfs2_removexattr, + .fallocate = gfs2_fallocate, .fiemap = gfs2_fiemap, }; diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 66b6d4d8b1d..f9ddcf40175 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -589,6 +589,8 @@ static int gfs2_ri_update(struct gfs2_inode *ip) struct inode *inode = &ip->i_inode; struct file_ra_state ra_state; u64 rgrp_count = i_size_read(inode); + struct gfs2_rgrpd *rgd; + unsigned int max_data = 0; int error; do_div(rgrp_count, sizeof(struct gfs2_rindex)); @@ -603,6 +605,10 @@ static int gfs2_ri_update(struct gfs2_inode *ip) } } + list_for_each_entry(rgd, &sdp->sd_rindex_list, rd_list) + if (rgd->rd_data > max_data) + max_data = rgd->rd_data; + sdp->sd_max_rg_data = max_data; sdp->sd_rindex_uptodate = 1; return 0; } @@ -622,6 +628,8 @@ static int gfs2_ri_update_special(struct gfs2_inode *ip) struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct inode *inode = &ip->i_inode; struct file_ra_state ra_state; + struct gfs2_rgrpd *rgd; + unsigned int max_data = 0; int error; file_ra_state_init(&ra_state, inode->i_mapping); @@ -636,6 +644,10 @@ static int gfs2_ri_update_special(struct gfs2_inode *ip) return error; } } + list_for_each_entry(rgd, &sdp->sd_rindex_list, rd_list) + if (rgd->rd_data > max_data) + max_data = rgd->rd_data; + sdp->sd_max_rg_data = max_data; sdp->sd_rindex_uptodate = 1; return 0; diff --git a/fs/gfs2/trans.h b/fs/gfs2/trans.h index edf9d4bd908..b849eb7ad37 100644 --- a/fs/gfs2/trans.h +++ b/fs/gfs2/trans.h @@ -20,6 +20,7 @@ struct gfs2_glock; #define RES_JDATA 1 #define RES_DATA 1 #define RES_LEAF 1 +#define RES_RG_HDR 1 #define RES_RG_BIT 2 #define RES_EATTR 1 #define RES_STATFS 1 -- cgit v1.2.3-18-g5258 From fe08d5a89726675a920b0e9bbbe849c46b27a6e5 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 23 Aug 2010 11:54:45 +0100 Subject: GFS2: Fix whitespace in previous patch Removes the offending space Signed-off-by: Steven Whitehouse --- fs/gfs2/ops_inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index f6da0d7676e..ce4f1dfb533 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -1423,7 +1423,7 @@ static void calc_max_reserv(struct gfs2_inode *ip, loff_t max, loff_t *len, max_data -= tmp; } /* This calculation isn't the exact reverse of gfs2_write_calc_reserve, - so it might end up with fewer data blocks */ + so it might end up with fewer data blocks */ if (max_data <= *data_blocks) return; *data_blocks = max_data; -- cgit v1.2.3-18-g5258 From 7b5e3d5fcf0d6fce66050bd0313a7dc2ae4abc62 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Fri, 3 Sep 2010 09:39:20 +0100 Subject: GFS2: Don't enforce min hold time when two demotes occur in rapid succession Due to the design of the VFS, it is quite usual for operations on GFS2 to consist of a lookup (requiring a shared lock) followed by an operation requiring an exclusive lock. If a remote node has cached an exclusive lock, then it will receive two demote events in rapid succession firstly for a shared lock and then to unlocked. The existing min hold time code was triggering in this case, even if the node was otherwise idle since the state change time was being updated by the initial demote. This patch introduces logic to skip the min hold timer in the case that a "double demote" of this kind has occurred. The min hold timer will still be used in all other cases. A new glock flag is introduced which is used to keep track of whether there have been any newly queued holders since the last glock state change. The min hold time is only applied if the flag is set. Signed-off-by: Steven Whitehouse Tested-by: Abhijith Das --- fs/gfs2/glock.c | 15 +++++++++++---- fs/gfs2/incore.h | 1 + fs/gfs2/trace_gfs2.h | 3 ++- 3 files changed, 14 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 9adf8f924e0..8e478e27f1f 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -441,6 +441,8 @@ static void state_change(struct gfs2_glock *gl, unsigned int new_state) else gfs2_glock_put_nolock(gl); } + if (held1 && held2 && list_empty(&gl->gl_holders)) + clear_bit(GLF_QUEUED, &gl->gl_flags); gl->gl_state = new_state; gl->gl_tchange = jiffies; @@ -1012,6 +1014,7 @@ fail: if (unlikely((gh->gh_flags & LM_FLAG_PRIORITY) && !insert_pt)) insert_pt = &gh2->gh_list; } + set_bit(GLF_QUEUED, &gl->gl_flags); if (likely(insert_pt == NULL)) { list_add_tail(&gh->gh_list, &gl->gl_holders); if (unlikely(gh->gh_flags & LM_FLAG_PRIORITY)) @@ -1310,10 +1313,12 @@ void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state) gfs2_glock_hold(gl); holdtime = gl->gl_tchange + gl->gl_ops->go_min_hold_time; - if (time_before(now, holdtime)) - delay = holdtime - now; - if (test_bit(GLF_REPLY_PENDING, &gl->gl_flags)) - delay = gl->gl_ops->go_min_hold_time; + if (test_bit(GLF_QUEUED, &gl->gl_flags)) { + if (time_before(now, holdtime)) + delay = holdtime - now; + if (test_bit(GLF_REPLY_PENDING, &gl->gl_flags)) + delay = gl->gl_ops->go_min_hold_time; + } spin_lock(&gl->gl_spin); handle_callback(gl, state, delay); @@ -1660,6 +1665,8 @@ static const char *gflags2str(char *buf, const unsigned long *gflags) *p++ = 'I'; if (test_bit(GLF_FROZEN, gflags)) *p++ = 'F'; + if (test_bit(GLF_QUEUED, gflags)) + *p++ = 'q'; *p = 0; return buf; } diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 578234bb03f..b12ca10481e 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -196,6 +196,7 @@ enum { GLF_REPLY_PENDING = 9, GLF_INITIAL = 10, GLF_FROZEN = 11, + GLF_QUEUED = 12, }; struct gfs2_glock { diff --git a/fs/gfs2/trace_gfs2.h b/fs/gfs2/trace_gfs2.h index 148d55c1417..cedb0bb96d9 100644 --- a/fs/gfs2/trace_gfs2.h +++ b/fs/gfs2/trace_gfs2.h @@ -39,7 +39,8 @@ {(1UL << GLF_INVALIDATE_IN_PROGRESS), "i" }, \ {(1UL << GLF_REPLY_PENDING), "r" }, \ {(1UL << GLF_INITIAL), "I" }, \ - {(1UL << GLF_FROZEN), "F" }) + {(1UL << GLF_FROZEN), "F" }, \ + {(1UL << GLF_QUEUED), "q" }) #ifndef NUMPTY #define NUMPTY -- cgit v1.2.3-18-g5258 From 1fea7c25a05d388c0cdbe02cbdaf3a2e70885581 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Wed, 8 Sep 2010 10:09:25 +0100 Subject: GFS2: Update handling of DLM return codes to match reality GFS2's idea of which return codes it needs to handle was based upon those listed in dlm.h. Those didn't cover all the possible codes and listed some which never happen. This updates GFS2 to handle all the codes which can actually be returned from the DLM under various circumstances. Signed-off-by: Steven Whitehouse --- fs/gfs2/lock_dlm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 0e0470ed34c..1c09425b45f 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -42,9 +42,9 @@ static void gdlm_ast(void *arg) ret |= LM_OUT_CANCELED; goto out; case -EAGAIN: /* Try lock fails */ + case -EDEADLK: /* Deadlock detected */ goto out; - case -EINVAL: /* Invalid */ - case -ENOMEM: /* Out of memory */ + case -ETIMEDOUT: /* Canceled due to timeout */ ret |= LM_OUT_ERROR; goto out; case 0: /* Success */ -- cgit v1.2.3-18-g5258 From 9fa0ea9f26f64fbfc3dfd51d1dc2c230b65ffb19 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 13 Sep 2010 16:23:00 +0100 Subject: GFS2: Use new workqueue scheme The recovery workqueue can be freezable since we want it to finish what it is doing if the system is to be frozen (although why you'd want to freeze a cluster node is beyond me since it will result in it being ejected from the cluster). It does still make sense for single node GFS2 filesystems though. The glock workqueue will benefit from being able to run more work items concurrently. A test running postmark shows improved performance and multi-threaded workloads are likely to benefit even more. It needs to be high priority because the latency directly affects the latency of filesystem glock operations. The delete workqueue is similar to the recovery workqueue in that it must not get blocked by memory allocations, and may run for a long time. Potentially other GFS2 threads might also be converted to workqueues, but I'll leave that for a later patch. Signed-off-by: Steven Whitehouse Acked-by: Tejun Heo --- fs/gfs2/glock.c | 6 ++++-- fs/gfs2/main.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 8e478e27f1f..c3f2a5cc8ef 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1783,10 +1783,12 @@ int __init gfs2_glock_init(void) } #endif - glock_workqueue = create_workqueue("glock_workqueue"); + glock_workqueue = alloc_workqueue("glock_workqueue", WQ_RESCUER | + WQ_HIGHPRI | WQ_FREEZEABLE, 0); if (IS_ERR(glock_workqueue)) return PTR_ERR(glock_workqueue); - gfs2_delete_workqueue = create_workqueue("delete_workqueue"); + gfs2_delete_workqueue = alloc_workqueue("delete_workqueue", WQ_RESCUER | + WQ_FREEZEABLE, 0); if (IS_ERR(gfs2_delete_workqueue)) { destroy_workqueue(glock_workqueue); return PTR_ERR(gfs2_delete_workqueue); diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index b1e9630eb46..1c8bbf29e60 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -140,7 +140,7 @@ static int __init init_gfs2_fs(void) error = -ENOMEM; gfs_recovery_wq = alloc_workqueue("gfs_recovery", - WQ_NON_REENTRANT | WQ_RESCUER, 0); + WQ_RESCUER | WQ_FREEZEABLE, 0); if (!gfs_recovery_wq) goto fail_wq; -- cgit v1.2.3-18-g5258 From 8d1235852b462cfb66aa036bd4a2686763c69ed4 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Fri, 17 Sep 2010 12:30:23 +0100 Subject: GFS2: Make . and .. qstrs constant Rather than calculating the qstrs for . and .. each time we need them, its better to keep a constant version of these and just refer to them when required. Signed-off-by: Steven Whitehouse Reviewed-by: Christoph Hellwig --- fs/gfs2/dir.c | 3 +++ fs/gfs2/dir.h | 34 +++++++++++++++++++--------------- fs/gfs2/export.c | 9 +-------- fs/gfs2/main.c | 4 ++++ fs/gfs2/ops_inode.c | 24 ++++++------------------ 5 files changed, 33 insertions(+), 41 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index c1042ae438c..5c356d09c32 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -79,6 +79,9 @@ #define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1) #define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1)) +struct qstr gfs2_qdot __read_mostly; +struct qstr gfs2_qdotdot __read_mostly; + typedef int (*leaf_call_t) (struct gfs2_inode *dip, u32 index, u32 len, u64 leaf_no, void *data); typedef int (*gfs2_dscan_t)(const struct gfs2_dirent *dent, diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h index 4f919440c3b..a98f644bd3d 100644 --- a/fs/gfs2/dir.h +++ b/fs/gfs2/dir.h @@ -17,23 +17,24 @@ struct inode; struct gfs2_inode; struct gfs2_inum; -struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *filename); -int gfs2_dir_check(struct inode *dir, const struct qstr *filename, - const struct gfs2_inode *ip); -int gfs2_dir_add(struct inode *inode, const struct qstr *filename, - const struct gfs2_inode *ip, unsigned int type); -int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *filename); -int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, - filldir_t filldir); -int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, - const struct gfs2_inode *nip, unsigned int new_type); +extern struct inode *gfs2_dir_search(struct inode *dir, + const struct qstr *filename); +extern int gfs2_dir_check(struct inode *dir, const struct qstr *filename, + const struct gfs2_inode *ip); +extern int gfs2_dir_add(struct inode *inode, const struct qstr *filename, + const struct gfs2_inode *ip, unsigned int type); +extern int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *filename); +extern int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, + filldir_t filldir); +extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, + const struct gfs2_inode *nip, unsigned int new_type); -int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip); +extern int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip); -int gfs2_diradd_alloc_required(struct inode *dir, - const struct qstr *filename); -int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block, - struct buffer_head **bhp); +extern int gfs2_diradd_alloc_required(struct inode *dir, + const struct qstr *filename); +extern int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block, + struct buffer_head **bhp); static inline u32 gfs2_disk_hash(const char *data, int len) { @@ -61,4 +62,7 @@ static inline void gfs2_qstr2dirent(const struct qstr *name, u16 reclen, struct memcpy(dent + 1, name->name, name->len); } +extern struct qstr gfs2_qdot; +extern struct qstr gfs2_qdotdot; + #endif /* __DIR_DOT_H__ */ diff --git a/fs/gfs2/export.c b/fs/gfs2/export.c index dfe237a3f8a..06d582732d3 100644 --- a/fs/gfs2/export.c +++ b/fs/gfs2/export.c @@ -126,16 +126,9 @@ static int gfs2_get_name(struct dentry *parent, char *name, static struct dentry *gfs2_get_parent(struct dentry *child) { - struct qstr dotdot; struct dentry *dentry; - /* - * XXX(hch): it would be a good idea to keep this around as a - * static variable. - */ - gfs2_str2qstr(&dotdot, ".."); - - dentry = d_obtain_alias(gfs2_lookupi(child->d_inode, &dotdot, 1)); + dentry = d_obtain_alias(gfs2_lookupi(child->d_inode, &gfs2_qdotdot, 1)); if (!IS_ERR(dentry)) dentry->d_op = &gfs2_dops; return dentry; diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index 1c8bbf29e60..d7eb1e209aa 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -24,6 +24,7 @@ #include "glock.h" #include "quota.h" #include "recovery.h" +#include "dir.h" static struct shrinker qd_shrinker = { .shrink = gfs2_shrink_qd_memory, @@ -78,6 +79,9 @@ static int __init init_gfs2_fs(void) { int error; + gfs2_str2qstr(&gfs2_qdot, "."); + gfs2_str2qstr(&gfs2_qdotdot, ".."); + error = gfs2_sys_init(); if (error) return error; diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index ce4f1dfb533..98a94cfc2bb 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -471,18 +471,15 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode) if (!gfs2_assert_withdraw(sdp, !error)) { struct gfs2_dinode *di = (struct gfs2_dinode *)dibh->b_data; struct gfs2_dirent *dent = (struct gfs2_dirent *)(di+1); - struct qstr str; - gfs2_str2qstr(&str, "."); gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_qstr2dirent(&str, GFS2_DIRENT_SIZE(str.len), dent); + gfs2_qstr2dirent(&gfs2_qdot, GFS2_DIRENT_SIZE(gfs2_qdot.len), dent); dent->de_inum = di->di_num; /* already GFS2 endian */ dent->de_type = cpu_to_be16(DT_DIR); di->di_entries = cpu_to_be32(1); - gfs2_str2qstr(&str, ".."); dent = (struct gfs2_dirent *)((char*)dent + GFS2_DIRENT_SIZE(1)); - gfs2_qstr2dirent(&str, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent); + gfs2_qstr2dirent(&gfs2_qdotdot, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent); gfs2_inum_out(dip, dent); dent->de_type = cpu_to_be16(DT_DIR); @@ -523,7 +520,6 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode) static int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name, struct gfs2_inode *ip) { - struct qstr dotname; int error; if (ip->i_entries != 2) { @@ -540,13 +536,11 @@ static int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name, if (error) return error; - gfs2_str2qstr(&dotname, "."); - error = gfs2_dir_del(ip, &dotname); + error = gfs2_dir_del(ip, &gfs2_qdot); if (error) return error; - gfs2_str2qstr(&dotname, ".."); - error = gfs2_dir_del(ip, &dotname); + error = gfs2_dir_del(ip, &gfs2_qdotdot); if (error) return error; @@ -695,11 +689,8 @@ static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to) struct inode *dir = &to->i_inode; struct super_block *sb = dir->i_sb; struct inode *tmp; - struct qstr dotdot; int error = 0; - gfs2_str2qstr(&dotdot, ".."); - igrab(dir); for (;;) { @@ -712,7 +703,7 @@ static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to) break; } - tmp = gfs2_lookupi(dir, &dotdot, 1); + tmp = gfs2_lookupi(dir, &gfs2_qdotdot, 1); if (IS_ERR(tmp)) { error = PTR_ERR(tmp); break; @@ -921,9 +912,6 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, } if (dir_rename) { - struct qstr name; - gfs2_str2qstr(&name, ".."); - error = gfs2_change_nlink(ndip, +1); if (error) goto out_end_trans; @@ -931,7 +919,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (error) goto out_end_trans; - error = gfs2_dir_mvino(ip, &name, ndip, DT_DIR); + error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); if (error) goto out_end_trans; } else { -- cgit v1.2.3-18-g5258 From 749ef9f8423054e326f3a246327ed2db4b6d395f Mon Sep 17 00:00:00 2001 From: Corrado Zoccolo Date: Mon, 20 Sep 2010 15:24:50 +0200 Subject: cfq: improve fsync performance for small files Fsync performance for small files achieved by cfq on high-end disks is lower than what deadline can achieve, due to idling introduced between the sync write happening in process context and the journal commit. Moreover, when competing with a sequential reader, a process writing small files and fsync-ing them is starved. This patch fixes the two problems by: - marking journal commits as WRITE_SYNC, so that they get the REQ_NOIDLE flag set, - force all queues that have REQ_NOIDLE requests to be put in the noidle tree. Having the queue associated to the fsync-ing process and the one associated to journal commits in the noidle tree allows: - switching between them without idling, - fairness vs. competing idling queues, since they will be serviced only after the noidle tree expires its slice. Acked-by: Vivek Goyal Reviewed-by: Jeff Moyer Tested-by: Jeff Moyer Signed-off-by: Corrado Zoccolo Signed-off-by: Jens Axboe --- fs/jbd/commit.c | 2 +- fs/jbd2/commit.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 95d8c11c929..3f030e9efea 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -318,7 +318,7 @@ void journal_commit_transaction(journal_t *journal) int first_tag = 0; int tag_flag; int i; - int write_op = WRITE; + int write_op = WRITE_SYNC; /* * First job: lock down the current transaction and wait for diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 7c068c189d8..80910f51d4b 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -360,7 +360,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) int tag_bytes = journal_tag_bytes(journal); struct buffer_head *cbh = NULL; /* For transactional checksums */ __u32 crc32_sum = ~0; - int write_op = WRITE; + int write_op = WRITE_SYNC; /* * First job: lock down the current transaction and wait for -- cgit v1.2.3-18-g5258 From f57a024ed2ee00d62515ffd4040f5ded3e0eb853 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Thu, 23 Sep 2010 13:41:42 +0100 Subject: GFS2: Remove ignore_local_fs mount argument This is been a no-op for a very long time now. I'm pretty sure nobody uses it, but just in case we'll still accept it on the command line, but ignore it. Signed-off-by: Steven Whitehouse --- fs/gfs2/incore.h | 1 - fs/gfs2/super.c | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index b12ca10481e..c8a2db1bfe6 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -416,7 +416,6 @@ struct gfs2_args { char ar_locktable[GFS2_LOCKNAME_LEN]; /* Name of the Lock Table */ char ar_hostdata[GFS2_LOCKNAME_LEN]; /* Host specific data */ unsigned int ar_spectator:1; /* Don't get a journal */ - unsigned int ar_ignore_local_fs:1; /* Ignore optimisations */ unsigned int ar_localflocks:1; /* Let the VFS do flock|fcntl */ unsigned int ar_localcaching:1; /* Local caching */ unsigned int ar_debug:1; /* Oops on errors */ diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index e031fa4965a..06a4a7e8cf7 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -159,7 +159,7 @@ int gfs2_mount_args(struct gfs2_args *args, char *options) args->ar_spectator = 1; break; case Opt_ignore_local_fs: - args->ar_ignore_local_fs = 1; + /* Retained for backwards compat only */ break; case Opt_localflocks: args->ar_localflocks = 1; @@ -1128,7 +1128,6 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data) /* Some flags must not be changed */ if (args_neq(&args, &sdp->sd_args, spectator) || - args_neq(&args, &sdp->sd_args, ignore_local_fs) || args_neq(&args, &sdp->sd_args, localflocks) || args_neq(&args, &sdp->sd_args, localcaching) || args_neq(&args, &sdp->sd_args, meta)) @@ -1233,8 +1232,6 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt) seq_printf(s, ",hostdata=%s", args->ar_hostdata); if (args->ar_spectator) seq_printf(s, ",spectator"); - if (args->ar_ignore_local_fs) - seq_printf(s, ",ignore_local_fs"); if (args->ar_localflocks) seq_printf(s, ",localflocks"); if (args->ar_localcaching) -- cgit v1.2.3-18-g5258 From c2048b003cfb840ad81bdc6eb55beb12a19a222e Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Thu, 23 Sep 2010 14:00:31 +0100 Subject: GFS2: Remove localcaching mount option This option defaulted to on for lock_nolock mounts and off otherwise. The only function was to avoid the revalidation of dentries. In the cluster case, that is entirely pointless and liable to cause coherency problems. The patch changes the revalidation to depend upon whether the fs is a local or cluster fs (i.e. it follows the existing default behaviour). I very much doubt anybody ever used this option as there is no reason to. Even so we will continue to accept it on the mount command line, but ignore it. Signed-off-by: Steven Whitehouse --- fs/gfs2/dentry.c | 2 +- fs/gfs2/incore.h | 1 - fs/gfs2/ops_fstype.c | 1 - fs/gfs2/super.c | 5 +---- 4 files changed, 2 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c index bb7907bde3d..6798755b385 100644 --- a/fs/gfs2/dentry.c +++ b/fs/gfs2/dentry.c @@ -49,7 +49,7 @@ static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) ip = GFS2_I(inode); } - if (sdp->sd_args.ar_localcaching) + if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) goto valid; had_lock = (gfs2_glock_is_locked_by_me(dip->i_gl) != NULL); diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index c8a2db1bfe6..2990a0a57e6 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -417,7 +417,6 @@ struct gfs2_args { char ar_hostdata[GFS2_LOCKNAME_LEN]; /* Host specific data */ unsigned int ar_spectator:1; /* Don't get a journal */ unsigned int ar_localflocks:1; /* Let the VFS do flock|fcntl */ - unsigned int ar_localcaching:1; /* Local caching */ unsigned int ar_debug:1; /* Oops on errors */ unsigned int ar_upgrade:1; /* Upgrade ondisk format */ unsigned int ar_posix_acl:1; /* Enable posix acls */ diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 5b5c87dfbfe..558bba493cc 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1022,7 +1022,6 @@ static int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent) if (!strcmp("lock_nolock", proto)) { lm = &nolock_ops; sdp->sd_args.ar_localflocks = 1; - sdp->sd_args.ar_localcaching = 1; #ifdef CONFIG_GFS2_FS_LOCKING_DLM } else if (!strcmp("lock_dlm", proto)) { lm = &gfs2_dlm_ops; diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 06a4a7e8cf7..e78de8bf272 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -165,7 +165,7 @@ int gfs2_mount_args(struct gfs2_args *args, char *options) args->ar_localflocks = 1; break; case Opt_localcaching: - args->ar_localcaching = 1; + /* Retained for backwards compat only */ break; case Opt_debug: if (args->ar_errors == GFS2_ERRORS_PANIC) { @@ -1129,7 +1129,6 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data) /* Some flags must not be changed */ if (args_neq(&args, &sdp->sd_args, spectator) || args_neq(&args, &sdp->sd_args, localflocks) || - args_neq(&args, &sdp->sd_args, localcaching) || args_neq(&args, &sdp->sd_args, meta)) return -EINVAL; @@ -1234,8 +1233,6 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt) seq_printf(s, ",spectator"); if (args->ar_localflocks) seq_printf(s, ",localflocks"); - if (args->ar_localcaching) - seq_printf(s, ",localcaching"); if (args->ar_debug) seq_printf(s, ",debug"); if (args->ar_upgrade) -- cgit v1.2.3-18-g5258 From c80dbb58f9c3f84372e37dfe80b41810052ad62f Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Fri, 24 Sep 2010 09:55:07 +0100 Subject: GFS2: Remove upgrade mount option This option has never done anything useful. Also at the same time this cleans up the sb checks which are done at mount time. The debug option will be accepted, but ignored in future. Since it didn't do anything, there didn't seem much point in retaining it. Signed-off-by: Steven Whitehouse --- fs/gfs2/incore.h | 1 - fs/gfs2/ops_fstype.c | 60 ++-------------------------------------------------- fs/gfs2/super.c | 4 +--- 3 files changed, 3 insertions(+), 62 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 2990a0a57e6..6f6ff8aba99 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -418,7 +418,6 @@ struct gfs2_args { unsigned int ar_spectator:1; /* Don't get a journal */ unsigned int ar_localflocks:1; /* Let the VFS do flock|fcntl */ unsigned int ar_debug:1; /* Oops on errors */ - unsigned int ar_upgrade:1; /* Upgrade ondisk format */ unsigned int ar_posix_acl:1; /* Enable posix acls */ unsigned int ar_quota:2; /* off/account/on */ unsigned int ar_suiddir:1; /* suiddir support */ diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 558bba493cc..5b11f376814 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -38,14 +38,6 @@ #define DO 0 #define UNDO 1 -static const u32 gfs2_old_fs_formats[] = { - 0 -}; - -static const u32 gfs2_old_multihost_formats[] = { - 0 -}; - /** * gfs2_tune_init - Fill a gfs2_tune structure with default values * @gt: tune @@ -135,8 +127,6 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) static int gfs2_check_sb(struct gfs2_sbd *sdp, struct gfs2_sb_host *sb, int silent) { - unsigned int x; - if (sb->sb_magic != GFS2_MAGIC || sb->sb_type != GFS2_METATYPE_SB) { if (!silent) @@ -150,55 +140,9 @@ static int gfs2_check_sb(struct gfs2_sbd *sdp, struct gfs2_sb_host *sb, int sile sb->sb_multihost_format == GFS2_FORMAT_MULTI) return 0; - if (sb->sb_fs_format != GFS2_FORMAT_FS) { - for (x = 0; gfs2_old_fs_formats[x]; x++) - if (gfs2_old_fs_formats[x] == sb->sb_fs_format) - break; - - if (!gfs2_old_fs_formats[x]) { - printk(KERN_WARNING - "GFS2: code version (%u, %u) is incompatible " - "with ondisk format (%u, %u)\n", - GFS2_FORMAT_FS, GFS2_FORMAT_MULTI, - sb->sb_fs_format, sb->sb_multihost_format); - printk(KERN_WARNING - "GFS2: I don't know how to upgrade this FS\n"); - return -EINVAL; - } - } - - if (sb->sb_multihost_format != GFS2_FORMAT_MULTI) { - for (x = 0; gfs2_old_multihost_formats[x]; x++) - if (gfs2_old_multihost_formats[x] == - sb->sb_multihost_format) - break; - - if (!gfs2_old_multihost_formats[x]) { - printk(KERN_WARNING - "GFS2: code version (%u, %u) is incompatible " - "with ondisk format (%u, %u)\n", - GFS2_FORMAT_FS, GFS2_FORMAT_MULTI, - sb->sb_fs_format, sb->sb_multihost_format); - printk(KERN_WARNING - "GFS2: I don't know how to upgrade this FS\n"); - return -EINVAL; - } - } + fs_warn(sdp, "Unknown on-disk format, unable to mount\n"); - if (!sdp->sd_args.ar_upgrade) { - printk(KERN_WARNING - "GFS2: code version (%u, %u) is incompatible " - "with ondisk format (%u, %u)\n", - GFS2_FORMAT_FS, GFS2_FORMAT_MULTI, - sb->sb_fs_format, sb->sb_multihost_format); - printk(KERN_INFO - "GFS2: Use the \"upgrade\" mount option to upgrade " - "the FS\n"); - printk(KERN_INFO "GFS2: See the manual for more details\n"); - return -EINVAL; - } - - return 0; + return -EINVAL; } static void end_bio_io_page(struct bio *bio, int error) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index e78de8bf272..d85e0b7c1bb 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -179,7 +179,7 @@ int gfs2_mount_args(struct gfs2_args *args, char *options) args->ar_debug = 0; break; case Opt_upgrade: - args->ar_upgrade = 1; + /* Retained for backwards compat only */ break; case Opt_acl: args->ar_posix_acl = 1; @@ -1235,8 +1235,6 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt) seq_printf(s, ",localflocks"); if (args->ar_debug) seq_printf(s, ",debug"); - if (args->ar_upgrade) - seq_printf(s, ",upgrade"); if (args->ar_posix_acl) seq_printf(s, ",acl"); if (args->ar_quota != GFS2_QUOTA_DEFAULT) { -- cgit v1.2.3-18-g5258 From d0795f912318f65b800c6b619d749c3bf7c930fb Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 27 Sep 2010 15:58:11 +0100 Subject: GFS2: Fix journal check for spectator mounts When checking journals for spectator mounts, we cannot rely on the journal being locked, whatever its jid might be. This patch ensures that we always get the journal locks when checking journals for a spectator mount. Signed-off-by: Steven Whitehouse --- fs/gfs2/recovery.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index f7f89a94a5a..666548e1459 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -456,7 +456,8 @@ void gfs2_recover_func(struct work_struct *work) unsigned int pass; int error; - if (jd->jd_jid != sdp->sd_lockstruct.ls_jid) { + if (sdp->sd_args.ar_spectator || + (jd->jd_jid != sdp->sd_lockstruct.ls_jid)) { fs_info(sdp, "jid=%u: Trying to acquire journal lock...\n", jd->jd_jid); -- cgit v1.2.3-18-g5258 From 54dd55a406f6e9cb5ae208f258b907455162e045 Mon Sep 17 00:00:00 2001 From: Steffen Sledz Date: Mon, 27 Sep 2010 14:20:26 +0200 Subject: UBIFS: avoid kernel error if ubifs superblock read fails .get_sb is called on mounts with automatic fs detection too, so this function should print an error if it cannot read the superblock in debug mode only (new behaviour conforms the other fs types) Signed-off-by: Steffen Sledz Signed-off-by: Artem Bityutskiy --- fs/ubifs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 4e5bf3fbf78..bb6ed5da2d1 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -2054,8 +2054,8 @@ static int ubifs_get_sb(struct file_system_type *fs_type, int flags, */ ubi = open_ubi(name, UBI_READONLY); if (IS_ERR(ubi)) { - ubifs_err("cannot open \"%s\", error %d", - name, (int)PTR_ERR(ubi)); + dbg_err("cannot open \"%s\", error %d", + name, (int)PTR_ERR(ubi)); return PTR_ERR(ubi); } ubi_get_volume_info(ubi, &vi); -- cgit v1.2.3-18-g5258 From bf97b6734e027cc18abad420ab88f861f65d7816 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Mon, 27 Sep 2010 16:00:04 -0500 Subject: GFS2: reserve more blocks for transactions Some of the functions in GFS2 were not reserving space in the transaction for the resource group header and the resource groups bitblocks that get added when you do allocation. GFS2 now makes sure to reserve space for the resource group header and either all the bitblocks in the resource group, or one for each block that it may allocate, whichever is smaller using the new gfs2_rg_blocks() inline function. Signed-off-by: Benjamin Marzinski Signed-off-by: Steven Whitehouse --- fs/gfs2/aops.c | 2 ++ fs/gfs2/bmap.c | 2 +- fs/gfs2/file.c | 4 +++- fs/gfs2/ops_inode.c | 6 +++--- fs/gfs2/quota.c | 3 ++- fs/gfs2/trans.h | 8 ++++++++ fs/gfs2/xattr.c | 2 +- 7 files changed, 20 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 180ef8a6de6..1bf178831ae 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -663,6 +663,8 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping, rblocks += RES_STATFS + RES_QUOTA; if (&ip->i_inode == sdp->sd_rindex) rblocks += 2 * RES_STATFS; + if (alloc_required) + rblocks += gfs2_rg_blocks(al); error = gfs2_trans_begin(sdp, rblocks, PAGE_CACHE_SIZE/sdp->sd_sb.sb_bsize); diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 04513e997df..5476c066d4e 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1151,7 +1151,7 @@ static int do_grow(struct inode *inode, u64 size) goto do_grow_qunlock; } - error = gfs2_trans_begin(sdp, RES_DINODE + 1, 0); + error = gfs2_trans_begin(sdp, RES_DINODE + RES_STATFS + RES_RG_BIT, 0); if (error) goto do_grow_release; diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index daadcd2e755..237ee6a940d 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -382,8 +382,10 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) rblocks = RES_DINODE + ind_blocks; if (gfs2_is_jdata(ip)) rblocks += data_blocks ? data_blocks : 1; - if (ind_blocks || data_blocks) + if (ind_blocks || data_blocks) { rblocks += RES_STATFS + RES_QUOTA; + rblocks += gfs2_rg_blocks(al); + } ret = gfs2_trans_begin(sdp, rblocks, 0); if (ret) goto out_trans_fail; diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 98a94cfc2bb..fba00171d91 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -219,7 +219,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, goto out_gunlock_q; error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_length + + gfs2_rg_blocks(al) + 2 * RES_DINODE + RES_STATFS + RES_QUOTA, 0); if (error) @@ -884,7 +884,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, goto out_gunlock_q; error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + - al->al_rgd->rd_length + + gfs2_rg_blocks(al) + 4 * RES_DINODE + 4 * RES_LEAF + RES_STATFS + RES_QUOTA + 4, 0); if (error) @@ -1481,7 +1481,7 @@ retry: al->al_requested = data_blocks + ind_blocks; rblocks = RES_DINODE + ind_blocks + RES_STATFS + RES_QUOTA + - RES_RG_HDR + ip->i_alloc->al_rgd->rd_length; + RES_RG_HDR + gfs2_rg_blocks(al); if (gfs2_is_jdata(ip)) rblocks += data_blocks ? data_blocks : 1; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 9bc6dd9a544..58a9b9998b4 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -815,7 +815,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) goto out_alloc; if (nalloc) - blocks += al->al_rgd->rd_length + nalloc * ind_blocks + RES_STATFS; + blocks += gfs2_rg_blocks(al) + nalloc * ind_blocks + RES_STATFS; error = gfs2_trans_begin(sdp, blocks, 0); if (error) @@ -1586,6 +1586,7 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id, error = gfs2_inplace_reserve(ip); if (error) goto out_alloc; + blocks += gfs2_rg_blocks(al); } error = gfs2_trans_begin(sdp, blocks + RES_DINODE + 1, 0); diff --git a/fs/gfs2/trans.h b/fs/gfs2/trans.h index b849eb7ad37..fb56b783e02 100644 --- a/fs/gfs2/trans.h +++ b/fs/gfs2/trans.h @@ -26,6 +26,14 @@ struct gfs2_glock; #define RES_STATFS 1 #define RES_QUOTA 2 +/* reserve either the number of blocks to be allocated plus the rg header + * block, or all of the blocks in the rg, whichever is smaller */ +static inline unsigned int gfs2_rg_blocks(const struct gfs2_alloc *al) +{ + return (al->al_requested < al->al_rgd->rd_length)? + al->al_requested + 1 : al->al_rgd->rd_length; +} + int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks, unsigned int revokes); diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c index 776af6eb4bc..30b58f07c8a 100644 --- a/fs/gfs2/xattr.c +++ b/fs/gfs2/xattr.c @@ -734,7 +734,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er, goto out_gunlock_q; error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), - blks + al->al_rgd->rd_length + + blks + gfs2_rg_blocks(al) + RES_DINODE + RES_STATFS + RES_QUOTA, 0); if (error) goto out_ipres; -- cgit v1.2.3-18-g5258 From d594845106f34c079a6b05be01a37e4883c3bf4c Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Tue, 28 Sep 2010 10:17:47 +0100 Subject: GFS2: Fix compiler warning from previous patch This shouldn't really be required, but gcc can't tell that "al" is only accessed when initialised. Signed-off-by: Steven Whitehouse --- fs/gfs2/aops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 1bf178831ae..6b24afb96aa 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -615,7 +615,7 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping, unsigned int data_blocks = 0, ind_blocks = 0, rblocks; int alloc_required; int error = 0; - struct gfs2_alloc *al; + struct gfs2_alloc *al = NULL; pgoff_t index = pos >> PAGE_CACHE_SHIFT; unsigned from = pos & (PAGE_CACHE_SIZE - 1); unsigned to = from + len; -- cgit v1.2.3-18-g5258 From 80168676ebfe4af51407d30f336d67f082d45201 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 24 Sep 2010 18:13:44 +1000 Subject: xfs: force background CIL push under sustained load I have been seeing occasional pauses in transaction throughput up to 30s long under heavy parallel workloads. The only notable thing was that the xfsaild was trying to be active during the pauses, but making no progress. It was running exactly 20 times a second (on the 50ms no-progress backoff), and the number of pushbuf events was constant across this time as well. IOWs, the xfsaild appeared to be stuck on buffers that it could not push out. Further investigation indicated that it was trying to push out inode buffers that were pinned and/or locked. The xfsbufd was also getting woken at the same frequency (by the xfsaild, no doubt) to push out delayed write buffers. The xfsbufd was not making any progress because all the buffers in the delwri queue were pinned. This scan- and-make-no-progress dance went one in the trace for some seconds, before the xfssyncd came along an issued a log force, and then things started going again. However, I noticed something strange about the log force - there were way too many IO's issued. 516 log buffers were written, to be exact. That added up to 129MB of log IO, which got me very interested because it's almost exactly 25% of the size of the log. He delayed logging code is suppose to aggregate the minimum of 25% of the log or 8MB worth of changes before flushing. That's what really puzzled me - why did a log force write 129MB instead of only 8MB? Essentially what has happened is that no CIL pushes had occurred since the previous tail push which cleared out 25% of the log space. That caused all the new transactions to block because there wasn't log space for them, but they kick the xfsaild to push the tail. However, the xfsaild was not making progress because there were buffers it could not lock and flush, and the xfsbufd could not flush them because they were pinned. As a result, both the xfsaild and the xfsbufd could not move the tail of the log forward without the CIL first committing. The cause of the problem was that the background CIL push, which should happen when 8MB of aggregated changes have been committed, is being held off by the concurrent transaction commit load. The background push does a down_write_trylock() which will fail if there is a concurrent transaction commit holding the push lock in read mode. With 8 CPUs all doing transactions as fast as they can, there was enough concurrent transaction commits to hold off the background push until tail-pushing could no longer free log space, and the halt would occur. It should be noted that there is no reason why it would halt at 25% of log space used by a single CIL checkpoint. This bug could definitely violate the "no transaction should be larger than half the log" requirement and hence result in corruption if the system crashed under heavy load. This sort of bug is exactly the reason why delayed logging was tagged as experimental.... The fix is to start blocking background pushes once the threshold has been exceeded. Rework the threshold calculations to keep the amount of log space a CIL checkpoint can use to below that of the AIL push threshold to avoid the problem completely. Signed-off-by: Dave Chinner Reviewed-by: Alex Elder Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_log_cil.c | 12 +++++++++--- fs/xfs/xfs_log_priv.h | 37 +++++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index ed575fb4b49..7e206fc1fa3 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -405,9 +405,15 @@ xlog_cil_push( new_ctx = kmem_zalloc(sizeof(*new_ctx), KM_SLEEP|KM_NOFS); new_ctx->ticket = xlog_cil_ticket_alloc(log); - /* lock out transaction commit, but don't block on background push */ + /* + * Lock out transaction commit, but don't block for background pushes + * unless we are well over the CIL space limit. See the definition of + * XLOG_CIL_HARD_SPACE_LIMIT() for the full explanation of the logic + * used here. + */ if (!down_write_trylock(&cil->xc_ctx_lock)) { - if (!push_seq) + if (!push_seq && + cil->xc_ctx->space_used < XLOG_CIL_HARD_SPACE_LIMIT(log)) goto out_free_ticket; down_write(&cil->xc_ctx_lock); } @@ -422,7 +428,7 @@ xlog_cil_push( goto out_skip; /* check for a previously pushed seqeunce */ - if (push_seq < cil->xc_ctx->sequence) + if (push_seq && push_seq < cil->xc_ctx->sequence) goto out_skip; /* diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h index ced52b98b32..edcdfe01617 100644 --- a/fs/xfs/xfs_log_priv.h +++ b/fs/xfs/xfs_log_priv.h @@ -426,13 +426,13 @@ struct xfs_cil { }; /* - * The amount of log space we should the CIL to aggregate is difficult to size. - * Whatever we chose we have to make we can get a reservation for the log space - * effectively, that it is large enough to capture sufficient relogging to - * reduce log buffer IO significantly, but it is not too large for the log or - * induces too much latency when writing out through the iclogs. We track both - * space consumed and the number of vectors in the checkpoint context, so we - * need to decide which to use for limiting. + * The amount of log space we allow the CIL to aggregate is difficult to size. + * Whatever we choose, we have to make sure we can get a reservation for the + * log space effectively, that it is large enough to capture sufficient + * relogging to reduce log buffer IO significantly, but it is not too large for + * the log or induces too much latency when writing out through the iclogs. We + * track both space consumed and the number of vectors in the checkpoint + * context, so we need to decide which to use for limiting. * * Every log buffer we write out during a push needs a header reserved, which * is at least one sector and more for v2 logs. Hence we need a reservation of @@ -459,16 +459,21 @@ struct xfs_cil { * checkpoint transaction ticket is specific to the checkpoint context, rather * than the CIL itself. * - * With dynamic reservations, we can basically make up arbitrary limits for the - * checkpoint size so long as they don't violate any other size rules. Hence - * the initial maximum size for the checkpoint transaction will be set to a - * quarter of the log or 8MB, which ever is smaller. 8MB is an arbitrary limit - * right now based on the latency of writing out a large amount of data through - * the circular iclog buffers. + * With dynamic reservations, we can effectively make up arbitrary limits for + * the checkpoint size so long as they don't violate any other size rules. + * Recovery imposes a rule that no transaction exceed half the log, so we are + * limited by that. Furthermore, the log transaction reservation subsystem + * tries to keep 25% of the log free, so we need to keep below that limit or we + * risk running out of free log space to start any new transactions. + * + * In order to keep background CIL push efficient, we will set a lower + * threshold at which background pushing is attempted without blocking current + * transaction commits. A separate, higher bound defines when CIL pushes are + * enforced to ensure we stay within our maximum checkpoint size bounds. + * threshold, yet give us plenty of space for aggregation on large logs. */ - -#define XLOG_CIL_SPACE_LIMIT(log) \ - (min((log->l_logsize >> 2), (8 * 1024 * 1024))) +#define XLOG_CIL_SPACE_LIMIT(log) (log->l_logsize >> 3) +#define XLOG_CIL_HARD_SPACE_LIMIT(log) (3 * (log->l_logsize >> 4)) /* * The reservation head lsn is not made up of a cycle number and block number. -- cgit v1.2.3-18-g5258 From c741c4551237f9c1bdcd3b1b39b0883bd19a3723 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Wed, 29 Sep 2010 14:20:52 +0100 Subject: GFS2: Fix spectator umount issue The tests further down the recovery function relating to unlocking the journal need to be updated to match the intial test. Also, a test in the umount code which was surplus to requirements has been removed. Umounting spectator mounts now works correctly, as expected. Signed-off-by: Steven Whitehouse --- fs/gfs2/glock.c | 2 +- fs/gfs2/recovery.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index c3f2a5cc8ef..87778857f09 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1517,7 +1517,7 @@ static void clear_glock(struct gfs2_glock *gl) spin_unlock(&lru_lock); spin_lock(&gl->gl_spin); - if (find_first_holder(gl) == NULL && gl->gl_state != LM_ST_UNLOCKED) + if (gl->gl_state != LM_ST_UNLOCKED) handle_callback(gl, LM_ST_UNLOCKED, 0); spin_unlock(&gl->gl_spin); gfs2_glock_hold(gl); diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 666548e1459..f2a02edcac8 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -455,12 +455,13 @@ void gfs2_recover_func(struct work_struct *work) int ro = 0; unsigned int pass; int error; + int jlocked = 0; if (sdp->sd_args.ar_spectator || (jd->jd_jid != sdp->sd_lockstruct.ls_jid)) { fs_info(sdp, "jid=%u: Trying to acquire journal lock...\n", jd->jd_jid); - + jlocked = 1; /* Acquire the journal lock so we can do recovery */ error = gfs2_glock_nq_num(sdp, jd->jd_jid, &gfs2_journal_glops, @@ -555,13 +556,12 @@ void gfs2_recover_func(struct work_struct *work) jd->jd_jid, t); } - if (jd->jd_jid != sdp->sd_lockstruct.ls_jid) - gfs2_glock_dq_uninit(&ji_gh); - gfs2_recovery_done(sdp, jd->jd_jid, LM_RD_SUCCESS); - if (jd->jd_jid != sdp->sd_lockstruct.ls_jid) + if (jlocked) { + gfs2_glock_dq_uninit(&ji_gh); gfs2_glock_dq_uninit(&j_gh); + } fs_info(sdp, "jid=%u: Done\n", jd->jd_jid); goto done; @@ -569,7 +569,7 @@ void gfs2_recover_func(struct work_struct *work) fail_gunlock_tr: gfs2_glock_dq_uninit(&t_gh); fail_gunlock_ji: - if (jd->jd_jid != sdp->sd_lockstruct.ls_jid) { + if (jlocked) { gfs2_glock_dq_uninit(&ji_gh); fail_gunlock_j: gfs2_glock_dq_uninit(&j_gh); -- cgit v1.2.3-18-g5258 From 43f74c199563a4273e528e2166d0650625a1e05f Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Wed, 29 Sep 2010 14:24:41 +0100 Subject: GFS2: Add "norecovery" mount option as a synonym for "spectator" XFS supports the "norecovery" mount option which is basically the same as the GFS2 spectator mode. This adds support for "norecovery" as a synonym for spectator mode, which is hopefully a more obvious description of what it actually does. Signed-off-by: Steven Whitehouse --- fs/gfs2/super.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index d85e0b7c1bb..047d1176096 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -85,6 +85,7 @@ static const match_table_t tokens = { {Opt_locktable, "locktable=%s"}, {Opt_hostdata, "hostdata=%s"}, {Opt_spectator, "spectator"}, + {Opt_spectator, "norecovery"}, {Opt_ignore_local_fs, "ignore_local_fs"}, {Opt_localflocks, "localflocks"}, {Opt_localcaching, "localcaching"}, -- cgit v1.2.3-18-g5258 From feb47ca9314666d920855b8a235032dea2b2caa4 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Wed, 29 Sep 2010 15:04:18 +0100 Subject: GFS2: Improve journal allocation via sysfs Recently a feature was added to GFS2 to allow journal id allocation via sysfs. This patch builds upon that so that a negative journal id will be treated as an error code to be passed back as the return code from mount. This allows termination of the mount process if there is a failure. Also, the process has been updated so that the kernel will wait for a journal id, even in the "spectator" case. This is required in order to avoid mounting a filesystem in case there is an error while joining the cluster. In the spectator case, 0 is written into the file to indicate that all is well, and that mount should continue. Signed-off-by: Steven Whitehouse --- fs/gfs2/incore.h | 2 +- fs/gfs2/ops_fstype.c | 16 ++++++++++++++-- fs/gfs2/sys.c | 17 +++++++++-------- 3 files changed, 24 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 6f6ff8aba99..764fbb49efc 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -494,7 +494,7 @@ struct gfs2_sb_host { */ struct lm_lockstruct { - unsigned int ls_jid; + int ls_jid; unsigned int ls_first; unsigned int ls_first_done; unsigned int ls_nodir; diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 5b11f376814..aeafc233dc8 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1056,8 +1056,6 @@ static int gfs2_journalid_wait(void *word) static int wait_on_journal(struct gfs2_sbd *sdp) { - if (sdp->sd_args.ar_spectator) - return 0; if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) return 0; @@ -1160,6 +1158,20 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent if (error) goto fail_sb; + /* + * If user space has failed to join the cluster or some similar + * failure has occurred, then the journal id will contain a + * negative (error) number. This will then be returned to the + * caller (of the mount syscall). We do this even for spectator + * mounts (which just write a jid of 0 to indicate "ok" even though + * the jid is unused in the spectator case) + */ + if (sdp->sd_lockstruct.ls_jid < 0) { + error = sdp->sd_lockstruct.ls_jid; + sdp->sd_lockstruct.ls_jid = 0; + goto fail_sb; + } + error = init_inodes(sdp, DO); if (error) goto fail_sb; diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index ccacffd2faa..64082a5feae 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -399,31 +399,32 @@ static ssize_t recover_status_show(struct gfs2_sbd *sdp, char *buf) static ssize_t jid_show(struct gfs2_sbd *sdp, char *buf) { - return sprintf(buf, "%u\n", sdp->sd_lockstruct.ls_jid); + return sprintf(buf, "%d\n", sdp->sd_lockstruct.ls_jid); } static ssize_t jid_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { - unsigned jid; + int jid; int rv; - rv = sscanf(buf, "%u", &jid); + rv = sscanf(buf, "%d", &jid); if (rv != 1) return -EINVAL; spin_lock(&sdp->sd_jindex_spin); rv = -EINVAL; - if (sdp->sd_args.ar_spectator) - goto out; if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) goto out; rv = -EBUSY; - if (test_and_clear_bit(SDF_NOJOURNALID, &sdp->sd_flags) == 0) + if (test_bit(SDF_NOJOURNALID, &sdp->sd_flags) == 0) goto out; + rv = 0; + if (sdp->sd_args.ar_spectator && jid > 0) + rv = jid = -EINVAL; sdp->sd_lockstruct.ls_jid = jid; + clear_bit(SDF_NOJOURNALID, &sdp->sd_flags); smp_mb__after_clear_bit(); wake_up_bit(&sdp->sd_flags, SDF_NOJOURNALID); - rv = 0; out: spin_unlock(&sdp->sd_jindex_spin); return rv ? rv : len; @@ -617,7 +618,7 @@ static int gfs2_uevent(struct kset *kset, struct kobject *kobj, add_uevent_var(env, "LOCKTABLE=%s", sdp->sd_table_name); add_uevent_var(env, "LOCKPROTO=%s", sdp->sd_proto_name); if (!test_bit(SDF_NOJOURNALID, &sdp->sd_flags)) - add_uevent_var(env, "JOURNALID=%u", sdp->sd_lockstruct.ls_jid); + add_uevent_var(env, "JOURNALID=%d", sdp->sd_lockstruct.ls_jid); if (gfs2_uuid_valid(uuid)) add_uevent_var(env, "UUID=%pUB", uuid); return 0; -- cgit v1.2.3-18-g5258 From a347ecb209b58a1b37f20d8299ab552f7d3ee8c3 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Fri, 17 Sep 2010 19:43:10 +0530 Subject: cifs: use type __u32 instead of int for the oplock parameter ... and avoid implicit casting from a signed type. Also, pass oplock by value instead by reference as we don't intend to change the value in cifs_open_inode_helper(). Thanks to Jeff Layton for spotting this. Reviewed-by: Jeff Layton Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/file.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index de748c652d1..d33da45fe98 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -165,7 +165,7 @@ psx_client_can_cache: /* all arguments to this function must be checked for validity in caller */ static inline int cifs_open_inode_helper(struct inode *inode, - struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf, + struct cifsTconInfo *pTcon, __u32 oplock, FILE_ALL_INFO *buf, char *full_path, int xid) { struct cifsInodeInfo *pCifsInode = CIFS_I(inode); @@ -207,11 +207,11 @@ client_can_cache: rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, xid, NULL); - if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) { + if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheRead = true; cFYI(1, "Exclusive Oplock granted on inode %p", inode); - } else if ((*oplock & 0xF) == OPLOCK_READ) + } else if ((oplock & 0xF) == OPLOCK_READ) pCifsInode->clientCanCacheRead = true; return rc; @@ -365,7 +365,7 @@ int cifs_open(struct inode *inode, struct file *file) goto out; } - rc = cifs_open_inode_helper(inode, tcon, &oplock, buf, full_path, xid); + rc = cifs_open_inode_helper(inode, tcon, oplock, buf, full_path, xid); if (rc != 0) goto out; -- cgit v1.2.3-18-g5258 From aa91c7e4ab9b0842b7d7a7cbf8cca18b20df89b5 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Fri, 17 Sep 2010 18:56:39 +0530 Subject: cifs: fix broken oplock handling cifs_new_fileinfo() does not use the 'oplock' value from the callers. Instead, it sets it to REQ_OPLOCK which seems wrong. We should be using the oplock value obtained from the Server to set the inode's clientCanCacheAll or clientCanCacheRead flags. Fix this by passing oplock from the callers to cifs_new_fileinfo(). This change dates back to commit a6ce4932 (2.6.30-rc3). So, all the affected versions will need this fix. Please Cc stable once reviewed and accepted. Cc: Stable Reviewed-by: Jeff Layton Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 3 ++- fs/cifs/dir.c | 12 +++++------- fs/cifs/file.c | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 1d60c655e3e..f110e0e7e94 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -107,7 +107,8 @@ extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, - struct vfsmount *mnt, unsigned int oflags); + struct vfsmount *mnt, unsigned int oflags, + __u32 oplock); extern int cifs_posix_open(char *full_path, struct inode **pinode, struct super_block *sb, int mode, int oflags, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index f9ed0751cc1..0f947bf73f8 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -132,9 +132,9 @@ cifs_bp_rename_retry: struct cifsFileInfo * cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, - struct file *file, struct vfsmount *mnt, unsigned int oflags) + struct file *file, struct vfsmount *mnt, unsigned int oflags, + __u32 oplock) { - int oplock = 0; struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb); @@ -143,9 +143,6 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, if (pCifsFile == NULL) return pCifsFile; - if (oplockEnabled) - oplock = REQ_OPLOCK; - pCifsFile->netfid = fileHandle; pCifsFile->pid = current->tgid; pCifsFile->pInode = igrab(newinode); @@ -468,7 +465,7 @@ cifs_create_set_dentry: } pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp, - nd->path.mnt, oflags); + nd->path.mnt, oflags, oplock); if (pfile_info == NULL) { fput(filp); CIFSSMBClose(xid, tcon, fileHandle); @@ -729,7 +726,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, cfile = cifs_new_fileinfo(newInode, fileHandle, filp, nd->path.mnt, - nd->intent.open.flags); + nd->intent.open.flags, + oplock); if (cfile == NULL) { fput(filp); CIFSSMBClose(xid, pTcon, fileHandle); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index d33da45fe98..60061b9c2f6 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -277,7 +277,7 @@ int cifs_open(struct inode *inode, struct file *file) pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, - oflags); + oflags, oplock); if (pCifsFile == NULL) { CIFSSMBClose(xid, tcon, netfid); rc = -ENOMEM; @@ -370,7 +370,7 @@ int cifs_open(struct inode *inode, struct file *file) goto out; pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, - file->f_flags); + file->f_flags, oplock); if (pCifsFile == NULL) { rc = -ENOMEM; goto out; -- cgit v1.2.3-18-g5258 From 5f98ca9afb9c004f8948c0d40920503de447918a Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Sat, 18 Sep 2010 22:01:58 -0500 Subject: cifs NTLMv2/NTLMSSP Change variable name mac_key to session key to reflect the key it holds Change name of variable mac_key to session key. The reason mac_key was changed to session key is, this structure does not hold message authentication code, it holds the session key (for ntlmv2, ntlmv1 etc.). mac is generated as a signature in cifs_calc* functions. Signed-off-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 22 +++++++++++----------- fs/cifs/cifsglob.h | 4 ++-- fs/cifs/cifsproto.h | 4 ++-- fs/cifs/sess.c | 10 +++++----- fs/cifs/transport.c | 6 +++--- 5 files changed, 23 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 35042d8f733..eed70cae127 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -42,7 +42,7 @@ extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24); static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, - const struct mac_key *key, char *signature) + const struct session_key *key, char *signature) { struct MD5Context context; @@ -78,7 +78,7 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, server->sequence_number++; spin_unlock(&GlobalMid_Lock); - rc = cifs_calculate_signature(cifs_pdu, &server->mac_signing_key, + rc = cifs_calculate_signature(cifs_pdu, &server->session_key, smb_signature); if (rc) memset(cifs_pdu->Signature.SecuritySignature, 0, 8); @@ -89,7 +89,7 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, } static int cifs_calc_signature2(const struct kvec *iov, int n_vec, - const struct mac_key *key, char *signature) + const struct session_key *key, char *signature) { struct MD5Context context; int i; @@ -145,7 +145,7 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, server->sequence_number++; spin_unlock(&GlobalMid_Lock); - rc = cifs_calc_signature2(iov, n_vec, &server->mac_signing_key, + rc = cifs_calc_signature2(iov, n_vec, &server->session_key, smb_signature); if (rc) memset(cifs_pdu->Signature.SecuritySignature, 0, 8); @@ -156,14 +156,14 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, } int cifs_verify_signature(struct smb_hdr *cifs_pdu, - const struct mac_key *mac_key, + const struct session_key *session_key, __u32 expected_sequence_number) { unsigned int rc; char server_response_sig[8]; char what_we_think_sig_should_be[20]; - if ((cifs_pdu == NULL) || (mac_key == NULL)) + if (cifs_pdu == NULL || session_key == NULL) return -EINVAL; if (cifs_pdu->Command == SMB_COM_NEGOTIATE) @@ -192,7 +192,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, cpu_to_le32(expected_sequence_number); cifs_pdu->Signature.Sequence.Reserved = 0; - rc = cifs_calculate_signature(cifs_pdu, mac_key, + rc = cifs_calculate_signature(cifs_pdu, session_key, what_we_think_sig_should_be); if (rc) @@ -209,7 +209,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, } /* We fill in key by putting in 40 byte array which was allocated by caller */ -int cifs_calculate_mac_key(struct mac_key *key, const char *rn, +int cifs_calculate_session_key(struct session_key *key, const char *rn, const char *password) { char temp_key[16]; @@ -347,11 +347,11 @@ void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, /* now calculate the MAC key for NTLMv2 */ hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); hmac_md5_update(resp_buf, 16, &context); - hmac_md5_final(ses->server->mac_signing_key.data.ntlmv2.key, &context); + hmac_md5_final(ses->server->session_key.data.ntlmv2.key, &context); - memcpy(&ses->server->mac_signing_key.data.ntlmv2.resp, resp_buf, + memcpy(&ses->server->session_key.data.ntlmv2.resp, resp_buf, sizeof(struct ntlmv2_resp)); - ses->server->mac_signing_key.len = 16 + sizeof(struct ntlmv2_resp); + ses->server->session_key.len = 16 + sizeof(struct ntlmv2_resp); } void CalcNTLMv2_response(const struct cifsSesInfo *ses, diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 0cdfb8c32ac..14dfa9a067e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -97,7 +97,7 @@ enum protocolEnum { /* Netbios frames protocol not supported at this time */ }; -struct mac_key { +struct session_key { unsigned int len; union { char ntlm[CIFS_SESS_KEY_SIZE + 16]; @@ -182,7 +182,7 @@ struct TCP_Server_Info { /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* needed for CIFS PDU signature */ - struct mac_key mac_signing_key; + struct session_key session_key; char ntlmv2_hash[16]; unsigned long lstrp; /* when we got last response from this server */ u16 dialect; /* dialect index that server chose */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index f110e0e7e94..099fd6173e0 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -363,9 +363,9 @@ extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *, __u32 *); extern int cifs_verify_signature(struct smb_hdr *, - const struct mac_key *mac_key, + const struct session_key *session_key, __u32 expected_sequence_number); -extern int cifs_calculate_mac_key(struct mac_key *key, const char *rn, +extern int cifs_calculate_session_key(struct session_key *key, const char *rn, const char *pass); extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *); extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *, diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 0a57cb7db5d..88820127650 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -480,7 +480,7 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, /* calculate session key, BB what about adding similar ntlmv2 path? */ SMBNTencrypt(ses->password, ses->server->cryptKey, ntlm_session_key); if (first) - cifs_calculate_mac_key(&ses->server->mac_signing_key, + cifs_calculate_session_key(&ses->server->session_key, ntlm_session_key, ses->password); memcpy(tmp, ntlm_session_key, CIFS_SESS_KEY_SIZE); @@ -690,7 +690,7 @@ ssetup_ntlmssp_authenticate: if (first_time) /* should this be moved into common code with similar ntlmv2 path? */ - cifs_calculate_mac_key(&ses->server->mac_signing_key, + cifs_calculate_session_key(&ses->server->session_key, ntlm_session_key, ses->password); /* copy session key */ @@ -765,15 +765,15 @@ ssetup_ntlmssp_authenticate: } /* bail out if key is too long */ if (msg->sesskey_len > - sizeof(ses->server->mac_signing_key.data.krb5)) { + sizeof(ses->server->session_key.data.krb5)) { cERROR(1, "Kerberos signing key too long (%u bytes)", msg->sesskey_len); rc = -EOVERFLOW; goto ssetup_exit; } if (first_time) { - ses->server->mac_signing_key.len = msg->sesskey_len; - memcpy(ses->server->mac_signing_key.data.krb5, + ses->server->session_key.len = msg->sesskey_len; + memcpy(ses->server->session_key.data.krb5, msg->data, msg->sesskey_len); } pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 82f78c4d697..a66c91eb6eb 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -543,7 +543,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(midQ->resp_buf, - &ses->server->mac_signing_key, + &ses->server->session_key, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); @@ -731,7 +731,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, - &ses->server->mac_signing_key, + &ses->server->session_key, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); @@ -981,7 +981,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, - &ses->server->mac_signing_key, + &ses->server->session_key, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); -- cgit v1.2.3-18-g5258 From 2b149f11978b44199954710d32c0eecf6c9efd9c Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Sat, 18 Sep 2010 22:02:18 -0500 Subject: cifs NTLMv2/NTLMSSP ntlmv2 within ntlmssp autentication code Attribue Value (AV) pairs or Target Info (TI) pairs are part of ntlmv2 authentication. Structure ntlmv2_resp had only definition for two av pairs. So removed it, and now allocation of av pairs is dynamic. For servers like Windows 7/2008, av pairs sent by server in challege packet (type 2 in the ntlmssp exchange/negotiation) can vary. Server sends them during ntlmssp negotiation. So when ntlmssp is used as an authentication mechanism, type 2 challenge packet from server has this information. Pluck it and use the entire blob for authenticaiton purpose. If user has not specified, extract (netbios) domain name from the av pairs which is used to calculate ntlmv2 hash. Servers like Windows 7 are particular about the AV pair blob. Servers like Windows 2003, are not very strict about the contents of av pair blob used during ntlmv2 authentication. So when security mechanism such as ntlmv2 is used (not ntlmv2 in ntlmssp), there is no negotiation and so genereate a minimal blob that gets used in ntlmv2 authentication as well as gets sent. Fields tilen and tilbob are session specific. AV pair values are defined. To calculate ntlmv2 response we need ti/av pair blob. For sec mech like ntlmssp, the blob is plucked from type 2 response from the server. From this blob, netbios name of the domain is retrieved, if user has not already provided, to be included in the Target String as part of ntlmv2 hash calculations. For sec mech like ntlmv2, create a minimal, two av pair blob. The allocated blob is freed in case of error. In case there is no error, this blob is used in calculating ntlmv2 response (in CalcNTLMv2_response) and is also copied on the response to the server, and then freed. The type 3 ntlmssp response is prepared on a buffer, 5 * sizeof of struct _AUTHENTICATE_MESSAGE, an empirical value large enough to hold _AUTHENTICATE_MESSAGE plus a blob with max possible 10 values as part of ntlmv2 response and lmv2 keys and domain, user, workstation names etc. Also, kerberos gets selected as a default mechanism if server supports it, over the other security mechanisms. Signed-off-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++--- fs/cifs/cifsglob.h | 2 + fs/cifs/cifspdu.h | 1 - fs/cifs/cifsproto.h | 2 +- fs/cifs/cifssmb.c | 16 ++++--- fs/cifs/connect.c | 2 + fs/cifs/ntlmssp.h | 15 +++++++ fs/cifs/sess.c | 119 +++++++++++++++++++++++++++++++++---------------- 8 files changed, 225 insertions(+), 53 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index eed70cae127..730038a1298 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -27,6 +27,7 @@ #include "md5.h" #include "cifs_unicode.h" #include "cifsproto.h" +#include "ntlmssp.h" #include #include @@ -262,6 +263,87 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, } #endif /* CIFS_WEAK_PW_HASH */ +/* This is just a filler for ntlmv2 type of security mechanisms. + * Older servers are not very particular about the contents of av pairs + * in the blob and for sec mechs like ntlmv2, there is no negotiation + * as in ntlmssp, so unless domain and server netbios and dns names + * are specified, there is no way to obtain name. In case of ntlmssp, + * server provides that info in type 2 challenge packet + */ +static int +build_avpair_blob(struct cifsSesInfo *ses) +{ + struct ntlmssp2_name *attrptr; + + ses->tilen = 2 * sizeof(struct ntlmssp2_name); + ses->tiblob = kzalloc(ses->tilen, GFP_KERNEL); + if (!ses->tiblob) { + ses->tilen = 0; + cERROR(1, "Challenge target info allocation failure"); + return -ENOMEM; + } + attrptr = (struct ntlmssp2_name *) ses->tiblob; + attrptr->type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE); + + return 0; +} + +/* Server has provided av pairs/target info in the type 2 challenge + * packet and we have plucked it and stored within smb session. + * We parse that blob here to find netbios domain name to be used + * as part of ntlmv2 authentication (in Target String), if not already + * specified on the command line. + * If this function returns without any error but without fetching + * domain name, authentication may fail against some server but + * may not fail against other (those who are not very particular + * about target string i.e. for some, just user name might suffice. + */ +static int +find_domain_name(struct cifsSesInfo *ses) +{ + unsigned int attrsize; + unsigned int type; + unsigned int onesize = sizeof(struct ntlmssp2_name); + unsigned char *blobptr; + unsigned char *blobend; + struct ntlmssp2_name *attrptr; + + if (!ses->tilen || !ses->tiblob) + return 0; + + blobptr = ses->tiblob; + blobend = ses->tiblob + ses->tilen; + + while (blobptr + onesize < blobend) { + attrptr = (struct ntlmssp2_name *) blobptr; + type = le16_to_cpu(attrptr->type); + if (type == NTLMSSP_AV_EOL) + break; + blobptr += 2; /* advance attr type */ + attrsize = le16_to_cpu(attrptr->length); + blobptr += 2; /* advance attr size */ + if (blobptr + attrsize > blobend) + break; + if (type == NTLMSSP_AV_NB_DOMAIN_NAME) { + if (!attrsize) + break; + if (!ses->domainName) { + ses->domainName = + kmalloc(attrsize + 1, GFP_KERNEL); + if (!ses->domainName) + return -ENOMEM; + cifs_from_ucs2(ses->domainName, + (__le16 *)blobptr, attrsize, attrsize, + load_nls_default(), false); + break; + } + } + blobptr += attrsize; /* advance attr value */ + } + + return 0; +} + static int calc_ntlmv2_hash(struct cifsSesInfo *ses, const struct nls_table *nls_cp) { @@ -321,7 +403,8 @@ calc_exit_2: return rc; } -void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, +int +setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, const struct nls_table *nls_cp) { int rc; @@ -333,15 +416,29 @@ void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); buf->reserved2 = 0; - buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE); - buf->names[0].length = 0; - buf->names[1].type = 0; - buf->names[1].length = 0; + + if (ses->server->secType == RawNTLMSSP) { + if (!ses->domainName) { + rc = find_domain_name(ses); + if (rc) { + cERROR(1, "error %d finding domain name", rc); + goto setup_ntlmv2_rsp_ret; + } + } + } else { + rc = build_avpair_blob(ses); + if (rc) { + cERROR(1, "error %d building av pair blob", rc); + return rc; + } + } /* calculate buf->ntlmv2_hash */ rc = calc_ntlmv2_hash(ses, nls_cp); - if (rc) + if (rc) { cERROR(1, "could not get v2 hash rc %d", rc); + goto setup_ntlmv2_rsp_ret; + } CalcNTLMv2_response(ses, resp_buf); /* now calculate the MAC key for NTLMv2 */ @@ -352,6 +449,15 @@ void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, memcpy(&ses->server->session_key.data.ntlmv2.resp, resp_buf, sizeof(struct ntlmv2_resp)); ses->server->session_key.len = 16 + sizeof(struct ntlmv2_resp); + + return 0; + +setup_ntlmv2_rsp_ret: + kfree(ses->tiblob); + ses->tiblob = NULL; + ses->tilen = 0; + + return rc; } void CalcNTLMv2_response(const struct cifsSesInfo *ses, @@ -365,6 +471,9 @@ void CalcNTLMv2_response(const struct cifsSesInfo *ses, hmac_md5_update(v2_session_response+8, sizeof(struct ntlmv2_resp) - 8, &context); + if (ses->tilen) + hmac_md5_update(ses->tiblob, ses->tilen, &context); + hmac_md5_final(v2_session_response, &context); /* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */ } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 14dfa9a067e..c68f31cf455 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -222,6 +222,8 @@ struct cifsSesInfo { char userName[MAX_USERNAME_SIZE + 1]; char *domainName; char *password; + unsigned int tilen; /* length of the target info blob */ + unsigned char *tiblob; /* target info blob in challenge response */ bool need_reconnect:1; /* connection reset, uid now invalid */ }; /* no more than one of the following three session flags may be set */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 14d036d8db1..b0f4b5656d4 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -663,7 +663,6 @@ struct ntlmv2_resp { __le64 time; __u64 client_chal; /* random */ __u32 reserved2; - struct ntlmssp2_name names[2]; /* array of name entries could follow ending in minimum 4 byte struct */ } __attribute__((packed)); diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 099fd6173e0..58861286745 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -368,7 +368,7 @@ extern int cifs_verify_signature(struct smb_hdr *, extern int cifs_calculate_session_key(struct session_key *key, const char *rn, const char *pass); extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *); -extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *, +extern int setup_ntlmv2_rsp(struct cifsSesInfo *, char *, const struct nls_table *); #ifdef CONFIG_CIFS_WEAK_PW_HASH extern void calc_lanman_hash(const char *password, const char *cryptkey, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index c65c3419dd3..13c854e4107 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -603,13 +603,15 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) rc = 0; else rc = -EINVAL; - - if (server->sec_kerberos || server->sec_mskerberos) - server->secType = Kerberos; - else if (server->sec_ntlmssp) - server->secType = RawNTLMSSP; - else - rc = -EOPNOTSUPP; + if (server->secType == Kerberos) { + if (!server->sec_kerberos && + !server->sec_mskerberos) + rc = -EOPNOTSUPP; + } else if (server->secType == RawNTLMSSP) { + if (!server->sec_ntlmssp) + rc = -EOPNOTSUPP; + } else + rc = -EOPNOTSUPP; } } else server->capabilities &= ~CAP_EXTENDED_SECURITY; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 88c84a38bcc..c99760a523b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1740,6 +1740,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) if (ses == NULL) goto get_ses_fail; + ses->tilen = 0; + ses->tiblob = NULL; /* new SMB session uses our server ref */ ses->server = server; if (server->addr.sockAddr6.sin6_family == AF_INET6) diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h index 49c9a4e7531..5d52e4a3b1e 100644 --- a/fs/cifs/ntlmssp.h +++ b/fs/cifs/ntlmssp.h @@ -61,6 +61,21 @@ #define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 #define NTLMSSP_NEGOTIATE_56 0x80000000 +/* Define AV Pair Field IDs */ +enum av_field_type { + NTLMSSP_AV_EOL = 0, + NTLMSSP_AV_NB_COMPUTER_NAME, + NTLMSSP_AV_NB_DOMAIN_NAME, + NTLMSSP_AV_DNS_COMPUTER_NAME, + NTLMSSP_AV_DNS_DOMAIN_NAME, + NTLMSSP_AV_DNS_TREE_NAME, + NTLMSSP_AV_FLAGS, + NTLMSSP_AV_TIMESTAMP, + NTLMSSP_AV_RESTRICTION, + NTLMSSP_AV_TARGET_NAME, + NTLMSSP_AV_CHANNEL_BINDINGS +}; + /* Although typedefs are not commonly used for structure definitions */ /* in the Linux kernel, in this particular case they are useful */ /* to more closely match the standards document for NTLMSSP from */ diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 88820127650..af18a500f7e 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -383,6 +383,9 @@ static int decode_ascii_ssetup(char **pbcc_area, int bleft, static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifsSesInfo *ses) { + unsigned int tioffset; /* challenge message target info area */ + unsigned int tilen; /* challenge message target info area length */ + CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr; if (blob_len < sizeof(CHALLENGE_MESSAGE)) { @@ -405,6 +408,19 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, /* BB spec says that if AvId field of MsvAvTimestamp is populated then we must set the MIC field of the AUTHENTICATE_MESSAGE */ + tioffset = cpu_to_le16(pblob->TargetInfoArray.BufferOffset); + tilen = cpu_to_le16(pblob->TargetInfoArray.Length); + ses->tilen = tilen; + if (ses->tilen) { + ses->tiblob = kmalloc(tilen, GFP_KERNEL); + if (!ses->tiblob) { + cERROR(1, "Challenge target info allocation failure"); + ses->tilen = 0; + return -ENOMEM; + } + memcpy(ses->tiblob, bcc_ptr + tioffset, ses->tilen); + } + return 0; } @@ -425,7 +441,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, /* BB is NTLMV2 session security format easier to use here? */ flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM; + NTLMSSP_NEGOTIATE_NTLM; if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) flags |= NTLMSSP_NEGOTIATE_SIGN; @@ -449,12 +465,14 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, This function returns the length of the data in the blob */ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, struct cifsSesInfo *ses, - const struct nls_table *nls_cp, bool first) + const struct nls_table *nls_cp) { + int rc; + unsigned int size; AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; __u32 flags; unsigned char *tmp; - char ntlm_session_key[CIFS_SESS_KEY_SIZE]; + struct ntlmv2_resp ntlmv2_response = {}; memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); sec_blob->MessageType = NtLmAuthenticate; @@ -462,7 +480,7 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | - NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM; + NTLMSSP_NEGOTIATE_NTLM; if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) flags |= NTLMSSP_NEGOTIATE_SIGN; @@ -477,19 +495,26 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, sec_blob->LmChallengeResponse.Length = 0; sec_blob->LmChallengeResponse.MaximumLength = 0; - /* calculate session key, BB what about adding similar ntlmv2 path? */ - SMBNTencrypt(ses->password, ses->server->cryptKey, ntlm_session_key); - if (first) - cifs_calculate_session_key(&ses->server->session_key, - ntlm_session_key, ses->password); - - memcpy(tmp, ntlm_session_key, CIFS_SESS_KEY_SIZE); sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); - sec_blob->NtChallengeResponse.Length = cpu_to_le16(CIFS_SESS_KEY_SIZE); - sec_blob->NtChallengeResponse.MaximumLength = - cpu_to_le16(CIFS_SESS_KEY_SIZE); + rc = setup_ntlmv2_rsp(ses, (char *)&ntlmv2_response, nls_cp); + if (rc) { + cERROR(1, "Error %d during NTLMSSP authentication", rc); + goto setup_ntlmv2_ret; + } + size = sizeof(struct ntlmv2_resp); + memcpy(tmp, (char *)&ntlmv2_response, size); + tmp += size; + if (ses->tilen > 0) { + memcpy(tmp, ses->tiblob, ses->tilen); + tmp += ses->tilen; + } - tmp += CIFS_SESS_KEY_SIZE; + sec_blob->NtChallengeResponse.Length = cpu_to_le16(size + ses->tilen); + sec_blob->NtChallengeResponse.MaximumLength = + cpu_to_le16(size + ses->tilen); + kfree(ses->tiblob); + ses->tiblob = NULL; + ses->tilen = 0; if (ses->domainName == NULL) { sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); @@ -501,7 +526,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, len = cifs_strtoUCS((__le16 *)tmp, ses->domainName, MAX_USERNAME_SIZE, nls_cp); len *= 2; /* unicode is 2 bytes each */ - len += 2; /* trailing null */ sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->DomainName.Length = cpu_to_le16(len); sec_blob->DomainName.MaximumLength = cpu_to_le16(len); @@ -518,7 +542,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, len = cifs_strtoUCS((__le16 *)tmp, ses->userName, MAX_USERNAME_SIZE, nls_cp); len *= 2; /* unicode is 2 bytes each */ - len += 2; /* trailing null */ sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->UserName.Length = cpu_to_le16(len); sec_blob->UserName.MaximumLength = cpu_to_le16(len); @@ -533,6 +556,8 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->SessionKey.Length = 0; sec_blob->SessionKey.MaximumLength = 0; + +setup_ntlmv2_ret: return tmp - pbuffer; } @@ -545,19 +570,6 @@ static void setup_ntlmssp_neg_req(SESSION_SETUP_ANDX *pSMB, return; } - -static int setup_ntlmssp_auth_req(SESSION_SETUP_ANDX *pSMB, - struct cifsSesInfo *ses, - const struct nls_table *nls, bool first_time) -{ - int bloblen; - - bloblen = build_ntlmssp_auth_blob(&pSMB->req.SecurityBlob[0], ses, nls, - first_time); - pSMB->req.SecurityBlobLength = cpu_to_le16(bloblen); - - return bloblen; -} #endif int @@ -580,6 +592,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, struct key *spnego_key = NULL; __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ bool first_time; + int blob_len; + char *ntlmsspblob = NULL; if (ses == NULL) return -EINVAL; @@ -729,12 +743,24 @@ ssetup_ntlmssp_authenticate: cpu_to_le16(sizeof(struct ntlmv2_resp)); /* calculate session key */ - setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); - /* FIXME: calculate MAC key */ + rc = setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); + if (rc) { + cERROR(1, "Error %d during NTLMv2 authentication", rc); + kfree(v2_sess_key); + goto ssetup_exit; + } memcpy(bcc_ptr, (char *)v2_sess_key, - sizeof(struct ntlmv2_resp)); + sizeof(struct ntlmv2_resp)); bcc_ptr += sizeof(struct ntlmv2_resp); kfree(v2_sess_key); + if (ses->tilen > 0) { + memcpy(bcc_ptr, ses->tiblob, ses->tilen); + bcc_ptr += ses->tilen; + /* we never did allocate ses->domainName to free */ + kfree(ses->tiblob); + ses->tiblob = NULL; + ses->tilen = 0; + } if (ses->capabilities & CAP_UNICODE) { if (iov[0].iov_len % 2) { *bcc_ptr = 0; @@ -815,12 +841,28 @@ ssetup_ntlmssp_authenticate: if (phase == NtLmNegotiate) { setup_ntlmssp_neg_req(pSMB, ses); iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE); + iov[1].iov_base = &pSMB->req.SecurityBlob[0]; } else if (phase == NtLmAuthenticate) { - int blob_len; - blob_len = setup_ntlmssp_auth_req(pSMB, ses, - nls_cp, - first_time); + /* 5 is an empirical value, large enought to + * hold authenticate message, max 10 of + * av paris, doamin,user,workstation mames, + * flags etc.. + */ + ntlmsspblob = kmalloc( + 5*sizeof(struct _AUTHENTICATE_MESSAGE), + GFP_KERNEL); + if (!ntlmsspblob) { + cERROR(1, "Can't allocate NTLMSSP"); + rc = -ENOMEM; + goto ssetup_exit; + } + + blob_len = build_ntlmssp_auth_blob(ntlmsspblob, + ses, nls_cp); iov[1].iov_len = blob_len; + iov[1].iov_base = ntlmsspblob; + pSMB->req.SecurityBlobLength = + cpu_to_le16(blob_len); /* Make sure that we tell the server that we are using the uid that it just gave us back on the response (challenge) */ @@ -830,7 +872,6 @@ ssetup_ntlmssp_authenticate: rc = -ENOSYS; goto ssetup_exit; } - iov[1].iov_base = &pSMB->req.SecurityBlob[0]; /* unicode strings must be word aligned */ if ((iov[0].iov_len + iov[1].iov_len) % 2) { *bcc_ptr = 0; @@ -931,6 +972,8 @@ ssetup_exit: key_put(spnego_key); } kfree(str_area); + kfree(ntlmsspblob); + ntlmsspblob = NULL; if (resp_buf_type == CIFS_SMALL_BUFFER) { cFYI(1, "ssetup freeing small buf %p", iov[0].iov_base); cifs_small_buf_release(iov[0].iov_base); -- cgit v1.2.3-18-g5258 From 3eb9a8893a76cf1cda3b41c3212eb2cfe83eae0e Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 1 Sep 2010 17:06:02 -0700 Subject: cifs: Allow binding to local IP address. When using multi-homed machines, it's nice to be able to specify the local IP to use for outbound connections. This patch gives cifs the ability to bind to a particular IP address. Usage: mount -t cifs -o srcaddr=192.168.1.50,user=foo, ... Usage: mount -t cifs -o srcaddr=2002::100:1,user=foo, ... Acked-by: Jeff Layton Acked-by: Dr. David Holder Signed-off-by: Ben Greear Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 19 ++++++++++++ fs/cifs/cifsglob.h | 1 + fs/cifs/connect.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index b7431afdd76..1b6ddd6f760 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "cifsfs.h" #include "cifspdu.h" #define DECLARE_GLOBALS_HERE @@ -367,6 +368,8 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) { struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb); struct cifsTconInfo *tcon = cifs_sb->tcon; + struct sockaddr *srcaddr; + srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; seq_printf(s, ",unc=%s", tcon->treeName); if (tcon->ses->userName) @@ -374,6 +377,22 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) if (tcon->ses->domainName) seq_printf(s, ",domain=%s", tcon->ses->domainName); + if (srcaddr->sa_family != AF_UNSPEC) { + struct sockaddr_in *saddr4; + struct sockaddr_in6 *saddr6; + saddr4 = (struct sockaddr_in *)srcaddr; + saddr6 = (struct sockaddr_in6 *)srcaddr; + if (srcaddr->sa_family == AF_INET6) + seq_printf(s, ",srcaddr=%pI6c", + &saddr6->sin6_addr); + else if (srcaddr->sa_family == AF_INET) + seq_printf(s, ",srcaddr=%pI4", + &saddr4->sin_addr.s_addr); + else + seq_printf(s, ",srcaddr=BAD-AF:%i", + (int)(srcaddr->sa_family)); + } + seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) seq_printf(s, ",forceuid"); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c68f31cf455..6ef0efaf68d 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -139,6 +139,7 @@ struct TCP_Server_Info { struct sockaddr_in sockAddr; struct sockaddr_in6 sockAddr6; } addr; + struct sockaddr_storage srcaddr; /* locally bind to this IP */ wait_queue_head_t response_q; wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ struct list_head pending_mid_q; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c99760a523b..fa884520fb8 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -105,6 +105,7 @@ struct smb_vol { bool sockopt_tcp_nodelay:1; unsigned short int port; char *prepath; + struct sockaddr_storage srcaddr; /* allow binding to a local IP */ struct nls_table *local_nls; }; @@ -1046,6 +1047,22 @@ cifs_parse_mount_options(char *options, const char *devname, "long\n"); return 1; } + } else if (strnicmp(data, "srcaddr", 7) == 0) { + vol->srcaddr.ss_family = AF_UNSPEC; + + if (!value || !*value) { + printk(KERN_WARNING "CIFS: srcaddr value" + " not specified.\n"); + return 1; /* needs_arg; */ + } + i = cifs_convert_address((struct sockaddr *)&vol->srcaddr, + value, strlen(value)); + if (i < 0) { + printk(KERN_WARNING "CIFS: Could not parse" + " srcaddr: %s\n", + value); + return 1; + } } else if (strnicmp(data, "prefixpath", 10) == 0) { if (!value || !*value) { printk(KERN_WARNING @@ -1374,8 +1391,36 @@ cifs_parse_mount_options(char *options, const char *devname, return 0; } +/** Returns true if srcaddr isn't specified and rhs isn't + * specified, or if srcaddr is specified and + * matches the IP address of the rhs argument. + */ +static bool +srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs) +{ + switch (srcaddr->sa_family) { + case AF_UNSPEC: + return (rhs->sa_family == AF_UNSPEC); + case AF_INET: { + struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr; + struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs; + return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr); + } + case AF_INET6: { + struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr; + struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)&rhs; + return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr); + } + default: + WARN_ON(1); + return false; /* don't expect to be here */ + } +} + + static bool -match_address(struct TCP_Server_Info *server, struct sockaddr *addr) +match_address(struct TCP_Server_Info *server, struct sockaddr *addr, + struct sockaddr *srcaddr) { struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; @@ -1402,6 +1447,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr) break; } + if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr)) + return false; + return true; } @@ -1469,7 +1517,8 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) if (server->tcpStatus == CifsNew) continue; - if (!match_address(server, addr)) + if (!match_address(server, addr, + (struct sockaddr *)&vol->srcaddr)) continue; if (!match_security(server, vol)) @@ -1584,6 +1633,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info) * no need to spinlock this init of tcpStatus or srv_count */ tcp_ses->tcpStatus = CifsNew; + memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr, + sizeof(tcp_ses->srcaddr)); ++tcp_ses->srv_count; if (addr.ss_family == AF_INET6) { @@ -1999,6 +2050,33 @@ static void rfc1002mangle(char *target, char *source, unsigned int length) } +static int +bind_socket(struct TCP_Server_Info *server) +{ + int rc = 0; + if (server->srcaddr.ss_family != AF_UNSPEC) { + /* Bind to the specified local IP address */ + struct socket *socket = server->ssocket; + rc = socket->ops->bind(socket, + (struct sockaddr *) &server->srcaddr, + sizeof(server->srcaddr)); + if (rc < 0) { + struct sockaddr_in *saddr4; + struct sockaddr_in6 *saddr6; + saddr4 = (struct sockaddr_in *)&server->srcaddr; + saddr6 = (struct sockaddr_in6 *)&server->srcaddr; + if (saddr6->sin6_family == AF_INET6) + cERROR(1, "cifs: " + "Failed to bind to: %pI6c, error: %d\n", + &saddr6->sin6_addr, rc); + else + cERROR(1, "cifs: " + "Failed to bind to: %pI4, error: %d\n", + &saddr4->sin_addr.s_addr, rc); + } + } + return rc; +} static int ipv4_connect(struct TCP_Server_Info *server) @@ -2024,6 +2102,10 @@ ipv4_connect(struct TCP_Server_Info *server) cifs_reclassify_socket4(socket); } + rc = bind_socket(server); + if (rc < 0) + return rc; + /* user overrode default port */ if (server->addr.sockAddr.sin_port) { rc = socket->ops->connect(socket, (struct sockaddr *) @@ -2186,6 +2268,10 @@ ipv6_connect(struct TCP_Server_Info *server) cifs_reclassify_socket6(socket); } + rc = bind_socket(server); + if (rc < 0) + return rc; + /* user overrode default port */ if (server->addr.sockAddr6.sin6_port) { rc = socket->ops->connect(socket, -- cgit v1.2.3-18-g5258 From c69c1b6eaea1b3e1eecf7ad2fba0208ac4a11131 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 31 Jul 2010 09:14:16 +0200 Subject: cifs: implement CIFSParseMFSymlink() Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French --- fs/cifs/link.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'fs') diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 473ca803365..12eb491d4c5 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -28,6 +28,68 @@ #include "cifsproto.h" #include "cifs_debug.h" #include "cifs_fs_sb.h" +#include "md5.h" + +#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1) +#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1)) +#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1)) +#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024) +#define CIFS_MF_SYMLINK_FILE_SIZE \ + (CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN) + +#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n" +#define CIFS_MF_SYMLINK_MD5_FORMAT \ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n" +#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) \ + md5_hash[0], md5_hash[1], md5_hash[2], md5_hash[3], \ + md5_hash[4], md5_hash[5], md5_hash[6], md5_hash[7], \ + md5_hash[8], md5_hash[9], md5_hash[10], md5_hash[11],\ + md5_hash[12], md5_hash[13], md5_hash[14], md5_hash[15] + +static int +CIFSParseMFSymlink(const u8 *buf, + unsigned int buf_len, + unsigned int *_link_len, + char **_link_str) +{ + int rc; + unsigned int link_len; + const char *md5_str1; + const char *link_str; + struct MD5Context md5_ctx; + u8 md5_hash[16]; + char md5_str2[34]; + + if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) + return -EINVAL; + + md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET]; + link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET]; + + rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len); + if (rc != 1) + return -EINVAL; + + cifs_MD5_init(&md5_ctx); + cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len); + cifs_MD5_final(md5_hash, &md5_ctx); + + snprintf(md5_str2, sizeof(md5_str2), + CIFS_MF_SYMLINK_MD5_FORMAT, + CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); + + if (strncmp(md5_str1, md5_str2, 17) != 0) + return -EINVAL; + + if (_link_str) { + *_link_str = kstrndup(link_str, link_len, GFP_KERNEL); + if (!*_link_str) + return -ENOMEM; + } + + *_link_len = link_len; + return 0; +} int cifs_hardlink(struct dentry *old_file, struct inode *inode, -- cgit v1.2.3-18-g5258 From 8bfb50a882ccd9804929876470f74edcb23d2326 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 31 Jul 2010 09:15:10 +0200 Subject: cifs: implement CIFSCouldBeMFSymlink() and CIFSCheckMFSymlink() Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 4 +++ fs/cifs/link.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 58861286745..8604a45c110 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -409,4 +409,8 @@ extern int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, extern int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon, const int netfid, __u64 *pExtAttrBits, __u64 *pMask); extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); +extern bool CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr); +extern int CIFSCheckMFSymlink(struct cifs_fattr *fattr, + const unsigned char *path, + struct cifs_sb_info *cifs_sb, int xid); #endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 12eb491d4c5..bec212b09a0 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -91,6 +91,85 @@ CIFSParseMFSymlink(const u8 *buf, return 0; } +bool +CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr) +{ + if (!(fattr->cf_mode & S_IFREG)) + /* it's not a symlink */ + return false; + + if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) + /* it's not a symlink */ + return false; + + return true; +} + +int +CIFSCheckMFSymlink(struct cifs_fattr *fattr, + const unsigned char *path, + struct cifs_sb_info *cifs_sb, int xid) +{ + int rc; + int oplock = 0; + __u16 netfid = 0; + struct cifsTconInfo *pTcon = cifs_sb->tcon; + u8 *buf; + char *pbuf; + unsigned int bytes_read = 0; + int buf_type = CIFS_NO_BUFFER; + unsigned int link_len = 0; + FILE_ALL_INFO file_info; + + if (!CIFSCouldBeMFSymlink(fattr)) + /* it's not a symlink */ + return 0; + + rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, + CREATE_NOT_DIR, &netfid, &oplock, &file_info, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + if (rc != 0) + return rc; + + if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { + CIFSSMBClose(xid, pTcon, netfid); + /* it's not a symlink */ + return 0; + } + + buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pbuf = buf; + + rc = CIFSSMBRead(xid, pTcon, netfid, + CIFS_MF_SYMLINK_FILE_SIZE /* length */, + 0 /* offset */, + &bytes_read, &pbuf, &buf_type); + CIFSSMBClose(xid, pTcon, netfid); + if (rc != 0) { + kfree(buf); + return rc; + } + + rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); + kfree(buf); + if (rc == -EINVAL) + /* it's not a symlink */ + return 0; + if (rc != 0) + return rc; + + /* it is a symlink */ + fattr->cf_eof = link_len; + fattr->cf_mode &= ~S_IFMT; + fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; + fattr->cf_dtype = DT_LNK; + return 0; +} + int cifs_hardlink(struct dentry *old_file, struct inode *inode, struct dentry *direntry) -- cgit v1.2.3-18-g5258 From 0fd43ae4758b2841656afda4439b80e8a3603af2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 5 Aug 2010 21:13:44 +0200 Subject: cifs: implement CIFSQueryMFSymLink() Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French --- fs/cifs/link.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'fs') diff --git a/fs/cifs/link.c b/fs/cifs/link.c index bec212b09a0..6e4e8957595 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -91,6 +91,56 @@ CIFSParseMFSymlink(const u8 *buf, return 0; } +static int +CIFSQueryMFSymLink(const int xid, struct cifsTconInfo *tcon, + const unsigned char *searchName, char **symlinkinfo, + const struct nls_table *nls_codepage, int remap) +{ + int rc; + int oplock = 0; + __u16 netfid = 0; + u8 *buf; + char *pbuf; + unsigned int bytes_read = 0; + int buf_type = CIFS_NO_BUFFER; + unsigned int link_len = 0; + FILE_ALL_INFO file_info; + + rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ, + CREATE_NOT_DIR, &netfid, &oplock, &file_info, + nls_codepage, remap); + if (rc != 0) + return rc; + + if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { + CIFSSMBClose(xid, tcon, netfid); + /* it's not a symlink */ + return -EINVAL; + } + + buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pbuf = buf; + + rc = CIFSSMBRead(xid, tcon, netfid, + CIFS_MF_SYMLINK_FILE_SIZE /* length */, + 0 /* offset */, + &bytes_read, &pbuf, &buf_type); + CIFSSMBClose(xid, tcon, netfid); + if (rc != 0) { + kfree(buf); + return rc; + } + + rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo); + kfree(buf); + if (rc != 0) + return rc; + + return 0; +} + bool CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr) { -- cgit v1.2.3-18-g5258 From 18bddd1059c5d1e17ad6e49c514c95484aa80a33 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 3 Aug 2010 11:24:22 +0200 Subject: cifs: implement CIFSFormatMFSymlink() Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French --- fs/cifs/link.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'fs') diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 6e4e8957595..0473a86031a 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -92,6 +92,47 @@ CIFSParseMFSymlink(const u8 *buf, } static int +CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str) +{ + unsigned int link_len; + unsigned int ofs; + struct MD5Context md5_ctx; + u8 md5_hash[16]; + + if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) + return -EINVAL; + + link_len = strlen(link_str); + + if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) + return -ENAMETOOLONG; + + cifs_MD5_init(&md5_ctx); + cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len); + cifs_MD5_final(md5_hash, &md5_ctx); + + snprintf(buf, buf_len, + CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, + link_len, + CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); + + ofs = CIFS_MF_SYMLINK_LINK_OFFSET; + memcpy(buf + ofs, link_str, link_len); + + ofs += link_len; + if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { + buf[ofs] = '\n'; + ofs++; + } + + while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { + buf[ofs] = ' '; + ofs++; + } + + return 0; +} + CIFSQueryMFSymLink(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char **symlinkinfo, const struct nls_table *nls_codepage, int remap) -- cgit v1.2.3-18-g5258 From 8713d01db8bf948eb9632726f529ec4f821bb025 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 5 Aug 2010 21:15:22 +0200 Subject: cifs: implement CIFSCreateMFSymLink() Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French --- fs/cifs/link.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'fs') diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 0473a86031a..1b2a8c97199 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -133,6 +133,51 @@ CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str) return 0; } +static int +CIFSCreateMFSymLink(const int xid, struct cifsTconInfo *tcon, + const char *fromName, const char *toName, + const struct nls_table *nls_codepage, int remap) +{ + int rc; + int oplock = 0; + __u16 netfid = 0; + u8 *buf; + unsigned int bytes_written = 0; + + buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); + if (rc != 0) { + kfree(buf); + return rc; + } + + rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, + CREATE_NOT_DIR, &netfid, &oplock, NULL, + nls_codepage, remap); + if (rc != 0) { + kfree(buf); + return rc; + } + + rc = CIFSSMBWrite(xid, tcon, netfid, + CIFS_MF_SYMLINK_FILE_SIZE /* length */, + 0 /* offset */, + &bytes_written, buf, NULL, 0); + CIFSSMBClose(xid, tcon, netfid); + kfree(buf); + if (rc != 0) + return rc; + + if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) + return -EIO; + + return 0; +} + +static int CIFSQueryMFSymLink(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char **symlinkinfo, const struct nls_table *nls_codepage, int remap) -- cgit v1.2.3-18-g5258 From 1b12b9c15b4371d83b729b8fc18c670e78a1479b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 5 Aug 2010 21:19:56 +0200 Subject: cifs: use Minshall+French symlink functions If configured, Minshall+French Symlinks are used against all servers. If the server supports UNIX Extensions, we still create Minshall+French Symlinks on write, but on read we fallback to UNIX Extension symlinks. Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/inode.c | 14 ++++++++++++++ fs/cifs/link.c | 27 +++++++++++++++++++++++---- fs/cifs/readdir.c | 9 +++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 9e771450c3b..7fde5296989 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -36,6 +36,7 @@ #define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */ #define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/ #define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ +#define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 93f77d438d3..016975b8e6d 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -332,6 +332,13 @@ int cifs_get_inode_info_unix(struct inode **pinode, return rc; } + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + int tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid); + if (tmprc) + cFYI(1, "CIFSCheckMFSymlink: %d", tmprc); + } + if (*pinode == NULL) { /* get new inode */ cifs_fill_uniqueid(sb, &fattr); @@ -661,6 +668,13 @@ int cifs_get_inode_info(struct inode **pinode, if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) cifs_sfu_mode(&fattr, full_path, cifs_sb, xid); + /* check for Minshall+French symlinks */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { + tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid); + if (tmprc) + cFYI(1, "CIFSCheckMFSymlink: %d", tmprc); + } + if (!*pinode) { *pinode = cifs_iget(sb, &fattr); if (!*pinode) diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 1b2a8c97199..cbf7b112287 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -407,7 +407,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) * but there doesn't seem to be any harm in allowing the client to * read them. */ - if (!(tcon->ses->capabilities & CAP_UNIX)) { + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + && !(tcon->ses->capabilities & CAP_UNIX)) { rc = -EACCES; goto out; } @@ -418,8 +419,21 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); - rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, - cifs_sb->local_nls); + rc = -EACCES; + /* + * First try Minshall+French Symlinks, if configured + * and fallback to UNIX Extensions Symlinks. + */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + + if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX)) + rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, + cifs_sb->local_nls); + kfree(full_path); out: if (rc != 0) { @@ -459,7 +473,12 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) cFYI(1, "symname is %s", symname); /* BB what if DFS and this volume is on different share? BB */ - if (pTcon->unix_ext) + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + else if (pTcon->unix_ext) rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, cifs_sb->local_nls); /* else diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index d5e591fab47..6a8b417baba 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -738,6 +738,15 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir, cifs_autodisable_serverino(cifs_sb); } + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && + CIFSCouldBeMFSymlink(&fattr)) + /* + * trying to get the type and mode can be slow, + * so just call those regular files for now, and mark + * for reval + */ + fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; + ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr); -- cgit v1.2.3-18-g5258 From 736a33205969c16f81d747db14ff4c0f133609a6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 30 Jul 2010 14:56:00 +0200 Subject: cifs: add "mfsymlinks" mount option This is the start for an implementation of "Minshall+French Symlinks" (see http://wiki.samba.org/index.php/UNIX_Extensions#Minshall.2BFrench_symlinks). Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French --- fs/cifs/README | 5 +++++ fs/cifs/cifsfs.c | 2 ++ fs/cifs/connect.c | 11 +++++++++++ 3 files changed, 18 insertions(+) (limited to 'fs') diff --git a/fs/cifs/README b/fs/cifs/README index 7099a526f77..ee68d103654 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -527,6 +527,11 @@ A partial list of the supported mount options follows: SFU does). In the future the bottom 9 bits of the mode also will be emulated using queries of the security descriptor (ACL). + mfsymlinks Enable support for Minshall+French symlinks + (see http://wiki.samba.org/index.php/UNIX_Extensions#Minshall.2BFrench_symlinks) + This option is ignored when specified together with the + 'sfu' option. Minshall+French symlinks are used even if + the server supports the CIFS Unix Extensions. sign Must use packet signing (helps avoid unwanted data modification by intermediate systems in the route). Note that signing does not work with lanman or plaintext authentication. diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 1b6ddd6f760..52e89ea0745 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -441,6 +441,8 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) seq_printf(s, ",dynperm"); if (m->mnt_sb->s_flags & MS_POSIXACL) seq_printf(s, ",acl"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) + seq_printf(s, ",mfsymlinks"); seq_printf(s, ",rsize=%d", cifs_sb->rsize); seq_printf(s, ",wsize=%d", cifs_sb->wsize); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index fa884520fb8..435b912f5de 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -100,6 +100,7 @@ struct smb_vol { bool noautotune:1; bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ bool fsc:1; /* enable fscache */ + bool mfsymlinks:1; /* use Minshall+French Symlinks */ unsigned int rsize; unsigned int wsize; bool sockopt_tcp_nodelay:1; @@ -1342,6 +1343,8 @@ cifs_parse_mount_options(char *options, const char *devname, "/proc/fs/cifs/LookupCacheEnabled to 0\n"); } else if (strnicmp(data, "fsc", 3) == 0) { vol->fsc = true; + } else if (strnicmp(data, "mfsymlinks", 10) == 0) { + vol->mfsymlinks = true; } else printk(KERN_WARNING "CIFS: Unknown mount option %s\n", data); @@ -2554,6 +2557,14 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, cFYI(1, "mounting share using direct i/o"); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; } + if (pvolume_info->mfsymlinks) { + if (pvolume_info->sfu_emul) { + cERROR(1, "mount option mfsymlinks ignored if sfu " + "mount option is used"); + } else { + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS; + } + } if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) cERROR(1, "mount option dynperm ignored if cifsacl " -- cgit v1.2.3-18-g5258 From 5fe97cfddc426f3145e8673b68faab7e54462173 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Sep 2010 16:01:30 -0700 Subject: cifs: add tcon field to cifsFileInfo struct Eventually, we'll have more than one tcon per superblock. At that point, we'll need to know which one is associated with a particular fid. For now, this is just set from the cifs_sb->tcon pointer, but eventually the caller of cifs_new_fileinfo will pass a tcon pointer in. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 + fs/cifs/dir.c | 1 + 2 files changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6ef0efaf68d..6f3968a5fea 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -351,6 +351,7 @@ struct cifsFileInfo { struct file *pfile; /* needed for writepage */ struct inode *pInode; /* needed for oplock break */ struct vfsmount *mnt; + struct cifsTconInfo *tcon; struct mutex lock_mutex; struct list_head llist; /* list of byte range locks we have. */ bool closePend:1; /* file is marked to close */ diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 0f947bf73f8..613589cf517 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -150,6 +150,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, pCifsFile->pfile = file; pCifsFile->invalidHandle = false; pCifsFile->closePend = false; + pCifsFile->tcon = cifs_sb->tcon; mutex_init(&pCifsFile->fh_mutex); mutex_init(&pCifsFile->lock_mutex); INIT_LIST_HEAD(&pCifsFile->llist); -- cgit v1.2.3-18-g5258 From ab9db8b737210bec365593a04dd1c534220bb311 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 21 Sep 2010 08:14:46 -0700 Subject: cifs: allow matching of tcp sessions in CifsNew state With commit 7332f2a6217ee6925f83ef0e725013067ed316ba, cifsd will no longer exit when the socket abends and the tcpStatus is CifsNew. With that change, there's no reason to avoid matching an existing session in this state. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'fs') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 435b912f5de..271038b6ec0 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1511,15 +1511,6 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) write_lock(&cifs_tcp_ses_lock); list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - /* - * the demux thread can exit on its own while still in CifsNew - * so don't accept any sockets in that state. Since the - * tcpStatus never changes back to CifsNew it's safe to check - * for this without a lock. - */ - if (server->tcpStatus == CifsNew) - continue; - if (!match_address(server, addr, (struct sockaddr *)&vol->srcaddr)) continue; -- cgit v1.2.3-18-g5258 From 17edec6f563ba6b5630329fbe9473557bb475c3d Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 22 Sep 2010 02:01:59 +0000 Subject: [CIFS] Remove obsolete header We decided not to use connector to do the upcalls so cn_cifs.h is obsolete - remove it. Signed-off-by: Steve French --- fs/cifs/cn_cifs.h | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 fs/cifs/cn_cifs.h (limited to 'fs') diff --git a/fs/cifs/cn_cifs.h b/fs/cifs/cn_cifs.h deleted file mode 100644 index ea59ccac2eb..00000000000 --- a/fs/cifs/cn_cifs.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * fs/cifs/cn_cifs.h - * - * Copyright (c) International Business Machines Corp., 2002 - * Author(s): Steve French (sfrench@us.ibm.com) - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _CN_CIFS_H -#define _CN_CIFS_H -#ifdef CONFIG_CIFS_UPCALL -#include -#include - -struct cifs_upcall { - char signature[4]; /* CIFS */ - enum command { - CIFS_GET_IP = 0x00000001, /* get ip address for hostname */ - CIFS_GET_SECBLOB = 0x00000002, /* get SPNEGO wrapped blob */ - } command; - /* union cifs upcall data follows */ -}; -#endif /* CIFS_UPCALL */ -#endif /* _CN_CIFS_H */ -- cgit v1.2.3-18-g5258 From d3bf5221d3274b5015ad18a55060b074cca8d2f0 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 22 Sep 2010 19:15:36 +0000 Subject: [CIFS] Fix ordering of cleanup on module init failure If registering fs cache failed, we weren't cleaning up proc. Acked-by: Jeff Layton CC: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 18 +++++++++--------- fs/cifs/connect.c | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 52e89ea0745..eeb8c67a3f2 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -933,11 +933,11 @@ init_cifs(void) rc = cifs_fscache_register(); if (rc) - goto out; + goto out_clean_proc; rc = cifs_init_inodecache(); if (rc) - goto out_clean_proc; + goto out_unreg_fscache; rc = cifs_init_mids(); if (rc) @@ -959,19 +959,19 @@ init_cifs(void) return 0; #ifdef CONFIG_CIFS_UPCALL - out_unregister_filesystem: +out_unregister_filesystem: unregister_filesystem(&cifs_fs_type); #endif - out_destroy_request_bufs: +out_destroy_request_bufs: cifs_destroy_request_bufs(); - out_destroy_mids: +out_destroy_mids: cifs_destroy_mids(); - out_destroy_inodecache: +out_destroy_inodecache: cifs_destroy_inodecache(); - out_clean_proc: - cifs_proc_clean(); +out_unreg_fscache: cifs_fscache_unregister(); - out: +out_clean_proc: + cifs_proc_clean(); return rc; } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 271038b6ec0..230410e0a45 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -47,7 +47,6 @@ #include "ntlmssp.h" #include "nterr.h" #include "rfc1002pdu.h" -#include "cn_cifs.h" #include "fscache.h" #define CIFS_PORT 445 -- cgit v1.2.3-18-g5258 From ba00ba64cf0895e4c2ac507e56306363dc125a90 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Sep 2010 16:01:31 -0700 Subject: cifs: make various routines use the cifsFileInfo->tcon pointer ...where it's available and appropriate. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 4 ++-- fs/cifs/file.c | 39 ++++++++++++++++----------------------- fs/cifs/inode.c | 22 ++++++++++++++++------ fs/cifs/ioctl.c | 17 +++-------------- fs/cifs/readdir.c | 19 ++++++++----------- 5 files changed, 45 insertions(+), 56 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index eeb8c67a3f2..7193494efc0 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -589,6 +589,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) /* note that this is called by vfs setlease with the BKL held although I doubt that BKL is needed here in cifs */ struct inode *inode = file->f_path.dentry->d_inode; + struct cifsFileInfo *cfile = file->private_data; if (!(S_ISREG(inode->i_mode))) return -EINVAL; @@ -599,8 +600,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) ((arg == F_WRLCK) && (CIFS_I(inode)->clientCanCacheAll))) return generic_setlease(file, arg, lease); - else if (CIFS_SB(inode->i_sb)->tcon->local_lease && - !CIFS_I(inode)->clientCanCacheRead) + else if (cfile->tcon->local_lease && !CIFS_I(inode)->clientCanCacheRead) /* If the server claims to support oplock on this file, then we still need to check oplock even if the local_lease mount option is set, but there diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 60061b9c2f6..84979fc7786 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -461,7 +461,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) } cifs_sb = CIFS_SB(inode->i_sb); - tcon = cifs_sb->tcon; + tcon = pCifsFile->tcon; /* can not grab rename sem here because various ops, including those that already have the rename sem can end up causing writepage @@ -575,7 +575,7 @@ int cifs_close(struct inode *inode, struct file *file) xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; + pTcon = pSMBFile->tcon; if (pSMBFile) { struct cifsLockInfo *li, *tmp; write_lock(&GlobalSMBSeslock); @@ -653,11 +653,7 @@ int cifs_closedir(struct inode *inode, struct file *file) xid = GetXid(); if (pCFileStruct) { - struct cifsTconInfo *pTcon; - struct cifs_sb_info *cifs_sb = - CIFS_SB(file->f_path.dentry->d_sb); - - pTcon = cifs_sb->tcon; + struct cifsTconInfo *pTcon = pCFileStruct->tcon; cFYI(1, "Freeing private data in close dir"); write_lock(&GlobalSMBSeslock); @@ -767,7 +763,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) cFYI(1, "Unknown type of lock"); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - tcon = cifs_sb->tcon; + tcon = ((struct cifsFileInfo *)file->private_data)->tcon; if (file->private_data == NULL) { rc = -EBADF; @@ -960,14 +956,14 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - pTcon = cifs_sb->tcon; - /* cFYI(1, " write %d bytes to offset %lld of %s", write_size, *poffset, file->f_path.dentry->d_name.name); */ if (file->private_data == NULL) return -EBADF; + open_file = file->private_data; + pTcon = open_file->tcon; rc = generic_write_checks(file, poffset, &write_size, 0); if (rc) @@ -1062,14 +1058,13 @@ static ssize_t cifs_write(struct file *file, const char *write_data, cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - pTcon = cifs_sb->tcon; - cFYI(1, "write %zd bytes to offset %lld of %s", write_size, *poffset, file->f_path.dentry->d_name.name); if (file->private_data == NULL) return -EBADF; open_file = file->private_data; + pTcon = open_file->tcon; xid = GetXid(); @@ -1284,7 +1279,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) int rc = -EFAULT; int bytes_written = 0; struct cifs_sb_info *cifs_sb; - struct cifsTconInfo *pTcon; struct inode *inode; struct cifsFileInfo *open_file; @@ -1293,7 +1287,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) inode = page->mapping->host; cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; offset += (loff_t)from; write_data = kmap(page); @@ -1352,6 +1345,7 @@ static int cifs_writepages(struct address_space *mapping, int nr_pages; __u64 offset = 0; struct cifsFileInfo *open_file; + struct cifsTconInfo *tcon; struct cifsInodeInfo *cifsi = CIFS_I(mapping->host); struct page *page; struct pagevec pvec; @@ -1501,8 +1495,9 @@ retry: cERROR(1, "No writable handles for inode"); rc = -EBADF; } else { + tcon = open_file->tcon; long_op = cifs_write_timeout(cifsi, offset); - rc = CIFSSMBWrite2(xid, cifs_sb->tcon, + rc = CIFSSMBWrite2(xid, tcon, open_file->netfid, bytes_to_write, offset, &bytes_written, iov, n_iov, @@ -1520,8 +1515,7 @@ retry: else set_bit(AS_EIO, &mapping->flags); } else { - cifs_stats_bytes_written(cifs_sb->tcon, - bytes_written); + cifs_stats_bytes_written(tcon, bytes_written); } } for (i = 0; i < n_iov; i++) { @@ -1665,7 +1659,7 @@ int cifs_fsync(struct file *file, int datasync) if (rc == 0) { rc = CIFS_I(inode)->write_behind_rc; CIFS_I(inode)->write_behind_rc = 0; - tcon = CIFS_SB(inode->i_sb)->tcon; + tcon = smbfile->tcon; if (!rc && tcon && smbfile && !(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); @@ -1750,7 +1744,6 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - pTcon = cifs_sb->tcon; if (file->private_data == NULL) { rc = -EBADF; @@ -1758,6 +1751,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, return rc; } open_file = file->private_data; + pTcon = open_file->tcon; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, "attempting read on write only file instance"); @@ -1831,7 +1825,6 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - pTcon = cifs_sb->tcon; if (file->private_data == NULL) { rc = -EBADF; @@ -1839,6 +1832,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, return rc; } open_file = file->private_data; + pTcon = open_file->tcon; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, "attempting read on write only file instance"); @@ -1974,7 +1968,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, } open_file = file->private_data; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - pTcon = cifs_sb->tcon; + pTcon = open_file->tcon; /* * Reads as many pages as possible from fscache. Returns -ENOBUFS @@ -2312,7 +2306,6 @@ void cifs_oplock_break(struct work_struct *work) oplock_break); struct inode *inode = cfile->pInode; struct cifsInodeInfo *cinode = CIFS_I(inode); - struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->mnt->mnt_sb); int rc, waitrc = 0; if (inode && S_ISREG(inode->i_mode)) { @@ -2339,7 +2332,7 @@ void cifs_oplock_break(struct work_struct *work) * disconnected since oplock already released by the server */ if (!cfile->closePend && !cfile->oplock_break_cancelled) { - rc = CIFSSMBLock(0, cifs_sb->tcon, cfile->netfid, 0, 0, 0, 0, + rc = CIFSSMBLock(0, cfile->tcon, cfile->netfid, 0, 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false); cFYI(1, "Oplock release rc = %d", rc); } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 016975b8e6d..0fa145596fc 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -288,8 +288,8 @@ int cifs_get_file_info_unix(struct file *filp) struct cifs_fattr fattr; struct inode *inode = filp->f_path.dentry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; struct cifsFileInfo *cfile = filp->private_data; + struct cifsTconInfo *tcon = cfile->tcon; xid = GetXid(); rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data); @@ -522,8 +522,8 @@ int cifs_get_file_info(struct file *filp) struct cifs_fattr fattr; struct inode *inode = filp->f_path.dentry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; struct cifsFileInfo *cfile = filp->private_data; + struct cifsTconInfo *tcon = cfile->tcon; xid = GetXid(); rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data); @@ -891,7 +891,7 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, struct cifsFileInfo *open_file; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct cifsTconInfo *pTcon; FILE_BASIC_INFO info_buf; if (attrs == NULL) @@ -934,9 +934,12 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, if (open_file) { netfid = open_file->netfid; netpid = open_file->pid; + pTcon = open_file->tcon; goto set_via_filehandle; } + pTcon = cifs_sb->tcon; + /* * NT4 apparently returns success on this call, but it doesn't * really work. @@ -1611,11 +1614,12 @@ int cifs_revalidate_file(struct file *filp) { int rc = 0; struct inode *inode = filp->f_path.dentry->d_inode; + struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; if (!cifs_inode_needs_reval(inode)) goto check_inval; - if (CIFS_SB(inode->i_sb)->tcon->unix_ext) + if (cfile->tcon->unix_ext) rc = cifs_get_file_info_unix(filp); else rc = cifs_get_file_info(filp); @@ -1720,7 +1724,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, struct cifsFileInfo *open_file; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct cifsTconInfo *pTcon = NULL; /* * To avoid spurious oplock breaks from server, in the case of @@ -1735,6 +1739,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, if (open_file) { __u16 nfid = open_file->netfid; __u32 npid = open_file->pid; + pTcon = open_file->tcon; rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, npid, false); cifsFileInfo_put(open_file); @@ -1749,6 +1754,9 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, rc = -EINVAL; if (rc != 0) { + if (pTcon == NULL) + pTcon = cifs_sb->tcon; + /* Set file size by pathname rather than by handle either because no valid, writeable file handle for it was found or because there was an error setting @@ -1798,7 +1806,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) struct inode *inode = direntry->d_inode; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct cifsTconInfo *pTcon; struct cifs_unix_set_info_args *args = NULL; struct cifsFileInfo *open_file; @@ -1889,9 +1897,11 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) if (open_file) { u16 nfid = open_file->netfid; u32 npid = open_file->pid; + pTcon = open_file->tcon; rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); cifsFileInfo_put(open_file); } else { + pTcon = cifs_sb->tcon; rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 9d38a71c8e1..cc70a61a47d 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -37,11 +37,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) int xid; struct cifs_sb_info *cifs_sb; #ifdef CONFIG_CIFS_POSIX + struct cifsFileInfo *pSMBFile = filep->private_data; + struct cifsTconInfo *tcon = pSMBFile->tcon; __u64 ExtAttrBits = 0; __u64 ExtAttrMask = 0; - __u64 caps; - struct cifsTconInfo *tcon; - struct cifsFileInfo *pSMBFile = filep->private_data; + __u64 caps = le64_to_cpu(tcon->fsUnixInfo.Capability); #endif /* CONFIG_CIFS_POSIX */ xid = GetXid(); @@ -50,17 +50,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) cifs_sb = CIFS_SB(inode->i_sb); -#ifdef CONFIG_CIFS_POSIX - tcon = cifs_sb->tcon; - if (tcon) - caps = le64_to_cpu(tcon->fsUnixInfo.Capability); - else { - rc = -EIO; - FreeXid(xid); - return -EIO; - } -#endif /* CONFIG_CIFS_POSIX */ - switch (command) { case CIFS_IOC_CHECKUMOUNT: cFYI(1, "User unmount attempted"); diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 6a8b417baba..d7784a95134 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -228,22 +228,21 @@ static int initiate_cifs_search(const int xid, struct file *file) struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - if (file->private_data == NULL) { + cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); + if (cifs_sb == NULL) + return -EINVAL; + + if (file->private_data == NULL) file->private_data = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); - } if (file->private_data == NULL) return -ENOMEM; cifsFile = file->private_data; cifsFile->invalidHandle = true; cifsFile->srch_inf.endOfSearch = false; - - cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - if (cifs_sb == NULL) - return -EINVAL; - - pTcon = cifs_sb->tcon; + cifsFile->tcon = cifs_sb->tcon; + pTcon = cifsFile->tcon; if (pTcon == NULL) return -EINVAL; @@ -786,9 +785,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - pTcon = cifs_sb->tcon; - if (pTcon == NULL) - return -EINVAL; switch ((int) file->f_pos) { case 0: @@ -838,6 +834,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) CIFSFindClose(xid, pTcon, cifsFile->netfid); } */ + pTcon = cifsFile->tcon; rc = find_cifs_entry(xid, pTcon, file, ¤t_entry, &num_to_fill); if (rc) { -- cgit v1.2.3-18-g5258 From a6e8a8455c94565c53e1a1756d2ab9d9e3a902b8 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Sep 2010 16:01:33 -0700 Subject: cifs: add function to get a tcon from cifs_sb When we convert cifs to do multiple sessions per mount, we'll need more than one tcon per superblock. At that point "cifs_sb->tcon" will make no sense. Add a new accessor function that gets a tcon given a cifs_sb. For now, it just returns cifs_sb->tcon. Later it'll do more. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_dfs_ref.c | 2 +- fs/cifs/cifsacl.c | 16 ++++++++-------- fs/cifs/cifsfs.c | 11 +++-------- fs/cifs/cifsglob.h | 6 ++++++ fs/cifs/connect.c | 4 ++-- fs/cifs/dir.c | 20 ++++++++++---------- fs/cifs/file.c | 4 ++-- fs/cifs/fscache.c | 7 ++++--- fs/cifs/inode.c | 49 +++++++++++++++++++++++++------------------------ fs/cifs/link.c | 8 ++++---- fs/cifs/misc.c | 2 +- fs/cifs/readdir.c | 8 ++++---- fs/cifs/xattr.c | 8 ++++---- 13 files changed, 74 insertions(+), 71 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index d6ced7aa23c..f1e13ea45a1 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -316,7 +316,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) nd->path.dentry = dget(dentry); cifs_sb = CIFS_SB(dentry->d_inode->i_sb); - ses = cifs_sb->tcon->ses; + ses = cifs_sb_tcon(cifs_sb)->ses; if (!ses) { rc = -EINVAL; diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 85d7cf7ff2c..32f244909a0 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -559,7 +559,7 @@ static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, int xid, rc; xid = GetXid(); - rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen); + rc = CIFSSMBGetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, &pntsd, pacllen); FreeXid(xid); @@ -577,7 +577,7 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, xid = GetXid(); - rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, READ_CONTROL, 0, + rc = CIFSSMBOpen(xid, cifs_sb_tcon(cifs_sb), path, FILE_OPEN, READ_CONTROL, 0, &fid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { @@ -585,10 +585,10 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, goto out; } - rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen); + rc = CIFSSMBGetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, &pntsd, pacllen); cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen); - CIFSSMBClose(xid, cifs_sb->tcon, fid); + CIFSSMBClose(xid, cifs_sb_tcon(cifs_sb), fid); out: FreeXid(xid); return pntsd; @@ -618,7 +618,7 @@ static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid, int xid, rc; xid = GetXid(); - rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen); + rc = CIFSSMBSetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, pnntsd, acllen); FreeXid(xid); cFYI(DBG2, "SetCIFSACL rc = %d", rc); @@ -634,7 +634,7 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path, xid = GetXid(); - rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, WRITE_DAC, 0, + rc = CIFSSMBOpen(xid, cifs_sb_tcon(cifs_sb), path, FILE_OPEN, WRITE_DAC, 0, &fid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { @@ -642,10 +642,10 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path, goto out; } - rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen); + rc = CIFSSMBSetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, pnntsd, acllen); cFYI(DBG2, "SetCIFSACL rc = %d", rc); - CIFSSMBClose(xid, cifs_sb->tcon, fid); + CIFSSMBClose(xid, cifs_sb_tcon(cifs_sb), fid); out: FreeXid(xid); return rc; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 7193494efc0..b9624abb726 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -137,9 +137,6 @@ cifs_read_super(struct super_block *sb, void *data, sb->s_magic = CIFS_MAGIC_NUMBER; sb->s_op = &cifs_super_ops; sb->s_bdi = &cifs_sb->bdi; -/* if (cifs_sb->tcon->ses->server->maxBuf > MAX_CIFS_HDR_SIZE + 512) - sb->s_blocksize = - cifs_sb->tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE; */ sb->s_blocksize = CIFS_MAX_MSGSIZE; sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ inode = cifs_root_iget(sb, ROOT_I); @@ -225,7 +222,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; + struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); int rc = -EOPNOTSUPP; int xid; @@ -367,7 +364,7 @@ static int cifs_show_options(struct seq_file *s, struct vfsmount *m) { struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; + struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); struct sockaddr *srcaddr; srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; @@ -458,9 +455,7 @@ static void cifs_umount_begin(struct super_block *sb) if (cifs_sb == NULL) return; - tcon = cifs_sb->tcon; - if (tcon == NULL) - return; + tcon = cifs_sb_tcon(cifs_sb); read_lock(&cifs_tcp_ses_lock); if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) { diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6f3968a5fea..c3510168438 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -413,6 +413,12 @@ CIFS_SB(struct super_block *sb) return sb->s_fs_info; } +static inline struct cifsTconInfo * +cifs_sb_tcon(struct cifs_sb_info *cifs_sb) +{ + return cifs_sb->tcon; +} + static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 230410e0a45..c42d37fb5b7 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3026,8 +3026,8 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) int rc = 0; char *tmp; - if (cifs_sb->tcon) - cifs_put_tcon(cifs_sb->tcon); + if (cifs_sb_tcon(cifs_sb)) + cifs_put_tcon(cifs_sb_tcon(cifs_sb)); cifs_sb->tcon = NULL; tmp = cifs_sb->prepath; diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 613589cf517..f660a15eb14 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -64,8 +64,8 @@ build_path_from_dentry(struct dentry *direntry) cifs_sb = CIFS_SB(direntry->d_sb); dirsep = CIFS_DIR_SEP(cifs_sb); pplen = cifs_sb->prepathlen; - if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS)) - dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1); + if (cifs_sb_tcon(cifs_sb) && (cifs_sb_tcon(cifs_sb)->Flags & SMB_SHARE_IS_IN_DFS)) + dfsplen = strnlen(cifs_sb_tcon(cifs_sb)->treeName, MAX_TREE_SIZE + 1); else dfsplen = 0; cifs_bp_rename_retry: @@ -117,7 +117,7 @@ cifs_bp_rename_retry: /* BB test paths to Windows with '/' in the midst of prepath */ if (dfsplen) { - strncpy(full_path, cifs_sb->tcon->treeName, dfsplen); + strncpy(full_path, cifs_sb_tcon(cifs_sb)->treeName, dfsplen); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { int i; for (i = 0; i < dfsplen; i++) { @@ -150,7 +150,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, pCifsFile->pfile = file; pCifsFile->invalidHandle = false; pCifsFile->closePend = false; - pCifsFile->tcon = cifs_sb->tcon; + pCifsFile->tcon = cifs_sb_tcon(cifs_sb); mutex_init(&pCifsFile->fh_mutex); mutex_init(&pCifsFile->lock_mutex); INIT_LIST_HEAD(&pCifsFile->llist); @@ -158,7 +158,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); write_lock(&GlobalSMBSeslock); - list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList); + list_add(&pCifsFile->tlist, &cifs_sb_tcon(cifs_sb)->openFileList); pCifsInode = CIFS_I(newinode); if (pCifsInode) { /* if readable file instance put first in list*/ @@ -225,7 +225,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode, posix_flags |= SMB_O_DIRECT; mode &= ~current_umask(); - rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode, + rc = CIFSPOSIXCreate(xid, cifs_sb_tcon(cifs_sb), posix_flags, mode, pnetfid, presp_data, poplock, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -298,7 +298,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - tcon = cifs_sb->tcon; + tcon = cifs_sb_tcon(cifs_sb); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -373,7 +373,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, if (!tcon->unix_ext && (mode & S_IWUGO) == 0) create_options |= CREATE_OPTION_READONLY; - if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) + if (tcon->ses->capabilities & CAP_NT_SMBS) rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess, create_options, &fileHandle, &oplock, buf, cifs_sb->local_nls, @@ -504,7 +504,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -631,7 +631,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, /* check whether path exists */ cifs_sb = CIFS_SB(parent_dir_inode->i_sb); - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); /* * Don't allow the separator character in a path component. diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 84979fc7786..6712c2b0a0a 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -235,7 +235,7 @@ int cifs_open(struct inode *inode, struct file *file) xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - tcon = cifs_sb->tcon; + tcon = cifs_sb_tcon(cifs_sb); pCifsInode = CIFS_I(file->f_path.dentry->d_inode); @@ -345,7 +345,7 @@ int cifs_open(struct inode *inode, struct file *file) goto out; } - if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) + if (tcon->ses->capabilities & CAP_NT_SMBS) rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index 9f3f5c4be16..ec4318b019c 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -66,11 +66,11 @@ static void cifs_fscache_enable_inode_cookie(struct inode *inode) if (cifsi->fscache) return; - cifsi->fscache = fscache_acquire_cookie(cifs_sb->tcon->fscache, + cifsi->fscache = fscache_acquire_cookie(cifs_sb_tcon(cifs_sb)->fscache, &cifs_fscache_inode_object_def, cifsi); cFYI(1, "CIFS: got FH cookie (0x%p/0x%p)", - cifs_sb->tcon->fscache, cifsi->fscache); + cifs_sb_tcon(cifs_sb)->fscache, cifsi->fscache); } void cifs_fscache_release_inode_cookie(struct inode *inode) @@ -117,7 +117,8 @@ void cifs_fscache_reset_inode_cookie(struct inode *inode) /* retire the current fscache cache and get a new one */ fscache_relinquish_cookie(cifsi->fscache, 1); - cifsi->fscache = fscache_acquire_cookie(cifs_sb->tcon->fscache, + cifsi->fscache = fscache_acquire_cookie( + cifs_sb_tcon(cifs_sb)->fscache, &cifs_fscache_inode_object_def, cifsi); cFYI(1, "CIFS: new cookie 0x%p oldcookie 0x%p", diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 0fa145596fc..dce2d598927 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -52,7 +52,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) /* check if server can support readpages */ - if (cifs_sb->tcon->ses->server->maxBuf < + if (cifs_sb_tcon(cifs_sb)->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else @@ -315,7 +315,8 @@ int cifs_get_inode_info_unix(struct inode **pinode, struct cifsTconInfo *tcon; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - tcon = cifs_sb->tcon; + tcon = cifs_sb_tcon(cifs_sb); + cFYI(1, "Getting info on %s", full_path); /* could have done a find first instead but this returns more info */ @@ -360,7 +361,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, int rc; int oplock = 0; __u16 netfid; - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb); char buf[24]; unsigned int bytes_read; char *pbuf; @@ -449,7 +450,7 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, char ea_value[4]; __u32 mode; - rc = CIFSSMBQAllEAs(xid, cifs_sb->tcon, path, "SETFILEBITS", + rc = CIFSSMBQAllEAs(xid, cifs_sb_tcon(cifs_sb), path, "SETFILEBITS", ea_value, 4 /* size of buf */, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -489,8 +490,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); if (adjust_tz) { - fattr->cf_ctime.tv_sec += cifs_sb->tcon->ses->server->timeAdj; - fattr->cf_mtime.tv_sec += cifs_sb->tcon->ses->server->timeAdj; + fattr->cf_ctime.tv_sec += cifs_sb_tcon(cifs_sb)->ses->server->timeAdj; + fattr->cf_mtime.tv_sec += cifs_sb_tcon(cifs_sb)->ses->server->timeAdj; } fattr->cf_eof = le64_to_cpu(info->EndOfFile); @@ -566,7 +567,7 @@ int cifs_get_inode_info(struct inode **pinode, bool adjustTZ = false; struct cifs_fattr fattr; - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); cFYI(1, "Getting info on %s", full_path); if ((pfindData == NULL) && (*pinode != NULL)) { @@ -706,8 +707,8 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) return full_path; } - if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS)) - dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1); + if (cifs_sb_tcon(cifs_sb) && (cifs_sb_tcon(cifs_sb)->Flags & SMB_SHARE_IS_IN_DFS)) + dfsplen = strnlen(cifs_sb_tcon(cifs_sb)->treeName, MAX_TREE_SIZE + 1); else dfsplen = 0; @@ -716,7 +717,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) return full_path; if (dfsplen) { - strncpy(full_path, cifs_sb->tcon->treeName, dfsplen); + strncpy(full_path, cifs_sb_tcon(cifs_sb)->treeName, dfsplen); /* switch slash direction in prepath depending on whether * windows or posix style path names */ @@ -841,7 +842,7 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) return ERR_PTR(-ENOMEM); xid = GetXid(); - if (cifs_sb->tcon->unix_ext) + if (cifs_sb_tcon(cifs_sb)->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, sb, @@ -852,10 +853,10 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) #ifdef CONFIG_CIFS_FSCACHE /* populate tcon->resource_id */ - cifs_sb->tcon->resource_id = CIFS_I(inode)->uniqueid; + cifs_sb_tcon(cifs_sb)->resource_id = CIFS_I(inode)->uniqueid; #endif - if (rc && cifs_sb->tcon->ipc) { + if (rc && cifs_sb_tcon(cifs_sb)->ipc) { cFYI(1, "ipc connection - fake read inode"); inode->i_mode |= S_IFDIR; inode->i_nlink = 2; @@ -938,7 +939,7 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, goto set_via_filehandle; } - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); /* * NT4 apparently returns success on this call, but it doesn't @@ -1000,7 +1001,7 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid) struct inode *inode = dentry->d_inode; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; + struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); __u32 dosattr, origattr; FILE_BASIC_INFO *info_buf = NULL; @@ -1111,7 +1112,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) struct cifsInodeInfo *cifs_inode; struct super_block *sb = dir->i_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; + struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); struct iattr *attrs = NULL; __u32 dosattr = 0, origattr = 0; @@ -1213,7 +1214,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -1394,7 +1395,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -1435,7 +1436,7 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, struct dentry *to_dentry, const char *toPath) { struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb); __u16 srcfid; int oplock, rc; @@ -1486,7 +1487,7 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, int xid, rc, tmprc; cifs_sb = CIFS_SB(source_dir->i_sb); - tcon = cifs_sb->tcon; + tcon = cifs_sb_tcon(cifs_sb); xid = GetXid(); @@ -1660,7 +1661,7 @@ int cifs_revalidate_dentry(struct dentry *dentry) "jiffies %ld", full_path, inode, inode->i_count.counter, dentry, dentry->d_time, jiffies); - if (CIFS_SB(sb)->tcon->unix_ext) + if (cifs_sb_tcon(CIFS_SB(sb))->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, sb, @@ -1755,7 +1756,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, if (rc != 0) { if (pTcon == NULL) - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); /* Set file size by pathname rather than by handle either because no valid, writeable file handle for @@ -1901,7 +1902,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); cifsFileInfo_put(open_file); } else { - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & @@ -2086,7 +2087,7 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs) { struct inode *inode = direntry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb); if (pTcon->unix_ext) return cifs_setattr_unix(direntry, attrs); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index cbf7b112287..66db2d61fa4 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -249,7 +249,7 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr, int rc; int oplock = 0; __u16 netfid = 0; - struct cifsTconInfo *pTcon = cifs_sb->tcon; + struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb); u8 *buf; char *pbuf; unsigned int bytes_read = 0; @@ -321,7 +321,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, xid = GetXid(); cifs_sb_target = CIFS_SB(inode->i_sb); - pTcon = cifs_sb_target->tcon; + pTcon = cifs_sb_tcon(cifs_sb_target); /* No need to check for cross device links since server will do that BB note DFS case in future though (when we may have to check) */ @@ -390,7 +390,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) char *full_path = NULL; char *target_path = NULL; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb->tcon; + struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); xid = GetXid(); @@ -459,7 +459,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); full_path = build_path_from_dentry(direntry); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 3ccadc1326d..c5cbfdb2a58 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -729,6 +729,6 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) "properly. Hardlinks will not be recognized on this " "mount. Consider mounting with the \"noserverino\" " "option to silence this message.", - cifs_sb->tcon->treeName); + cifs_sb_tcon(cifs_sb)->treeName); } } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index d7784a95134..3efc2424964 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -102,7 +102,7 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name, return NULL; } - if (CIFS_SB(sb)->tcon->nocase) + if (cifs_sb_tcon(CIFS_SB(sb))->nocase) dentry->d_op = &cifs_ci_dentry_ops; else dentry->d_op = &cifs_dentry_ops; @@ -171,7 +171,7 @@ static void cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, struct cifs_sb_info *cifs_sb) { - int offset = cifs_sb->tcon->ses->server->timeAdj; + int offset = cifs_sb_tcon(cifs_sb)->ses->server->timeAdj; memset(fattr, 0, sizeof(*fattr)); fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate, @@ -199,7 +199,7 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, int len; int oplock = 0; int rc; - struct cifsTconInfo *ptcon = cifs_sb->tcon; + struct cifsTconInfo *ptcon = cifs_sb_tcon(cifs_sb); char *tmpbuffer; rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ, @@ -241,7 +241,7 @@ static int initiate_cifs_search(const int xid, struct file *file) cifsFile = file->private_data; cifsFile->invalidHandle = true; cifsFile->srch_inf.endOfSearch = false; - cifsFile->tcon = cifs_sb->tcon; + cifsFile->tcon = cifs_sb_tcon(cifs_sb); pTcon = cifsFile->tcon; if (pTcon == NULL) return -EINVAL; diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index a1509207bfa..41f95bf6797 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -61,7 +61,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name) xid = GetXid(); cifs_sb = CIFS_SB(sb); - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -116,7 +116,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, xid = GetXid(); cifs_sb = CIFS_SB(sb); - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -224,7 +224,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, xid = GetXid(); cifs_sb = CIFS_SB(sb); - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -346,7 +346,7 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) return -EIO; cifs_sb = CIFS_SB(sb); - pTcon = cifs_sb->tcon; + pTcon = cifs_sb_tcon(cifs_sb); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) return -EOPNOTSUPP; -- cgit v1.2.3-18-g5258 From f6acb9d0596889a774e142ed76cb05b90d9763d2 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Sep 2010 16:01:34 -0700 Subject: cifs: temporarily rename cifs_sb->tcon to ptcon to catch stragglers Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 2 +- fs/cifs/cifsglob.h | 2 +- fs/cifs/connect.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 7fde5296989..ba0afd3acff 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -39,7 +39,7 @@ #define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */ struct cifs_sb_info { - struct cifsTconInfo *tcon; /* primary mount */ + struct cifsTconInfo *ptcon; /* primary mount */ struct list_head nested_tcon_q; struct nls_table *local_nls; unsigned int rsize; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c3510168438..cc8300c741b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -416,7 +416,7 @@ CIFS_SB(struct super_block *sb) static inline struct cifsTconInfo * cifs_sb_tcon(struct cifs_sb_info *cifs_sb) { - return cifs_sb->tcon; + return cifs_sb->ptcon; } static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c42d37fb5b7..b4bacea5462 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2727,7 +2727,7 @@ try_mount_again: goto remote_path_check; } - cifs_sb->tcon = tcon; + cifs_sb->ptcon = tcon; /* do not care if following two calls succeed - informational */ if (!tcon->ipc) { @@ -3029,7 +3029,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) if (cifs_sb_tcon(cifs_sb)) cifs_put_tcon(cifs_sb_tcon(cifs_sb)); - cifs_sb->tcon = NULL; + cifs_sb->ptcon = NULL; tmp = cifs_sb->prepath; cifs_sb->prepathlen = 0; cifs_sb->prepath = NULL; -- cgit v1.2.3-18-g5258 From 0d424ad0a4b8c08e45928bccfa5b4b240097b01b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Sep 2010 16:01:35 -0700 Subject: cifs: add cifs_sb_master_tcon and convert some callers to use it At mount time, we'll always need to create a tcon that will serve as a template for others that are associated with the mount. This tcon is known as the "master" tcon. In some cases, we'll need to use that tcon regardless of who's accessing the mount. Add an accessor function for the master tcon and go ahead and switch the appropriate places to use it. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 6 +++--- fs/cifs/cifsglob.h | 7 +++++++ fs/cifs/connect.c | 4 ++-- fs/cifs/dir.c | 10 +++++----- fs/cifs/fscache.c | 12 ++++++------ fs/cifs/inode.c | 29 ++++++++++++++++------------- fs/cifs/misc.c | 2 +- fs/cifs/readdir.c | 4 ++-- 8 files changed, 42 insertions(+), 32 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index b9624abb726..898d2a5cfad 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -222,7 +222,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); int rc = -EOPNOTSUPP; int xid; @@ -364,7 +364,7 @@ static int cifs_show_options(struct seq_file *s, struct vfsmount *m) { struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb); - struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); struct sockaddr *srcaddr; srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; @@ -455,7 +455,7 @@ static void cifs_umount_begin(struct super_block *sb) if (cifs_sb == NULL) return; - tcon = cifs_sb_tcon(cifs_sb); + tcon = cifs_sb_master_tcon(cifs_sb); read_lock(&cifs_tcp_ses_lock); if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) { diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index cc8300c741b..c265ebdcd17 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -419,6 +419,13 @@ cifs_sb_tcon(struct cifs_sb_info *cifs_sb) return cifs_sb->ptcon; } +/* This function is always expected to succeed */ +static inline struct cifsTconInfo * +cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) +{ + return cifs_sb->ptcon; +} + static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index b4bacea5462..f6a3091c287 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3025,9 +3025,9 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) { int rc = 0; char *tmp; + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); - if (cifs_sb_tcon(cifs_sb)) - cifs_put_tcon(cifs_sb_tcon(cifs_sb)); + cifs_put_tcon(tcon); cifs_sb->ptcon = NULL; tmp = cifs_sb->prepath; diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index f660a15eb14..fe02435acb3 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -54,18 +54,18 @@ build_path_from_dentry(struct dentry *direntry) int dfsplen; char *full_path; char dirsep; - struct cifs_sb_info *cifs_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); if (direntry == NULL) return NULL; /* not much we can do if dentry is freed and we need to reopen the file after it was closed implicitly when the server crashed */ - cifs_sb = CIFS_SB(direntry->d_sb); dirsep = CIFS_DIR_SEP(cifs_sb); pplen = cifs_sb->prepathlen; - if (cifs_sb_tcon(cifs_sb) && (cifs_sb_tcon(cifs_sb)->Flags & SMB_SHARE_IS_IN_DFS)) - dfsplen = strnlen(cifs_sb_tcon(cifs_sb)->treeName, MAX_TREE_SIZE + 1); + if (tcon->Flags & SMB_SHARE_IS_IN_DFS) + dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1); else dfsplen = 0; cifs_bp_rename_retry: @@ -117,7 +117,7 @@ cifs_bp_rename_retry: /* BB test paths to Windows with '/' in the midst of prepath */ if (dfsplen) { - strncpy(full_path, cifs_sb_tcon(cifs_sb)->treeName, dfsplen); + strncpy(full_path, tcon->treeName, dfsplen); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { int i; for (i = 0; i < dfsplen; i++) { diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index ec4318b019c..a2ad94efcfe 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -62,15 +62,15 @@ static void cifs_fscache_enable_inode_cookie(struct inode *inode) { struct cifsInodeInfo *cifsi = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); if (cifsi->fscache) return; - cifsi->fscache = fscache_acquire_cookie(cifs_sb_tcon(cifs_sb)->fscache, - &cifs_fscache_inode_object_def, - cifsi); - cFYI(1, "CIFS: got FH cookie (0x%p/0x%p)", - cifs_sb_tcon(cifs_sb)->fscache, cifsi->fscache); + cifsi->fscache = fscache_acquire_cookie(tcon->fscache, + &cifs_fscache_inode_object_def, cifsi); + cFYI(1, "CIFS: got FH cookie (0x%p/0x%p)", tcon->fscache, + cifsi->fscache); } void cifs_fscache_release_inode_cookie(struct inode *inode) @@ -118,7 +118,7 @@ void cifs_fscache_reset_inode_cookie(struct inode *inode) fscache_relinquish_cookie(cifsi->fscache, 1); cifsi->fscache = fscache_acquire_cookie( - cifs_sb_tcon(cifs_sb)->fscache, + cifs_sb_master_tcon(cifs_sb)->fscache, &cifs_fscache_inode_object_def, cifsi); cFYI(1, "CIFS: new cookie 0x%p oldcookie 0x%p", diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index dce2d598927..da716d96dae 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -52,7 +52,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) /* check if server can support readpages */ - if (cifs_sb_tcon(cifs_sb)->ses->server->maxBuf < + if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) inode->i_data.a_ops = &cifs_addr_ops_smallbuf; else @@ -476,6 +476,8 @@ static void cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, struct cifs_sb_info *cifs_sb, bool adjust_tz) { + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); + memset(fattr, 0, sizeof(*fattr)); fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); if (info->DeletePending) @@ -490,8 +492,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); if (adjust_tz) { - fattr->cf_ctime.tv_sec += cifs_sb_tcon(cifs_sb)->ses->server->timeAdj; - fattr->cf_mtime.tv_sec += cifs_sb_tcon(cifs_sb)->ses->server->timeAdj; + fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; + fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; } fattr->cf_eof = le64_to_cpu(info->EndOfFile); @@ -698,6 +700,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) int pplen = cifs_sb->prepathlen; int dfsplen; char *full_path = NULL; + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); /* if no prefix path, simply set path to the root of share to "" */ if (pplen == 0) { @@ -707,8 +710,8 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) return full_path; } - if (cifs_sb_tcon(cifs_sb) && (cifs_sb_tcon(cifs_sb)->Flags & SMB_SHARE_IS_IN_DFS)) - dfsplen = strnlen(cifs_sb_tcon(cifs_sb)->treeName, MAX_TREE_SIZE + 1); + if (tcon->Flags & SMB_SHARE_IS_IN_DFS) + dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1); else dfsplen = 0; @@ -717,7 +720,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) return full_path; if (dfsplen) { - strncpy(full_path, cifs_sb_tcon(cifs_sb)->treeName, dfsplen); + strncpy(full_path, tcon->treeName, dfsplen); /* switch slash direction in prepath depending on whether * windows or posix style path names */ @@ -831,18 +834,18 @@ retry_iget5_locked: struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) { int xid; - struct cifs_sb_info *cifs_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct inode *inode = NULL; long rc; char *full_path; + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); - cifs_sb = CIFS_SB(sb); full_path = cifs_build_path_to_root(cifs_sb); if (full_path == NULL) return ERR_PTR(-ENOMEM); xid = GetXid(); - if (cifs_sb_tcon(cifs_sb)->unix_ext) + if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, sb, @@ -853,10 +856,10 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) #ifdef CONFIG_CIFS_FSCACHE /* populate tcon->resource_id */ - cifs_sb_tcon(cifs_sb)->resource_id = CIFS_I(inode)->uniqueid; + tcon->resource_id = CIFS_I(inode)->uniqueid; #endif - if (rc && cifs_sb_tcon(cifs_sb)->ipc) { + if (rc && tcon->ipc) { cFYI(1, "ipc connection - fake read inode"); inode->i_mode |= S_IFDIR; inode->i_nlink = 2; @@ -1661,7 +1664,7 @@ int cifs_revalidate_dentry(struct dentry *dentry) "jiffies %ld", full_path, inode, inode->i_count.counter, dentry, dentry->d_time, jiffies); - if (cifs_sb_tcon(CIFS_SB(sb))->unix_ext) + if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); else rc = cifs_get_inode_info(&inode, full_path, NULL, sb, @@ -2087,7 +2090,7 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs) { struct inode *inode = direntry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb); + struct cifsTconInfo *pTcon = cifs_sb_master_tcon(cifs_sb); if (pTcon->unix_ext) return cifs_setattr_unix(direntry, attrs); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index c5cbfdb2a58..252f2768db8 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -729,6 +729,6 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) "properly. Hardlinks will not be recognized on this " "mount. Consider mounting with the \"noserverino\" " "option to silence this message.", - cifs_sb_tcon(cifs_sb)->treeName); + cifs_sb_master_tcon(cifs_sb)->treeName); } } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 3efc2424964..887a7e23037 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -102,7 +102,7 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name, return NULL; } - if (cifs_sb_tcon(CIFS_SB(sb))->nocase) + if (cifs_sb_master_tcon(CIFS_SB(sb))->nocase) dentry->d_op = &cifs_ci_dentry_ops; else dentry->d_op = &cifs_dentry_ops; @@ -171,7 +171,7 @@ static void cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, struct cifs_sb_info *cifs_sb) { - int offset = cifs_sb_tcon(cifs_sb)->ses->server->timeAdj; + int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj; memset(fattr, 0, sizeof(*fattr)); fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate, -- cgit v1.2.3-18-g5258 From f7a40689fd1e963cb1006349e050c07584895db5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 20 Sep 2010 16:01:36 -0700 Subject: cifs: have cifs_new_fileinfo take a tcon arg To minimize calls to cifs_sb_tcon and to allow for a clear error path if a tcon can't be acquired. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 4 ++-- fs/cifs/dir.c | 22 ++++++++++++---------- fs/cifs/file.c | 4 ++-- 3 files changed, 16 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 8604a45c110..610b2263c9f 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -107,8 +107,8 @@ extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, - struct vfsmount *mnt, unsigned int oflags, - __u32 oplock); + struct vfsmount *mnt, struct cifsTconInfo *tcon, + unsigned int oflags, __u32 oplock); extern int cifs_posix_open(char *full_path, struct inode **pinode, struct super_block *sb, int mode, int oflags, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index fe02435acb3..23ec28a4c11 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -131,9 +131,9 @@ cifs_bp_rename_retry: } struct cifsFileInfo * -cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, - struct file *file, struct vfsmount *mnt, unsigned int oflags, - __u32 oplock) +cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, + struct vfsmount *mnt, struct cifsTconInfo *tcon, + unsigned int oflags, __u32 oplock) { struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; @@ -150,7 +150,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, pCifsFile->pfile = file; pCifsFile->invalidHandle = false; pCifsFile->closePend = false; - pCifsFile->tcon = cifs_sb_tcon(cifs_sb); + pCifsFile->tcon = tcon; mutex_init(&pCifsFile->fh_mutex); mutex_init(&pCifsFile->lock_mutex); INIT_LIST_HEAD(&pCifsFile->llist); @@ -158,7 +158,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); write_lock(&GlobalSMBSeslock); - list_add(&pCifsFile->tlist, &cifs_sb_tcon(cifs_sb)->openFileList); + list_add(&pCifsFile->tlist, &tcon->openFileList); pCifsInode = CIFS_I(newinode); if (pCifsInode) { /* if readable file instance put first in list*/ @@ -191,6 +191,7 @@ int cifs_posix_open(char *full_path, struct inode **pinode, __u32 posix_flags = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_fattr fattr; + struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); cFYI(1, "posix open %s", full_path); @@ -225,9 +226,9 @@ int cifs_posix_open(char *full_path, struct inode **pinode, posix_flags |= SMB_O_DIRECT; mode &= ~current_umask(); - rc = CIFSPOSIXCreate(xid, cifs_sb_tcon(cifs_sb), posix_flags, mode, - pnetfid, presp_data, poplock, full_path, - cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & + rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data, + poplock, full_path, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) goto posix_open_ret; @@ -466,7 +467,8 @@ cifs_create_set_dentry: } pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp, - nd->path.mnt, oflags, oplock); + nd->path.mnt, tcon, oflags, + oplock); if (pfile_info == NULL) { fput(filp); CIFSSMBClose(xid, tcon, fileHandle); @@ -726,7 +728,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, } cfile = cifs_new_fileinfo(newInode, fileHandle, filp, - nd->path.mnt, + nd->path.mnt, pTcon, nd->intent.open.flags, oplock); if (cfile == NULL) { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 6712c2b0a0a..e5f463e15c9 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -277,7 +277,7 @@ int cifs_open(struct inode *inode, struct file *file) pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, - oflags, oplock); + tcon, oflags, oplock); if (pCifsFile == NULL) { CIFSSMBClose(xid, tcon, netfid); rc = -ENOMEM; @@ -370,7 +370,7 @@ int cifs_open(struct inode *inode, struct file *file) goto out; pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, - file->f_flags, oplock); + tcon, file->f_flags, oplock); if (pCifsFile == NULL) { rc = -ENOMEM; goto out; -- cgit v1.2.3-18-g5258 From f3983c2133e9bea9c8b4f690737d15e3e9b02491 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 22 Sep 2010 16:17:40 -0700 Subject: cifs: fix handling of signing with writepages (try #6) Get a reference to the file early so we can eventually base the decision about signing on the correct tcon. If that doesn't work for some reason, then fall back to generic_writepages. That's just as likely to fail, but it simplifies the error handling. In truth, I'm not sure how that could occur anyway, so maybe a NULL open_file here ought to be a BUG()? After that, we drop the reference to the open_file and then we re-get one prior to each WriteAndX call. This helps ensure that the filehandle isn't held open any longer than necessary and that open files are reclaimed prior to each write call. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/file.c | 68 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 31 deletions(-) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index e5f463e15c9..de046e183d1 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1353,6 +1353,15 @@ static int cifs_writepages(struct address_space *mapping, int scanned = 0; int xid, long_op; + /* + * BB: Is this meaningful for a non-block-device file system? + * If it is, we should test it again after we do I/O + */ + if (wbc->nonblocking && bdi_write_congested(bdi)) { + wbc->encountered_congestion = 1; + return 0; + } + cifs_sb = CIFS_SB(mapping->host->i_sb); /* @@ -1362,26 +1371,28 @@ static int cifs_writepages(struct address_space *mapping, if (cifs_sb->wsize < PAGE_CACHE_SIZE) return generic_writepages(mapping, wbc); - if ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server)) - if (cifs_sb->tcon->ses->server->secMode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - if (!experimEnabled) - return generic_writepages(mapping, wbc); - iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL); if (iov == NULL) return generic_writepages(mapping, wbc); - /* - * BB: Is this meaningful for a non-block-device file system? - * If it is, we should test it again after we do I/O + * if there's no open file, then this is likely to fail too, + * but it'll at least handle the return. Maybe it should be + * a BUG() instead? */ - if (wbc->nonblocking && bdi_write_congested(bdi)) { - wbc->encountered_congestion = 1; + open_file = find_writable_file(CIFS_I(mapping->host)); + if (!open_file) { kfree(iov); - return 0; + return generic_writepages(mapping, wbc); + } + + tcon = open_file->tcon; + if (!experimEnabled && tcon->ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + cifsFileInfo_put(open_file); + return generic_writepages(mapping, wbc); } + cifsFileInfo_put(open_file); xid = GetXid(); @@ -1486,38 +1497,33 @@ retry: break; } if (n_iov) { - /* Search for a writable handle every time we call - * CIFSSMBWrite2. We can't rely on the last handle - * we used to still be valid - */ open_file = find_writable_file(CIFS_I(mapping->host)); if (!open_file) { cERROR(1, "No writable handles for inode"); rc = -EBADF; } else { - tcon = open_file->tcon; long_op = cifs_write_timeout(cifsi, offset); - rc = CIFSSMBWrite2(xid, tcon, - open_file->netfid, + rc = CIFSSMBWrite2(xid, tcon, open_file->netfid, bytes_to_write, offset, &bytes_written, iov, n_iov, long_op); cifsFileInfo_put(open_file); cifs_update_eof(cifsi, offset, bytes_written); + } - if (rc || bytes_written < bytes_to_write) { - cERROR(1, "Write2 ret %d, wrote %d", - rc, bytes_written); - /* BB what if continued retry is - requested via mount flags? */ - if (rc == -ENOSPC) - set_bit(AS_ENOSPC, &mapping->flags); - else - set_bit(AS_EIO, &mapping->flags); - } else { - cifs_stats_bytes_written(tcon, bytes_written); - } + if (rc || bytes_written < bytes_to_write) { + cERROR(1, "Write2 ret %d, wrote %d", + rc, bytes_written); + /* BB what if continued retry is + requested via mount flags? */ + if (rc == -ENOSPC) + set_bit(AS_ENOSPC, &mapping->flags); + else + set_bit(AS_EIO, &mapping->flags); + } else { + cifs_stats_bytes_written(tcon, bytes_written); } + for (i = 0; i < n_iov; i++) { page = pvec.pages[first + i]; /* Should we also set page error on -- cgit v1.2.3-18-g5258 From 522440ed55d2cc8855ea5f82bc067e0483b2e1be Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 29 Sep 2010 09:49:54 -0400 Subject: cifs: set backing_dev_info on new S_ISREG inodes Testing on very recent kernel (2.6.36-rc6) made this warning pop: WARNING: at fs/fs-writeback.c:87 inode_to_bdi+0x65/0x70() Hardware name: Dirtiable inode bdi default != sb bdi cifs ...the following patch fixes it and seems to be the obviously correct thing to do for cifs. Cc: stable@kernel.org Acked-by: Dave Kleikamp Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/inode.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 93f77d438d3..53cce8cc222 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -801,6 +801,8 @@ retry_iget5_locked: inode->i_flags |= S_NOATIME | S_NOCMTIME; if (inode->i_state & I_NEW) { inode->i_ino = hash; + if (S_ISREG(inode->i_mode)) + inode->i_data.backing_dev_info = sb->s_bdi; #ifdef CONFIG_CIFS_FSCACHE /* initialize per-inode cache cookie pointer */ CIFS_I(inode)->fscache = NULL; -- cgit v1.2.3-18-g5258 From 1fc8a117865b54590acd773a55fbac9221b018f0 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Wed, 29 Sep 2010 17:33:05 -0700 Subject: ocfs2: Don't walk off the end of fast symlinks. ocfs2 fast symlinks are NUL terminated strings stored inline in the inode data area. However, disk corruption or a local attacker could, in theory, remove that NUL. Because we're using strlen() (my fault, introduced in a731d1 when removing vfs_follow_link()), we could walk off the end of that string. Signed-off-by: Joel Becker Cc: stable@kernel.org --- fs/ocfs2/symlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c index 32499d213fc..9975457c981 100644 --- a/fs/ocfs2/symlink.c +++ b/fs/ocfs2/symlink.c @@ -128,7 +128,7 @@ static void *ocfs2_fast_follow_link(struct dentry *dentry, } /* Fast symlinks can't be large */ - len = strlen(target); + len = strnlen(target, ocfs2_fast_symlink_chars(inode->i_sb)); link = kzalloc(len + 1, GFP_NOFS); if (!link) { status = -ENOMEM; -- cgit v1.2.3-18-g5258 From 46290341cd649c2bfb69e5067c1804c0395c83a1 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 30 Sep 2010 10:34:00 -0400 Subject: GFS2 fatal: filesystem consistency error on rename This patch fixes a GFS2 problem whereby the first rename after a mount can result in a file system consistency error being flagged improperly and cause the file system to withdraw. The problem is that the rename code tries to run the rgrp list with function gfs2_blk2rgrpd before the rgrp list is guaranteed to be read in from disk. The patch makes the rename function hold the rindex glock (as the gfs2_unlink code does today) which reads in the rgrp list if need be. There were a total of three places in the rename code that improperly referenced the rgrp list without the rindex glock and this patch fixes all three. Signed-off-by: Bob Peterson Signed-off-by: Steven Whitehouse --- fs/gfs2/ops_inode.c | 8 ++++++-- fs/gfs2/rgrp.c | 22 +++++++++++++--------- fs/gfs2/rgrp.h | 8 +++++--- 3 files changed, 24 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index fba00171d91..0534510200d 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -736,7 +736,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, struct gfs2_inode *ip = GFS2_I(odentry->d_inode); struct gfs2_inode *nip = NULL; struct gfs2_sbd *sdp = GFS2_SB(odir); - struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, }; + struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, }, ri_gh; struct gfs2_rgrpd *nrgd; unsigned int num_gh; int dir_rename = 0; @@ -750,6 +750,9 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, return 0; } + error = gfs2_rindex_hold(sdp, &ri_gh); + if (error) + return error; if (odip != ndip) { error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE, @@ -879,7 +882,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, al->al_requested = sdp->sd_max_dirres; - error = gfs2_inplace_reserve(ndip); + error = gfs2_inplace_reserve_ri(ndip); if (error) goto out_gunlock_q; @@ -961,6 +964,7 @@ out_gunlock_r: if (r_gh.gh_gl) gfs2_glock_dq_uninit(&r_gh); out: + gfs2_glock_dq_uninit(&ri_gh); return error; } diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index f9ddcf40175..fb67f593f40 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1200,7 +1200,8 @@ out: * Returns: errno */ -int gfs2_inplace_reserve_i(struct gfs2_inode *ip, char *file, unsigned int line) +int gfs2_inplace_reserve_i(struct gfs2_inode *ip, int hold_rindex, + char *file, unsigned int line) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_alloc *al = ip->i_alloc; @@ -1211,12 +1212,15 @@ int gfs2_inplace_reserve_i(struct gfs2_inode *ip, char *file, unsigned int line) return -EINVAL; try_again: - /* We need to hold the rindex unless the inode we're using is - the rindex itself, in which case it's already held. */ - if (ip != GFS2_I(sdp->sd_rindex)) - error = gfs2_rindex_hold(sdp, &al->al_ri_gh); - else if (!sdp->sd_rgrps) /* We may not have the rindex read in, so: */ - error = gfs2_ri_update_special(ip); + if (hold_rindex) { + /* We need to hold the rindex unless the inode we're using is + the rindex itself, in which case it's already held. */ + if (ip != GFS2_I(sdp->sd_rindex)) + error = gfs2_rindex_hold(sdp, &al->al_ri_gh); + else if (!sdp->sd_rgrps) /* We may not have the rindex read + in, so: */ + error = gfs2_ri_update_special(ip); + } if (error) return error; @@ -1227,7 +1231,7 @@ try_again: try to free it, and try the allocation again. */ error = get_local_rgrp(ip, &unlinked, &last_unlinked); if (error) { - if (ip != GFS2_I(sdp->sd_rindex)) + if (hold_rindex && ip != GFS2_I(sdp->sd_rindex)) gfs2_glock_dq_uninit(&al->al_ri_gh); if (error != -EAGAIN) return error; @@ -1269,7 +1273,7 @@ void gfs2_inplace_release(struct gfs2_inode *ip) al->al_rgd = NULL; if (al->al_rgd_gh.gh_gl) gfs2_glock_dq_uninit(&al->al_rgd_gh); - if (ip != GFS2_I(sdp->sd_rindex)) + if (ip != GFS2_I(sdp->sd_rindex) && al->al_ri_gh.gh_gl) gfs2_glock_dq_uninit(&al->al_ri_gh); } diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h index f07119d8955..0e35c0466f9 100644 --- a/fs/gfs2/rgrp.h +++ b/fs/gfs2/rgrp.h @@ -39,10 +39,12 @@ static inline void gfs2_alloc_put(struct gfs2_inode *ip) ip->i_alloc = NULL; } -extern int gfs2_inplace_reserve_i(struct gfs2_inode *ip, char *file, - unsigned int line); +extern int gfs2_inplace_reserve_i(struct gfs2_inode *ip, int hold_rindex, + char *file, unsigned int line); #define gfs2_inplace_reserve(ip) \ -gfs2_inplace_reserve_i((ip), __FILE__, __LINE__) + gfs2_inplace_reserve_i((ip), 1, __FILE__, __LINE__) +#define gfs2_inplace_reserve_ri(ip) \ + gfs2_inplace_reserve_i((ip), 0, __FILE__, __LINE__) extern void gfs2_inplace_release(struct gfs2_inode *ip); -- cgit v1.2.3-18-g5258 From 249e6353001e407edf5c9a74482ecfca90c8ff33 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:41:27 +0200 Subject: hfsplus: fix BKL leak in hfsplus_ioctl Currenly the HFSPLUS_IOC_EXT2_GETFLAGS case never unlocks the BKL, which can lead to easily reproduced lockups when doing multiple GETFLAGS ioctls. Fix this by only taking the BKL for the HFSPLUS_IOC_EXT2_SETFLAGS case as neither HFSPLUS_IOC_EXT2_GETFLAGS not the default error case needs it. Signed-off-by: Christoph Hellwig --- fs/hfsplus/ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c index ac405f09902..59dc402dfe9 100644 --- a/fs/hfsplus/ioctl.c +++ b/fs/hfsplus/ioctl.c @@ -26,7 +26,6 @@ long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct inode *inode = filp->f_path.dentry->d_inode; unsigned int flags; - lock_kernel(); switch (cmd) { case HFSPLUS_IOC_EXT2_GETFLAGS: flags = 0; @@ -39,6 +38,8 @@ long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return put_user(flags, (int __user *)arg); case HFSPLUS_IOC_EXT2_SETFLAGS: { int err = 0; + + lock_kernel(); err = mnt_want_write(filp->f_path.mnt); if (err) { unlock_kernel(); @@ -93,7 +94,6 @@ setflags_out: return err; } default: - unlock_kernel(); return -ENOTTY; } } -- cgit v1.2.3-18-g5258 From 94744567fef9602c3d8218a1d8f58c04cce354f6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:41:31 +0200 Subject: hfsplus: split hfsplus_ioctl Give each ioctl command a function of it's own. Signed-off-by: Christoph Hellwig --- fs/hfsplus/ioctl.c | 145 +++++++++++++++++++++++++++++------------------------ 1 file changed, 80 insertions(+), 65 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c index 59dc402dfe9..0e359c3b55e 100644 --- a/fs/hfsplus/ioctl.c +++ b/fs/hfsplus/ioctl.c @@ -21,78 +21,93 @@ #include #include "hfsplus_fs.h" -long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) { - struct inode *inode = filp->f_path.dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; + unsigned int flags = 0; + + if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE) + flags |= FS_IMMUTABLE_FL; + if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND) + flags |= FS_APPEND_FL; + if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP) + flags |= FS_NODUMP_FL; + + return put_user(flags, user_flags); +} + +static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) +{ + struct inode *inode = file->f_path.dentry->d_inode; unsigned int flags; + int err = 0; - switch (cmd) { - case HFSPLUS_IOC_EXT2_GETFLAGS: - flags = 0; - if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE) - flags |= FS_IMMUTABLE_FL; /* EXT2_IMMUTABLE_FL */ - if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND) - flags |= FS_APPEND_FL; /* EXT2_APPEND_FL */ - if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP) - flags |= FS_NODUMP_FL; /* EXT2_NODUMP_FL */ - return put_user(flags, (int __user *)arg); - case HFSPLUS_IOC_EXT2_SETFLAGS: { - int err = 0; - - lock_kernel(); - err = mnt_want_write(filp->f_path.mnt); - if (err) { - unlock_kernel(); - return err; - } + lock_kernel(); + err = mnt_want_write(file->f_path.mnt); + if (err) + goto out_unlock_kernel; - if (!is_owner_or_cap(inode)) { - err = -EACCES; - goto setflags_out; - } - if (get_user(flags, (int __user *)arg)) { - err = -EFAULT; - goto setflags_out; - } - if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) || - HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) { - if (!capable(CAP_LINUX_IMMUTABLE)) { - err = -EPERM; - goto setflags_out; - } - } + if (!is_owner_or_cap(inode)) { + err = -EACCES; + goto out_drop_write; + } - /* don't silently ignore unsupported ext2 flags */ - if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) { - err = -EOPNOTSUPP; - goto setflags_out; - } - if (flags & FS_IMMUTABLE_FL) { /* EXT2_IMMUTABLE_FL */ - inode->i_flags |= S_IMMUTABLE; - HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE; - } else { - inode->i_flags &= ~S_IMMUTABLE; - HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE; - } - if (flags & FS_APPEND_FL) { /* EXT2_APPEND_FL */ - inode->i_flags |= S_APPEND; - HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND; - } else { - inode->i_flags &= ~S_APPEND; - HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND; + if (get_user(flags, user_flags)) { + err = -EFAULT; + goto out_drop_write; + } + + if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) || + HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) { + if (!capable(CAP_LINUX_IMMUTABLE)) { + err = -EPERM; + goto out_drop_write; } - if (flags & FS_NODUMP_FL) /* EXT2_NODUMP_FL */ - HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP; - else - HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP; - - inode->i_ctime = CURRENT_TIME_SEC; - mark_inode_dirty(inode); -setflags_out: - mnt_drop_write(filp->f_path.mnt); - unlock_kernel(); - return err; } + + /* don't silently ignore unsupported ext2 flags */ + if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) { + err = -EOPNOTSUPP; + goto out_drop_write; + } + if (flags & FS_IMMUTABLE_FL) { + inode->i_flags |= S_IMMUTABLE; + HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE; + } else { + inode->i_flags &= ~S_IMMUTABLE; + HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE; + } + if (flags & FS_APPEND_FL) { + inode->i_flags |= S_APPEND; + HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND; + } else { + inode->i_flags &= ~S_APPEND; + HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND; + } + if (flags & FS_NODUMP_FL) + HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP; + else + HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP; + + inode->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + +out_drop_write: + mnt_drop_write(file->f_path.mnt); +out_unlock_kernel: + unlock_kernel(); + return err; +} + +long hfsplus_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + + switch (cmd) { + case HFSPLUS_IOC_EXT2_GETFLAGS: + return hfsplus_ioctl_getflags(file, argp); + case HFSPLUS_IOC_EXT2_SETFLAGS: + return hfsplus_ioctl_setflags(file, argp); default: return -ENOTTY; } -- cgit v1.2.3-18-g5258 From 6333816ade7e04a96ec0a34a8378c455e4f7c4dd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:41:35 +0200 Subject: hfsplus: protect setflags using i_mutex Use i_mutex for protecting against concurrent setflags ioctls like in other filesystems and get rid of the BKL in hfsplus_ioctl. Signed-off-by: Christoph Hellwig --- fs/hfsplus/ioctl.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c index 0e359c3b55e..906bd3dd314 100644 --- a/fs/hfsplus/ioctl.c +++ b/fs/hfsplus/ioctl.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include "hfsplus_fs.h" @@ -42,10 +41,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) unsigned int flags; int err = 0; - lock_kernel(); err = mnt_want_write(file->f_path.mnt); if (err) - goto out_unlock_kernel; + goto out; if (!is_owner_or_cap(inode)) { err = -EACCES; @@ -57,18 +55,20 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) goto out_drop_write; } + mutex_lock(&inode->i_mutex); + if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) || HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) { if (!capable(CAP_LINUX_IMMUTABLE)) { err = -EPERM; - goto out_drop_write; + goto out_unlock_inode; } } /* don't silently ignore unsupported ext2 flags */ if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) { err = -EOPNOTSUPP; - goto out_drop_write; + goto out_unlock_inode; } if (flags & FS_IMMUTABLE_FL) { inode->i_flags |= S_IMMUTABLE; @@ -92,10 +92,11 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); +out_unlock_inode: + mutex_lock(&inode->i_mutex); out_drop_write: mnt_drop_write(file->f_path.mnt); -out_unlock_kernel: - unlock_kernel(); +out: return err; } -- cgit v1.2.3-18-g5258 From 40bf48afe92fcea61e7e164f0b2599fba8b88124 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:41:39 +0200 Subject: hfsplus: introduce alloc_mutex Use a new per-sb alloc_mutex instead of abusing i_mutex of the alloc_file to protect block allocations. This gets rid of lockdep nesting warnings and prepares for extending the scope of alloc_mutex. Signed-off-by: Christoph Hellwig --- fs/hfsplus/bitmap.c | 8 ++++---- fs/hfsplus/hfsplus_fs.h | 3 +++ fs/hfsplus/super.c | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/bitmap.c b/fs/hfsplus/bitmap.c index ea30afc2a03..8a781769a90 100644 --- a/fs/hfsplus/bitmap.c +++ b/fs/hfsplus/bitmap.c @@ -29,7 +29,7 @@ int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, u32 *ma return size; dprint(DBG_BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len); - mutex_lock(&HFSPLUS_SB(sb).alloc_file->i_mutex); + mutex_lock(&HFSPLUS_SB(sb).alloc_mutex); mapping = HFSPLUS_SB(sb).alloc_file->i_mapping; page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, NULL); if (IS_ERR(page)) { @@ -154,7 +154,7 @@ done: sb->s_dirt = 1; dprint(DBG_BITMAP, "-> %u,%u\n", start, *max); out: - mutex_unlock(&HFSPLUS_SB(sb).alloc_file->i_mutex); + mutex_unlock(&HFSPLUS_SB(sb).alloc_mutex); return start; } @@ -175,7 +175,7 @@ int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count) if ((offset + count) > HFSPLUS_SB(sb).total_blocks) return -2; - mutex_lock(&HFSPLUS_SB(sb).alloc_file->i_mutex); + mutex_lock(&HFSPLUS_SB(sb).alloc_mutex); mapping = HFSPLUS_SB(sb).alloc_file->i_mapping; pnr = offset / PAGE_CACHE_BITS; page = read_mapping_page(mapping, pnr, NULL); @@ -226,7 +226,7 @@ out: kunmap(page); HFSPLUS_SB(sb).free_blocks += len; sb->s_dirt = 1; - mutex_unlock(&HFSPLUS_SB(sb).alloc_file->i_mutex); + mutex_unlock(&HFSPLUS_SB(sb).alloc_mutex); return 0; } diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index dc856be3c2b..df0a6312f0f 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -116,6 +116,9 @@ struct hfsplus_sb_info { struct inode *hidden_dir; struct nls_table *nls; + /* synchronize block allocations */ + struct mutex alloc_mutex; + /* Runtime variables */ u32 blockoffset; u32 sect_count; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 3b55c050c74..3dc62aa5872 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -321,6 +321,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) sb->s_fs_info = sbi; INIT_HLIST_HEAD(&sbi->rsrc_inodes); + mutex_init(&sbi->alloc_mutex); hfsplus_fill_defaults(sbi); if (!hfsplus_parse_options(data, sbi)) { printk(KERN_ERR "hfs: unable to parse mount options\n"); -- cgit v1.2.3-18-g5258 From a9fdbf8c6070d49c482e209df7ee93d9ec41ea27 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:41:50 +0200 Subject: hfsplus: use alloc_mutex in hfsplus_sync_fs Use alloc_mutex to protect hfsplus_sync_fs against itself and concurrent allocations, which allows to get rid of lock_super in hfsplus. Note that most fields in the superblock still aren't protected against concurrent allocations, that will follow later. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 3dc62aa5872..4936642deba 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -162,7 +162,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) dprint(DBG_SUPER, "hfsplus_write_super\n"); - lock_super(sb); + mutex_lock(&HFSPLUS_SB(sb).alloc_mutex); sb->s_dirt = 0; vhdr->free_blocks = cpu_to_be32(HFSPLUS_SB(sb).free_blocks); @@ -195,7 +195,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) } HFSPLUS_SB(sb).flags &= ~HFSPLUS_SB_WRITEBACKUP; } - unlock_super(sb); + mutex_unlock(&HFSPLUS_SB(sb).alloc_mutex); return 0; } -- cgit v1.2.3-18-g5258 From e753a62156e952fd5a3c64f98454d9aeee3a2546 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:41:53 +0200 Subject: hfsplus: remove BKL from hfsplus_put_super Except for ->put_super the BKL is now gone from HFS, which means it's superflous there too as ->put_super is serialized by the VFS. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 4936642deba..a1d3fd92040 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -213,8 +212,6 @@ static void hfsplus_put_super(struct super_block *sb) if (!sb->s_fs_info) return; - lock_kernel(); - if (sb->s_dirt) hfsplus_write_super(sb); if (!(sb->s_flags & MS_RDONLY) && HFSPLUS_SB(sb).s_vhdr) { @@ -235,8 +232,6 @@ static void hfsplus_put_super(struct super_block *sb) unload_nls(HFSPLUS_SB(sb).nls); kfree(sb->s_fs_info); sb->s_fs_info = NULL; - - unlock_kernel(); } static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf) -- cgit v1.2.3-18-g5258 From dd73a01a30d729e8fa6f829c4582650e258e36f9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:42:59 +0200 Subject: hfsplus: fix HFSPLUS_SB calling convention HFSPLUS_SB doesn't return a pointer to the hfsplus-specific superblock information like all other FOO_SB macros, but dereference the pointer in a way that made it look like a direct struct derefence. This only works as long as the HFSPLUS_SB macro is used directly and prevents us from keepig a local hfsplus_sb_info pointer. Fix the calling convention and introduce a local sbi variable in all functions that use it constantly. Signed-off-by: Christoph Hellwig --- fs/hfsplus/bitmap.c | 20 ++++--- fs/hfsplus/btree.c | 8 +-- fs/hfsplus/catalog.c | 25 ++++---- fs/hfsplus/dir.c | 40 +++++++------ fs/hfsplus/extents.c | 37 ++++++------ fs/hfsplus/hfsplus_fs.h | 3 +- fs/hfsplus/inode.c | 57 +++++++++--------- fs/hfsplus/ioctl.c | 4 +- fs/hfsplus/options.c | 2 +- fs/hfsplus/part_tbl.c | 5 +- fs/hfsplus/super.c | 151 +++++++++++++++++++++++++----------------------- fs/hfsplus/unicode.c | 16 ++--- fs/hfsplus/wrapper.c | 30 +++++----- 13 files changed, 209 insertions(+), 189 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/bitmap.c b/fs/hfsplus/bitmap.c index 8a781769a90..ad57f5991eb 100644 --- a/fs/hfsplus/bitmap.c +++ b/fs/hfsplus/bitmap.c @@ -17,6 +17,7 @@ int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, u32 *max) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); struct page *page; struct address_space *mapping; __be32 *pptr, *curr, *end; @@ -29,8 +30,8 @@ int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, u32 *ma return size; dprint(DBG_BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len); - mutex_lock(&HFSPLUS_SB(sb).alloc_mutex); - mapping = HFSPLUS_SB(sb).alloc_file->i_mapping; + mutex_lock(&sbi->alloc_mutex); + mapping = sbi->alloc_file->i_mapping; page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, NULL); if (IS_ERR(page)) { start = size; @@ -150,16 +151,17 @@ done: set_page_dirty(page); kunmap(page); *max = offset + (curr - pptr) * 32 + i - start; - HFSPLUS_SB(sb).free_blocks -= *max; + sbi->free_blocks -= *max; sb->s_dirt = 1; dprint(DBG_BITMAP, "-> %u,%u\n", start, *max); out: - mutex_unlock(&HFSPLUS_SB(sb).alloc_mutex); + mutex_unlock(&sbi->alloc_mutex); return start; } int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); struct page *page; struct address_space *mapping; __be32 *pptr, *curr, *end; @@ -172,11 +174,11 @@ int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count) dprint(DBG_BITMAP, "block_free: %u,%u\n", offset, count); /* are all of the bits in range? */ - if ((offset + count) > HFSPLUS_SB(sb).total_blocks) + if ((offset + count) > sbi->total_blocks) return -2; - mutex_lock(&HFSPLUS_SB(sb).alloc_mutex); - mapping = HFSPLUS_SB(sb).alloc_file->i_mapping; + mutex_lock(&sbi->alloc_mutex); + mapping = sbi->alloc_file->i_mapping; pnr = offset / PAGE_CACHE_BITS; page = read_mapping_page(mapping, pnr, NULL); pptr = kmap(page); @@ -224,9 +226,9 @@ done: out: set_page_dirty(page); kunmap(page); - HFSPLUS_SB(sb).free_blocks += len; + sbi->free_blocks += len; sb->s_dirt = 1; - mutex_unlock(&HFSPLUS_SB(sb).alloc_mutex); + mutex_unlock(&sbi->alloc_mutex); return 0; } diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index e49fcee1e29..96e2128748f 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -61,12 +61,12 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) if (id == HFSPLUS_EXT_CNID) { tree->keycmp = hfsplus_ext_cmp_key; } else if (id == HFSPLUS_CAT_CNID) { - if ((HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX) && + if ((HFSPLUS_SB(sb)->flags & HFSPLUS_SB_HFSX) && (head->key_type == HFSPLUS_KEY_BINARY)) tree->keycmp = hfsplus_cat_bin_cmp_key; else { tree->keycmp = hfsplus_cat_case_cmp_key; - HFSPLUS_SB(sb).flags |= HFSPLUS_SB_CASEFOLD; + HFSPLUS_SB(sb)->flags |= HFSPLUS_SB_CASEFOLD; } } else { printk(KERN_ERR "hfs: unknown B*Tree requested\n"); @@ -200,9 +200,9 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) return ERR_PTR(res); HFSPLUS_I(inode).phys_size = inode->i_size = (loff_t)HFSPLUS_I(inode).alloc_blocks << - HFSPLUS_SB(tree->sb).alloc_blksz_shift; + HFSPLUS_SB(tree->sb)->alloc_blksz_shift; HFSPLUS_I(inode).fs_blocks = HFSPLUS_I(inode).alloc_blocks << - HFSPLUS_SB(tree->sb).fs_shift; + HFSPLUS_SB(tree->sb)->fs_shift; inode_set_bytes(inode, inode->i_size); count = inode->i_size >> tree->node_size_shift; tree->free_nodes = count - tree->node_count; diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index f6874acb2cf..75ac1e466f1 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -86,6 +86,8 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct inode *inode) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); + if (S_ISDIR(inode->i_mode)) { struct hfsplus_cat_folder *folder; @@ -99,7 +101,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i folder->attribute_mod_date = folder->access_date = hfsp_now2mt(); hfsplus_set_perms(inode, &folder->permissions); - if (inode == HFSPLUS_SB(inode->i_sb).hidden_dir) + if (inode == sbi->hidden_dir) /* invisible and namelocked */ folder->user_info.frFlags = cpu_to_be16(0x5000); return sizeof(*folder); @@ -122,8 +124,8 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i file->user_info.fdType = cpu_to_be32(HFSP_SYMLINK_TYPE); file->user_info.fdCreator = cpu_to_be32(HFSP_SYMLINK_CREATOR); } else { - file->user_info.fdType = cpu_to_be32(HFSPLUS_SB(inode->i_sb).type); - file->user_info.fdCreator = cpu_to_be32(HFSPLUS_SB(inode->i_sb).creator); + file->user_info.fdType = cpu_to_be32(sbi->type); + file->user_info.fdCreator = cpu_to_be32(sbi->creator); } if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE) file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED); @@ -131,7 +133,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i file->user_info.fdType = cpu_to_be32(HFSP_HARDLINK_TYPE); file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR); file->user_info.fdFlags = cpu_to_be16(0x100); - file->create_date = HFSPLUS_I(HFSPLUS_SB(inode->i_sb).hidden_dir).create_date; + file->create_date = HFSPLUS_I(sbi->hidden_dir).create_date; file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode).dev); } return sizeof(*file); @@ -180,15 +182,14 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid, int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct inode *inode) { + struct super_block *sb = dir->i_sb; struct hfs_find_data fd; - struct super_block *sb; hfsplus_cat_entry entry; int entry_size; int err; dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n", str->name, cnid, inode->i_nlink); - sb = dir->i_sb; - hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); entry_size = hfsplus_fill_cat_thread(sb, &entry, S_ISDIR(inode->i_mode) ? @@ -234,7 +235,7 @@ err2: int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) { - struct super_block *sb; + struct super_block *sb = dir->i_sb; struct hfs_find_data fd; struct hfsplus_fork_raw fork; struct list_head *pos; @@ -242,8 +243,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) u16 type; dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid); - sb = dir->i_sb; - hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); if (!str) { int len; @@ -312,7 +312,7 @@ int hfsplus_rename_cat(u32 cnid, struct inode *src_dir, struct qstr *src_name, struct inode *dst_dir, struct qstr *dst_name) { - struct super_block *sb; + struct super_block *sb = src_dir->i_sb; struct hfs_find_data src_fd, dst_fd; hfsplus_cat_entry entry; int entry_size, type; @@ -320,8 +320,7 @@ int hfsplus_rename_cat(u32 cnid, dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid, src_dir->i_ino, src_name->name, dst_dir->i_ino, dst_name->name); - sb = src_dir->i_sb; - hfs_find_init(HFSPLUS_SB(sb).cat_tree, &src_fd); + hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd); dst_fd = src_fd; /* find the old dir entry and read the data */ diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 764fd1bdca8..584777ddb0b 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -39,7 +39,7 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry, dentry->d_op = &hfsplus_dentry_operations; dentry->d_fsdata = NULL; - hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name); again: err = hfs_brec_read(&fd, &entry, sizeof(entry)); @@ -68,9 +68,9 @@ again: cnid = be32_to_cpu(entry.file.id); if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) && entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR) && - (entry.file.create_date == HFSPLUS_I(HFSPLUS_SB(sb).hidden_dir).create_date || + (entry.file.create_date == HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir).create_date || entry.file.create_date == HFSPLUS_I(sb->s_root->d_inode).create_date) && - HFSPLUS_SB(sb).hidden_dir) { + HFSPLUS_SB(sb)->hidden_dir) { struct qstr str; char name[32]; @@ -86,7 +86,8 @@ again: linkid = be32_to_cpu(entry.file.permissions.dev); str.len = sprintf(name, "iNode%d", linkid); str.name = name; - hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str); + hfsplus_cat_build_key(sb, fd.search_key, + HFSPLUS_SB(sb)->hidden_dir->i_ino, &str); goto again; } } else if (!dentry->d_fsdata) @@ -124,7 +125,7 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) if (filp->f_pos >= inode->i_size) return 0; - hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); err = hfs_brec_find(&fd); if (err) @@ -180,8 +181,9 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) err = -EIO; goto out; } - if (HFSPLUS_SB(sb).hidden_dir && - HFSPLUS_SB(sb).hidden_dir->i_ino == be32_to_cpu(entry.folder.id)) + if (HFSPLUS_SB(sb)->hidden_dir && + HFSPLUS_SB(sb)->hidden_dir->i_ino == + be32_to_cpu(entry.folder.id)) goto next; if (filldir(dirent, strbuf, len, filp->f_pos, be32_to_cpu(entry.folder.id), DT_DIR)) @@ -260,7 +262,7 @@ static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode, static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry) { - struct super_block *sb = dst_dir->i_sb; + struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb); struct inode *inode = src_dentry->d_inode; struct inode *src_dir = src_dentry->d_parent->d_inode; struct qstr str; @@ -279,22 +281,22 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, str.len = sprintf(name, "iNode%d", id); res = hfsplus_rename_cat(inode->i_ino, src_dir, &src_dentry->d_name, - HFSPLUS_SB(sb).hidden_dir, &str); + sbi->hidden_dir, &str); if (!res) break; if (res != -EEXIST) return res; } HFSPLUS_I(inode).dev = id; - cnid = HFSPLUS_SB(sb).next_cnid++; + cnid = sbi->next_cnid++; src_dentry->d_fsdata = (void *)(unsigned long)cnid; res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); if (res) /* panic? */ return res; - HFSPLUS_SB(sb).file_count++; + sbi->file_count++; } - cnid = HFSPLUS_SB(sb).next_cnid++; + cnid = sbi->next_cnid++; res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); if (res) return res; @@ -304,15 +306,15 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, atomic_inc(&inode->i_count); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); - HFSPLUS_SB(sb).file_count++; - sb->s_dirt = 1; + sbi->file_count++; + dst_dir->i_sb->s_dirt = 1; return 0; } static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) { - struct super_block *sb = dir->i_sb; + struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); struct inode *inode = dentry->d_inode; struct qstr str; char name[32]; @@ -329,7 +331,7 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) str.len = sprintf(name, "temp%lu", inode->i_ino); res = hfsplus_rename_cat(inode->i_ino, dir, &dentry->d_name, - HFSPLUS_SB(sb).hidden_dir, &str); + sbi->hidden_dir, &str); if (!res) inode->i_flags |= S_DEAD; return res; @@ -344,10 +346,10 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) clear_nlink(inode); if (!inode->i_nlink) { if (inode->i_ino != cnid) { - HFSPLUS_SB(sb).file_count--; + sbi->file_count--; if (!atomic_read(&HFSPLUS_I(inode).opencnt)) { res = hfsplus_delete_cat(inode->i_ino, - HFSPLUS_SB(sb).hidden_dir, + sbi->hidden_dir, NULL); if (!res) hfsplus_delete_inode(inode); @@ -356,7 +358,7 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) } else hfsplus_delete_inode(inode); } else - HFSPLUS_SB(sb).file_count--; + sbi->file_count--; inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c index 0022eec63cd..18196981434 100644 --- a/fs/hfsplus/extents.c +++ b/fs/hfsplus/extents.c @@ -108,7 +108,7 @@ void hfsplus_ext_write_extent(struct inode *inode) if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_DIRTY) { struct hfs_find_data fd; - hfs_find_init(HFSPLUS_SB(inode->i_sb).ext_tree, &fd); + hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); __hfsplus_ext_write_extent(inode, &fd); hfs_find_exit(&fd); } @@ -162,7 +162,7 @@ static int hfsplus_ext_read_extent(struct inode *inode, u32 block) block < HFSPLUS_I(inode).cached_start + HFSPLUS_I(inode).cached_blocks) return 0; - hfs_find_init(HFSPLUS_SB(inode->i_sb).ext_tree, &fd); + hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); res = __hfsplus_ext_cache_extent(&fd, inode, block); hfs_find_exit(&fd); return res; @@ -172,16 +172,15 @@ static int hfsplus_ext_read_extent(struct inode *inode, u32 block) int hfsplus_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { - struct super_block *sb; + struct super_block *sb = inode->i_sb; + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); int res = -EIO; u32 ablock, dblock, mask; int shift; - sb = inode->i_sb; - /* Convert inode block to disk allocation block */ - shift = HFSPLUS_SB(sb).alloc_blksz_shift - sb->s_blocksize_bits; - ablock = iblock >> HFSPLUS_SB(sb).fs_shift; + shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; + ablock = iblock >> sbi->fs_shift; if (iblock >= HFSPLUS_I(inode).fs_blocks) { if (iblock > HFSPLUS_I(inode).fs_blocks || !create) @@ -215,8 +214,8 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock, done: dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n", inode->i_ino, (long long)iblock, dblock); - mask = (1 << HFSPLUS_SB(sb).fs_shift) - 1; - map_bh(bh_result, sb, (dblock << HFSPLUS_SB(sb).fs_shift) + HFSPLUS_SB(sb).blockoffset + (iblock & mask)); + mask = (1 << sbi->fs_shift) - 1; + map_bh(bh_result, sb, (dblock << sbi->fs_shift) + sbi->blockoffset + (iblock & mask)); if (create) { set_buffer_new(bh_result); HFSPLUS_I(inode).phys_size += sb->s_blocksize; @@ -327,7 +326,7 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid, struct hfsplus_fork_raw if (total_blocks == blocks) return 0; - hfs_find_init(HFSPLUS_SB(sb).ext_tree, &fd); + hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); do { res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid, total_blocks, type); @@ -348,13 +347,16 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid, struct hfsplus_fork_raw int hfsplus_file_extend(struct inode *inode) { struct super_block *sb = inode->i_sb; + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); u32 start, len, goal; int res; - if (HFSPLUS_SB(sb).alloc_file->i_size * 8 < HFSPLUS_SB(sb).total_blocks - HFSPLUS_SB(sb).free_blocks + 8) { + if (sbi->alloc_file->i_size * 8 < + sbi->total_blocks - sbi->free_blocks + 8) { // extend alloc file - printk(KERN_ERR "hfs: extend alloc file! (%Lu,%u,%u)\n", HFSPLUS_SB(sb).alloc_file->i_size * 8, - HFSPLUS_SB(sb).total_blocks, HFSPLUS_SB(sb).free_blocks); + printk(KERN_ERR "hfs: extend alloc file! (%Lu,%u,%u)\n", + sbi->alloc_file->i_size * 8, + sbi->total_blocks, sbi->free_blocks); return -ENOSPC; } @@ -369,8 +371,8 @@ int hfsplus_file_extend(struct inode *inode) } len = HFSPLUS_I(inode).clump_blocks; - start = hfsplus_block_allocate(sb, HFSPLUS_SB(sb).total_blocks, goal, &len); - if (start >= HFSPLUS_SB(sb).total_blocks) { + start = hfsplus_block_allocate(sb, sbi->total_blocks, goal, &len); + if (start >= sbi->total_blocks) { start = hfsplus_block_allocate(sb, goal, 0, &len); if (start >= goal) { res = -ENOSPC; @@ -463,13 +465,14 @@ void hfsplus_file_truncate(struct inode *inode) } else if (inode->i_size == HFSPLUS_I(inode).phys_size) return; - blk_cnt = (inode->i_size + HFSPLUS_SB(sb).alloc_blksz - 1) >> HFSPLUS_SB(sb).alloc_blksz_shift; + blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >> + HFSPLUS_SB(sb)->alloc_blksz_shift; alloc_cnt = HFSPLUS_I(inode).alloc_blocks; if (blk_cnt == alloc_cnt) goto out; mutex_lock(&HFSPLUS_I(inode).extents_lock); - hfs_find_init(HFSPLUS_SB(sb).ext_tree, &fd); + hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); while (1) { if (alloc_cnt == HFSPLUS_I(inode).first_blocks) { hfsplus_free_extents(sb, HFSPLUS_I(inode).first_extents, diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index df0a6312f0f..55f42b49f0f 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -375,17 +375,16 @@ int hfsplus_read_wrapper(struct super_block *); int hfs_part_find(struct super_block *, sector_t *, sector_t *); /* access macros */ -/* static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb) { return sb->s_fs_info; } +/* static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode) { return list_entry(inode, struct hfsplus_inode_info, vfs_inode); } */ -#define HFSPLUS_SB(super) (*(struct hfsplus_sb_info *)(super)->s_fs_info) #define HFSPLUS_I(inode) (*list_entry(inode, struct hfsplus_inode_info, vfs_inode)) #if 1 diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index c5a979d62c6..050eee6c81b 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -62,13 +62,13 @@ static int hfsplus_releasepage(struct page *page, gfp_t mask) switch (inode->i_ino) { case HFSPLUS_EXT_CNID: - tree = HFSPLUS_SB(sb).ext_tree; + tree = HFSPLUS_SB(sb)->ext_tree; break; case HFSPLUS_CAT_CNID: - tree = HFSPLUS_SB(sb).cat_tree; + tree = HFSPLUS_SB(sb)->cat_tree; break; case HFSPLUS_ATTR_CNID: - tree = HFSPLUS_SB(sb).attr_tree; + tree = HFSPLUS_SB(sb)->attr_tree; break; default: BUG(); @@ -190,7 +190,7 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dent mutex_init(&HFSPLUS_I(inode).extents_lock); HFSPLUS_I(inode).flags = HFSPLUS_FLG_RSRC; - hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); err = hfsplus_find_cat(sb, dir->i_ino, &fd); if (!err) err = hfsplus_cat_read_inode(inode, &fd); @@ -202,7 +202,7 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dent HFSPLUS_I(inode).rsrc_inode = dir; HFSPLUS_I(dir).rsrc_inode = inode; igrab(dir); - hlist_add_head(&inode->i_hash, &HFSPLUS_SB(sb).rsrc_inodes); + hlist_add_head(&inode->i_hash, &HFSPLUS_SB(sb)->rsrc_inodes); mark_inode_dirty(inode); out: d_add(dentry, inode); @@ -211,26 +211,24 @@ out: static void hfsplus_get_perms(struct inode *inode, struct hfsplus_perm *perms, int dir) { - struct super_block *sb = inode->i_sb; + struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); u16 mode; mode = be16_to_cpu(perms->mode); inode->i_uid = be32_to_cpu(perms->owner); if (!inode->i_uid && !mode) - inode->i_uid = HFSPLUS_SB(sb).uid; + inode->i_uid = sbi->uid; inode->i_gid = be32_to_cpu(perms->group); if (!inode->i_gid && !mode) - inode->i_gid = HFSPLUS_SB(sb).gid; + inode->i_gid = sbi->gid; if (dir) { - mode = mode ? (mode & S_IALLUGO) : - (S_IRWXUGO & ~(HFSPLUS_SB(sb).umask)); + mode = mode ? (mode & S_IALLUGO) : (S_IRWXUGO & ~(sbi->umask)); mode |= S_IFDIR; } else if (!mode) - mode = S_IFREG | ((S_IRUGO|S_IWUGO) & - ~(HFSPLUS_SB(sb).umask)); + mode = S_IFREG | ((S_IRUGO|S_IWUGO) & ~(sbi->umask)); inode->i_mode = mode; HFSPLUS_I(inode).rootflags = perms->rootflags; @@ -282,7 +280,8 @@ static int hfsplus_file_release(struct inode *inode, struct file *file) mutex_lock(&inode->i_mutex); hfsplus_file_truncate(inode); if (inode->i_flags & S_DEAD) { - hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL); + hfsplus_delete_cat(inode->i_ino, + HFSPLUS_SB(sb)->hidden_dir, NULL); hfsplus_delete_inode(inode); } mutex_unlock(&inode->i_mutex); @@ -361,11 +360,13 @@ static const struct file_operations hfsplus_file_operations = { struct inode *hfsplus_new_inode(struct super_block *sb, int mode) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); struct inode *inode = new_inode(sb); + if (!inode) return NULL; - inode->i_ino = HFSPLUS_SB(sb).next_cnid++; + inode->i_ino = sbi->next_cnid++; inode->i_mode = mode; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); @@ -386,22 +387,22 @@ struct inode *hfsplus_new_inode(struct super_block *sb, int mode) HFSPLUS_I(inode).rsrc_inode = NULL; if (S_ISDIR(inode->i_mode)) { inode->i_size = 2; - HFSPLUS_SB(sb).folder_count++; + sbi->folder_count++; inode->i_op = &hfsplus_dir_inode_operations; inode->i_fop = &hfsplus_dir_operations; } else if (S_ISREG(inode->i_mode)) { - HFSPLUS_SB(sb).file_count++; + sbi->file_count++; inode->i_op = &hfsplus_file_inode_operations; inode->i_fop = &hfsplus_file_operations; inode->i_mapping->a_ops = &hfsplus_aops; - HFSPLUS_I(inode).clump_blocks = HFSPLUS_SB(sb).data_clump_blocks; + HFSPLUS_I(inode).clump_blocks = sbi->data_clump_blocks; } else if (S_ISLNK(inode->i_mode)) { - HFSPLUS_SB(sb).file_count++; + sbi->file_count++; inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &hfsplus_aops; HFSPLUS_I(inode).clump_blocks = 1; } else - HFSPLUS_SB(sb).file_count++; + sbi->file_count++; insert_inode_hash(inode); mark_inode_dirty(inode); sb->s_dirt = 1; @@ -414,11 +415,11 @@ void hfsplus_delete_inode(struct inode *inode) struct super_block *sb = inode->i_sb; if (S_ISDIR(inode->i_mode)) { - HFSPLUS_SB(sb).folder_count--; + HFSPLUS_SB(sb)->folder_count--; sb->s_dirt = 1; return; } - HFSPLUS_SB(sb).file_count--; + HFSPLUS_SB(sb)->file_count--; if (S_ISREG(inode->i_mode)) { if (!inode->i_nlink) { inode->i_size = 0; @@ -434,6 +435,7 @@ void hfsplus_delete_inode(struct inode *inode) void hfsplus_inode_read_fork(struct inode *inode, struct hfsplus_fork_raw *fork) { struct super_block *sb = inode->i_sb; + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); u32 count; int i; @@ -450,10 +452,13 @@ void hfsplus_inode_read_fork(struct inode *inode, struct hfsplus_fork_raw *fork) inode->i_size = HFSPLUS_I(inode).phys_size = be64_to_cpu(fork->total_size); HFSPLUS_I(inode).fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; inode_set_bytes(inode, HFSPLUS_I(inode).fs_blocks << sb->s_blocksize_bits); - HFSPLUS_I(inode).clump_blocks = be32_to_cpu(fork->clump_size) >> HFSPLUS_SB(sb).alloc_blksz_shift; - if (!HFSPLUS_I(inode).clump_blocks) - HFSPLUS_I(inode).clump_blocks = HFSPLUS_IS_RSRC(inode) ? HFSPLUS_SB(sb).rsrc_clump_blocks : - HFSPLUS_SB(sb).data_clump_blocks; + HFSPLUS_I(inode).clump_blocks = + be32_to_cpu(fork->clump_size) >> sbi->alloc_blksz_shift; + if (!HFSPLUS_I(inode).clump_blocks) { + HFSPLUS_I(inode).clump_blocks = HFSPLUS_IS_RSRC(inode) ? + sbi->rsrc_clump_blocks : + sbi->data_clump_blocks; + } } void hfsplus_inode_write_fork(struct inode *inode, struct hfsplus_fork_raw *fork) @@ -538,7 +543,7 @@ int hfsplus_cat_write_inode(struct inode *inode) if (!main_inode->i_nlink) return 0; - if (hfs_find_init(HFSPLUS_SB(main_inode->i_sb).cat_tree, &fd)) + if (hfs_find_init(HFSPLUS_SB(main_inode->i_sb)->cat_tree, &fd)) /* panic? */ return -EIO; diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c index 906bd3dd314..66dd64b88b2 100644 --- a/fs/hfsplus/ioctl.c +++ b/fs/hfsplus/ioctl.c @@ -126,7 +126,7 @@ int hfsplus_setxattr(struct dentry *dentry, const char *name, if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode)) return -EOPNOTSUPP; - res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd); + res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); if (res) return res; res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); @@ -169,7 +169,7 @@ ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name, return -EOPNOTSUPP; if (size) { - res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd); + res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); if (res) return res; res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c index 572628b4b07..f2598aef206 100644 --- a/fs/hfsplus/options.c +++ b/fs/hfsplus/options.c @@ -171,7 +171,7 @@ done: int hfsplus_show_options(struct seq_file *seq, struct vfsmount *mnt) { - struct hfsplus_sb_info *sbi = &HFSPLUS_SB(mnt->mnt_sb); + struct hfsplus_sb_info *sbi = HFSPLUS_SB(mnt->mnt_sb); if (sbi->creator != HFSPLUS_DEF_CR_TYPE) seq_printf(seq, ",creator=%.4s", (char *)&sbi->creator); diff --git a/fs/hfsplus/part_tbl.c b/fs/hfsplus/part_tbl.c index 1528a6fd029..208b16c645c 100644 --- a/fs/hfsplus/part_tbl.c +++ b/fs/hfsplus/part_tbl.c @@ -74,6 +74,7 @@ struct old_pmap { int hfs_part_find(struct super_block *sb, sector_t *part_start, sector_t *part_size) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); struct buffer_head *bh; __be16 *data; int i, size, res; @@ -95,7 +96,7 @@ int hfs_part_find(struct super_block *sb, for (i = 0; i < size; p++, i++) { if (p->pdStart && p->pdSize && p->pdFSID == cpu_to_be32(0x54465331)/*"TFS1"*/ && - (HFSPLUS_SB(sb).part < 0 || HFSPLUS_SB(sb).part == i)) { + (sbi->part < 0 || sbi->part == i)) { *part_start += be32_to_cpu(p->pdStart); *part_size = be32_to_cpu(p->pdSize); res = 0; @@ -111,7 +112,7 @@ int hfs_part_find(struct super_block *sb, size = be32_to_cpu(pm->pmMapBlkCnt); for (i = 0; i < size;) { if (!memcmp(pm->pmPartType,"Apple_HFS", 9) && - (HFSPLUS_SB(sb).part < 0 || HFSPLUS_SB(sb).part == i)) { + (sbi->part < 0 || sbi->part == i)) { *part_start += be32_to_cpu(pm->pmPyPartStart); *part_size = be32_to_cpu(pm->pmPartBlkCnt); res = 0; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index a1d3fd92040..1bf00b9ecc1 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -41,7 +41,7 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) { read_inode: - hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd); + hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); if (!err) err = hfsplus_cat_read_inode(inode, &fd); @@ -50,7 +50,7 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) goto bad_inode; goto done; } - vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr; + vhdr = HFSPLUS_SB(inode->i_sb)->s_vhdr; switch(inode->i_ino) { case HFSPLUS_ROOT_CNID: goto read_inode; @@ -89,6 +89,7 @@ bad_inode: static int hfsplus_write_inode(struct inode *inode, struct writeback_control *wbc) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); struct hfsplus_vh *vhdr; int ret = 0; @@ -97,48 +98,48 @@ static int hfsplus_write_inode(struct inode *inode, if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) { return hfsplus_cat_write_inode(inode); } - vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr; + vhdr = sbi->s_vhdr; switch (inode->i_ino) { case HFSPLUS_ROOT_CNID: ret = hfsplus_cat_write_inode(inode); break; case HFSPLUS_EXT_CNID: if (vhdr->ext_file.total_size != cpu_to_be64(inode->i_size)) { - HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + sbi->flags |= HFSPLUS_SB_WRITEBACKUP; inode->i_sb->s_dirt = 1; } hfsplus_inode_write_fork(inode, &vhdr->ext_file); - hfs_btree_write(HFSPLUS_SB(inode->i_sb).ext_tree); + hfs_btree_write(sbi->ext_tree); break; case HFSPLUS_CAT_CNID: if (vhdr->cat_file.total_size != cpu_to_be64(inode->i_size)) { - HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + sbi->flags |= HFSPLUS_SB_WRITEBACKUP; inode->i_sb->s_dirt = 1; } hfsplus_inode_write_fork(inode, &vhdr->cat_file); - hfs_btree_write(HFSPLUS_SB(inode->i_sb).cat_tree); + hfs_btree_write(sbi->cat_tree); break; case HFSPLUS_ALLOC_CNID: if (vhdr->alloc_file.total_size != cpu_to_be64(inode->i_size)) { - HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + sbi->flags |= HFSPLUS_SB_WRITEBACKUP; inode->i_sb->s_dirt = 1; } hfsplus_inode_write_fork(inode, &vhdr->alloc_file); break; case HFSPLUS_START_CNID: if (vhdr->start_file.total_size != cpu_to_be64(inode->i_size)) { - HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + sbi->flags |= HFSPLUS_SB_WRITEBACKUP; inode->i_sb->s_dirt = 1; } hfsplus_inode_write_fork(inode, &vhdr->start_file); break; case HFSPLUS_ATTR_CNID: if (vhdr->attr_file.total_size != cpu_to_be64(inode->i_size)) { - HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP; + sbi->flags |= HFSPLUS_SB_WRITEBACKUP; inode->i_sb->s_dirt = 1; } hfsplus_inode_write_fork(inode, &vhdr->attr_file); - hfs_btree_write(HFSPLUS_SB(inode->i_sb).attr_tree); + hfs_btree_write(sbi->attr_tree); break; } return ret; @@ -157,44 +158,46 @@ static void hfsplus_evict_inode(struct inode *inode) int hfsplus_sync_fs(struct super_block *sb, int wait) { - struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr; + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); + struct hfsplus_vh *vhdr = sbi->s_vhdr; dprint(DBG_SUPER, "hfsplus_write_super\n"); - mutex_lock(&HFSPLUS_SB(sb).alloc_mutex); + mutex_lock(&sbi->alloc_mutex); sb->s_dirt = 0; - vhdr->free_blocks = cpu_to_be32(HFSPLUS_SB(sb).free_blocks); - vhdr->next_alloc = cpu_to_be32(HFSPLUS_SB(sb).next_alloc); - vhdr->next_cnid = cpu_to_be32(HFSPLUS_SB(sb).next_cnid); - vhdr->folder_count = cpu_to_be32(HFSPLUS_SB(sb).folder_count); - vhdr->file_count = cpu_to_be32(HFSPLUS_SB(sb).file_count); + vhdr->free_blocks = cpu_to_be32(sbi->free_blocks); + vhdr->next_alloc = cpu_to_be32(sbi->next_alloc); + vhdr->next_cnid = cpu_to_be32(sbi->next_cnid); + vhdr->folder_count = cpu_to_be32(sbi->folder_count); + vhdr->file_count = cpu_to_be32(sbi->file_count); - mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh); - if (HFSPLUS_SB(sb).flags & HFSPLUS_SB_WRITEBACKUP) { - if (HFSPLUS_SB(sb).sect_count) { + mark_buffer_dirty(sbi->s_vhbh); + if (sbi->flags & HFSPLUS_SB_WRITEBACKUP) { + if (sbi->sect_count) { struct buffer_head *bh; u32 block, offset; - block = HFSPLUS_SB(sb).blockoffset; - block += (HFSPLUS_SB(sb).sect_count - 2) >> (sb->s_blocksize_bits - 9); - offset = ((HFSPLUS_SB(sb).sect_count - 2) << 9) & (sb->s_blocksize - 1); - printk(KERN_DEBUG "hfs: backup: %u,%u,%u,%u\n", HFSPLUS_SB(sb).blockoffset, - HFSPLUS_SB(sb).sect_count, block, offset); + block = sbi->blockoffset; + block += (sbi->sect_count - 2) >> (sb->s_blocksize_bits - 9); + offset = ((sbi->sect_count - 2) << 9) & (sb->s_blocksize - 1); + printk(KERN_DEBUG "hfs: backup: %u,%u,%u,%u\n", + sbi->blockoffset, sbi->sect_count, + block, offset); bh = sb_bread(sb, block); if (bh) { vhdr = (struct hfsplus_vh *)(bh->b_data + offset); if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) { - memcpy(vhdr, HFSPLUS_SB(sb).s_vhdr, sizeof(*vhdr)); + memcpy(vhdr, sbi->s_vhdr, sizeof(*vhdr)); mark_buffer_dirty(bh); brelse(bh); } else printk(KERN_WARNING "hfs: backup not found!\n"); } } - HFSPLUS_SB(sb).flags &= ~HFSPLUS_SB_WRITEBACKUP; + sbi->flags &= ~HFSPLUS_SB_WRITEBACKUP; } - mutex_unlock(&HFSPLUS_SB(sb).alloc_mutex); + mutex_unlock(&sbi->alloc_mutex); return 0; } @@ -208,28 +211,31 @@ static void hfsplus_write_super(struct super_block *sb) static void hfsplus_put_super(struct super_block *sb) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); + dprint(DBG_SUPER, "hfsplus_put_super\n"); + if (!sb->s_fs_info) return; if (sb->s_dirt) hfsplus_write_super(sb); - if (!(sb->s_flags & MS_RDONLY) && HFSPLUS_SB(sb).s_vhdr) { - struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr; + if (!(sb->s_flags & MS_RDONLY) && sbi->s_vhdr) { + struct hfsplus_vh *vhdr = sbi->s_vhdr; vhdr->modify_date = hfsp_now2mt(); vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT); vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT); - mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh); - sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh); + mark_buffer_dirty(sbi->s_vhbh); + sync_dirty_buffer(sbi->s_vhbh); } - hfs_btree_close(HFSPLUS_SB(sb).cat_tree); - hfs_btree_close(HFSPLUS_SB(sb).ext_tree); - iput(HFSPLUS_SB(sb).alloc_file); - iput(HFSPLUS_SB(sb).hidden_dir); - brelse(HFSPLUS_SB(sb).s_vhbh); - unload_nls(HFSPLUS_SB(sb).nls); + hfs_btree_close(sbi->cat_tree); + hfs_btree_close(sbi->ext_tree); + iput(sbi->alloc_file); + iput(sbi->hidden_dir); + brelse(sbi->s_vhbh); + unload_nls(sbi->nls); kfree(sb->s_fs_info); sb->s_fs_info = NULL; } @@ -237,15 +243,16 @@ static void hfsplus_put_super(struct super_block *sb) static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); u64 id = huge_encode_dev(sb->s_bdev->bd_dev); buf->f_type = HFSPLUS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; - buf->f_blocks = HFSPLUS_SB(sb).total_blocks << HFSPLUS_SB(sb).fs_shift; - buf->f_bfree = HFSPLUS_SB(sb).free_blocks << HFSPLUS_SB(sb).fs_shift; + buf->f_blocks = sbi->total_blocks << sbi->fs_shift; + buf->f_bfree = sbi->free_blocks << sbi->fs_shift; buf->f_bavail = buf->f_bfree; buf->f_files = 0xFFFFFFFF; - buf->f_ffree = 0xFFFFFFFF - HFSPLUS_SB(sb).next_cnid; + buf->f_ffree = 0xFFFFFFFF - sbi->next_cnid; buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); buf->f_namelen = HFSPLUS_MAX_STRLEN; @@ -258,11 +265,11 @@ static int hfsplus_remount(struct super_block *sb, int *flags, char *data) if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (!(*flags & MS_RDONLY)) { - struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr; + struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr; struct hfsplus_sb_info sbi; memset(&sbi, 0, sizeof(struct hfsplus_sb_info)); - sbi.nls = HFSPLUS_SB(sb).nls; + sbi.nls = HFSPLUS_SB(sb)->nls; if (!hfsplus_parse_options(data, &sbi)) return -EINVAL; @@ -340,7 +347,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) err = -EINVAL; goto cleanup; } - vhdr = HFSPLUS_SB(sb).s_vhdr; + vhdr = sbi->s_vhdr; /* Copy parts of the volume header into the superblock */ sb->s_magic = HFSPLUS_VOLHEAD_SIG; @@ -349,18 +356,20 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) printk(KERN_ERR "hfs: wrong filesystem version\n"); goto cleanup; } - HFSPLUS_SB(sb).total_blocks = be32_to_cpu(vhdr->total_blocks); - HFSPLUS_SB(sb).free_blocks = be32_to_cpu(vhdr->free_blocks); - HFSPLUS_SB(sb).next_alloc = be32_to_cpu(vhdr->next_alloc); - HFSPLUS_SB(sb).next_cnid = be32_to_cpu(vhdr->next_cnid); - HFSPLUS_SB(sb).file_count = be32_to_cpu(vhdr->file_count); - HFSPLUS_SB(sb).folder_count = be32_to_cpu(vhdr->folder_count); - HFSPLUS_SB(sb).data_clump_blocks = be32_to_cpu(vhdr->data_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift; - if (!HFSPLUS_SB(sb).data_clump_blocks) - HFSPLUS_SB(sb).data_clump_blocks = 1; - HFSPLUS_SB(sb).rsrc_clump_blocks = be32_to_cpu(vhdr->rsrc_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift; - if (!HFSPLUS_SB(sb).rsrc_clump_blocks) - HFSPLUS_SB(sb).rsrc_clump_blocks = 1; + sbi->total_blocks = be32_to_cpu(vhdr->total_blocks); + sbi->free_blocks = be32_to_cpu(vhdr->free_blocks); + sbi->next_alloc = be32_to_cpu(vhdr->next_alloc); + sbi->next_cnid = be32_to_cpu(vhdr->next_cnid); + sbi->file_count = be32_to_cpu(vhdr->file_count); + sbi->folder_count = be32_to_cpu(vhdr->folder_count); + sbi->data_clump_blocks = + be32_to_cpu(vhdr->data_clump_sz) >> sbi->alloc_blksz_shift; + if (!sbi->data_clump_blocks) + sbi->data_clump_blocks = 1; + sbi->rsrc_clump_blocks = + be32_to_cpu(vhdr->rsrc_clump_sz) >> sbi->alloc_blksz_shift; + if (!sbi->rsrc_clump_blocks) + sbi->rsrc_clump_blocks = 1; /* Set up operations so we can load metadata */ sb->s_op = &hfsplus_sops; @@ -383,13 +392,13 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) sbi->flags &= ~HFSPLUS_SB_FORCE; /* Load metadata objects (B*Trees) */ - HFSPLUS_SB(sb).ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID); - if (!HFSPLUS_SB(sb).ext_tree) { + sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID); + if (!sbi->ext_tree) { printk(KERN_ERR "hfs: failed to load extents file\n"); goto cleanup; } - HFSPLUS_SB(sb).cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID); - if (!HFSPLUS_SB(sb).cat_tree) { + sbi->cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID); + if (!sbi->cat_tree) { printk(KERN_ERR "hfs: failed to load catalog file\n"); goto cleanup; } @@ -400,7 +409,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) err = PTR_ERR(inode); goto cleanup; } - HFSPLUS_SB(sb).alloc_file = inode; + sbi->alloc_file = inode; /* Load the root directory */ root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID); @@ -419,7 +428,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; str.name = HFSP_HIDDENDIR_NAME; - hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfs_find_init(sbi->cat_tree, &fd); hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { hfs_find_exit(&fd); @@ -430,7 +439,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) err = PTR_ERR(inode); goto cleanup; } - HFSPLUS_SB(sb).hidden_dir = inode; + sbi->hidden_dir = inode; } else hfs_find_exit(&fd); @@ -445,15 +454,15 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) be32_add_cpu(&vhdr->write_count, 1); vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); - mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh); - sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh); + mark_buffer_dirty(sbi->s_vhbh); + sync_dirty_buffer(sbi->s_vhbh); - if (!HFSPLUS_SB(sb).hidden_dir) { + if (!sbi->hidden_dir) { printk(KERN_DEBUG "hfs: create hidden dir...\n"); - HFSPLUS_SB(sb).hidden_dir = hfsplus_new_inode(sb, S_IFDIR); - hfsplus_create_cat(HFSPLUS_SB(sb).hidden_dir->i_ino, sb->s_root->d_inode, - &str, HFSPLUS_SB(sb).hidden_dir); - mark_inode_dirty(HFSPLUS_SB(sb).hidden_dir); + sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); + hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode, + &str, sbi->hidden_dir); + mark_inode_dirty(sbi->hidden_dir); } out: unload_nls(sbi->nls); diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index 628ccf6fa40..a15fcffce74 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -121,7 +121,7 @@ static u16 *hfsplus_compose_lookup(u16 *p, u16 cc) int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, char *astr, int *len_p) { const hfsplus_unichr *ip; - struct nls_table *nls = HFSPLUS_SB(sb).nls; + struct nls_table *nls = HFSPLUS_SB(sb)->nls; u8 *op; u16 cc, c0, c1; u16 *ce1, *ce2; @@ -132,7 +132,7 @@ int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, c ustrlen = be16_to_cpu(ustr->length); len = *len_p; ce1 = NULL; - compose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); + compose = !(HFSPLUS_SB(sb)->flags & HFSPLUS_SB_NODECOMPOSE); while (ustrlen > 0) { c0 = be16_to_cpu(*ip++); @@ -246,7 +246,7 @@ out: static inline int asc2unichar(struct super_block *sb, const char *astr, int len, wchar_t *uc) { - int size = HFSPLUS_SB(sb).nls->char2uni(astr, len, uc); + int size = HFSPLUS_SB(sb)->nls->char2uni(astr, len, uc); if (size <= 0) { *uc = '?'; size = 1; @@ -293,7 +293,7 @@ int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, u16 *dstr, outlen = 0; wchar_t c; - decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); + decompose = !(HFSPLUS_SB(sb)->flags & HFSPLUS_SB_NODECOMPOSE); while (outlen < HFSPLUS_MAX_STRLEN && len > 0) { size = asc2unichar(sb, astr, len, &c); @@ -330,8 +330,8 @@ int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str) wchar_t c; u16 c2; - casefold = (HFSPLUS_SB(sb).flags & HFSPLUS_SB_CASEFOLD); - decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); + casefold = (HFSPLUS_SB(sb)->flags & HFSPLUS_SB_CASEFOLD); + decompose = !(HFSPLUS_SB(sb)->flags & HFSPLUS_SB_NODECOMPOSE); hash = init_name_hash(); astr = str->name; len = str->len; @@ -373,8 +373,8 @@ int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr * u16 c1, c2; wchar_t c; - casefold = (HFSPLUS_SB(sb).flags & HFSPLUS_SB_CASEFOLD); - decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); + casefold = (HFSPLUS_SB(sb)->flags & HFSPLUS_SB_CASEFOLD); + decompose = !(HFSPLUS_SB(sb)->flags & HFSPLUS_SB_NODECOMPOSE); astr1 = s1->name; len1 = s1->len; astr2 = s2->name; diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c index bed78ac8f6d..c89e198e8a2 100644 --- a/fs/hfsplus/wrapper.c +++ b/fs/hfsplus/wrapper.c @@ -65,8 +65,8 @@ static int hfsplus_get_last_session(struct super_block *sb, *start = 0; *size = sb->s_bdev->bd_inode->i_size >> 9; - if (HFSPLUS_SB(sb).session >= 0) { - te.cdte_track = HFSPLUS_SB(sb).session; + if (HFSPLUS_SB(sb)->session >= 0) { + te.cdte_track = HFSPLUS_SB(sb)->session; te.cdte_format = CDROM_LBA; res = ioctl_by_bdev(sb->s_bdev, CDROMREADTOCENTRY, (unsigned long)&te); if (!res && (te.cdte_ctrl & CDROM_DATA_TRACK) == 4) { @@ -87,6 +87,7 @@ static int hfsplus_get_last_session(struct super_block *sb, /* Takes in super block, returns true if good data read */ int hfsplus_read_wrapper(struct super_block *sb) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); struct buffer_head *bh; struct hfsplus_vh *vhdr; struct hfsplus_wd wd; @@ -122,7 +123,7 @@ int hfsplus_read_wrapper(struct super_block *sb) if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG)) break; if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) { - HFSPLUS_SB(sb).flags |= HFSPLUS_SB_HFSX; + sbi->flags |= HFSPLUS_SB_HFSX; break; } brelse(bh); @@ -143,11 +144,11 @@ int hfsplus_read_wrapper(struct super_block *sb) if (blocksize < HFSPLUS_SECTOR_SIZE || ((blocksize - 1) & blocksize)) return -EINVAL; - HFSPLUS_SB(sb).alloc_blksz = blocksize; - HFSPLUS_SB(sb).alloc_blksz_shift = 0; + sbi->alloc_blksz = blocksize; + sbi->alloc_blksz_shift = 0; while ((blocksize >>= 1) != 0) - HFSPLUS_SB(sb).alloc_blksz_shift++; - blocksize = min(HFSPLUS_SB(sb).alloc_blksz, (u32)PAGE_SIZE); + sbi->alloc_blksz_shift++; + blocksize = min(sbi->alloc_blksz, (u32)PAGE_SIZE); /* align block size to block offset */ while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1)) @@ -158,23 +159,22 @@ int hfsplus_read_wrapper(struct super_block *sb) return -EINVAL; } - HFSPLUS_SB(sb).blockoffset = part_start >> - (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); - HFSPLUS_SB(sb).sect_count = part_size; - HFSPLUS_SB(sb).fs_shift = HFSPLUS_SB(sb).alloc_blksz_shift - - sb->s_blocksize_bits; + sbi->blockoffset = + part_start >> (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT); + sbi->sect_count = part_size; + sbi->fs_shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr); if (!bh) return -EIO; /* should still be the same... */ - if (vhdr->signature != (HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX ? + if (vhdr->signature != (sbi->flags & HFSPLUS_SB_HFSX ? cpu_to_be16(HFSPLUS_VOLHEAD_SIGX) : cpu_to_be16(HFSPLUS_VOLHEAD_SIG))) goto error; - HFSPLUS_SB(sb).s_vhbh = bh; - HFSPLUS_SB(sb).s_vhdr = vhdr; + sbi->s_vhbh = bh; + sbi->s_vhdr = vhdr; return 0; error: -- cgit v1.2.3-18-g5258 From 6af502de224c3742936d54eee7e3690c09822934 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:43:31 +0200 Subject: hfsplus: fix HFSPLUS_I calling convention HFSPLUS_I doesn't return a pointer to the hfsplus-specific inode information like all other FOO_I macros, but dereference the pointer in a way that made it look like a direct struct derefence. This only works as long as the HFSPLUS_I macro is used directly and prevents us from keepig a local hfsplus_inode_info pointer. Fix the calling convention and introduce a local hip variable in all functions that use it constantly. Signed-off-by: Christoph Hellwig --- fs/hfsplus/btree.c | 9 +-- fs/hfsplus/catalog.c | 14 ++-- fs/hfsplus/dir.c | 14 ++-- fs/hfsplus/extents.c | 171 ++++++++++++++++++++++++++---------------------- fs/hfsplus/hfsplus_fs.h | 8 +-- fs/hfsplus/inode.c | 112 ++++++++++++++++--------------- fs/hfsplus/ioctl.c | 22 ++++--- fs/hfsplus/super.c | 16 ++--- 8 files changed, 193 insertions(+), 173 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 96e2128748f..f75cf222b8b 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -192,17 +192,18 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree) while (!tree->free_nodes) { struct inode *inode = tree->inode; + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); u32 count; int res; res = hfsplus_file_extend(inode); if (res) return ERR_PTR(res); - HFSPLUS_I(inode).phys_size = inode->i_size = - (loff_t)HFSPLUS_I(inode).alloc_blocks << + hip->phys_size = inode->i_size = + (loff_t)hip->alloc_blocks << HFSPLUS_SB(tree->sb)->alloc_blksz_shift; - HFSPLUS_I(inode).fs_blocks = HFSPLUS_I(inode).alloc_blocks << - HFSPLUS_SB(tree->sb)->fs_shift; + hip->fs_blocks = + hip->alloc_blocks << HFSPLUS_SB(tree->sb)->fs_shift; inode_set_bytes(inode, inode->i_size); count = inode->i_size >> tree->node_size_shift; tree->free_nodes = count - tree->node_count; diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 75ac1e466f1..48979c4e8fa 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -77,8 +77,8 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) perms->rootflags |= HFSPLUS_FLG_APPEND; else perms->rootflags &= ~HFSPLUS_FLG_APPEND; - HFSPLUS_I(inode).rootflags = perms->rootflags; - HFSPLUS_I(inode).userflags = perms->userflags; + HFSPLUS_I(inode)->rootflags = perms->rootflags; + HFSPLUS_I(inode)->userflags = perms->userflags; perms->mode = cpu_to_be16(inode->i_mode); perms->owner = cpu_to_be32(inode->i_uid); perms->group = cpu_to_be32(inode->i_gid); @@ -95,7 +95,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i memset(folder, 0, sizeof(*folder)); folder->type = cpu_to_be16(HFSPLUS_FOLDER); folder->id = cpu_to_be32(inode->i_ino); - HFSPLUS_I(inode).create_date = + HFSPLUS_I(inode)->create_date = folder->create_date = folder->content_mod_date = folder->attribute_mod_date = @@ -113,7 +113,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i file->type = cpu_to_be16(HFSPLUS_FILE); file->flags = cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS); file->id = cpu_to_be32(cnid); - HFSPLUS_I(inode).create_date = + HFSPLUS_I(inode)->create_date = file->create_date = file->content_mod_date = file->attribute_mod_date = @@ -133,8 +133,8 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i file->user_info.fdType = cpu_to_be32(HFSP_HARDLINK_TYPE); file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR); file->user_info.fdFlags = cpu_to_be16(0x100); - file->create_date = HFSPLUS_I(sbi->hidden_dir).create_date; - file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode).dev); + file->create_date = HFSPLUS_I(sbi->hidden_dir)->create_date; + file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode)->dev); } return sizeof(*file); } @@ -279,7 +279,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC); } - list_for_each(pos, &HFSPLUS_I(dir).open_dir_list) { + list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) { struct hfsplus_readdir_data *rd = list_entry(pos, struct hfsplus_readdir_data, list); if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0) diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 584777ddb0b..5ca8308f9eb 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -68,8 +68,8 @@ again: cnid = be32_to_cpu(entry.file.id); if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) && entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR) && - (entry.file.create_date == HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir).create_date || - entry.file.create_date == HFSPLUS_I(sb->s_root->d_inode).create_date) && + (entry.file.create_date == HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->create_date || + entry.file.create_date == HFSPLUS_I(sb->s_root->d_inode)->create_date) && HFSPLUS_SB(sb)->hidden_dir) { struct qstr str; char name[32]; @@ -102,7 +102,7 @@ again: if (IS_ERR(inode)) return ERR_CAST(inode); if (S_ISREG(inode->i_mode)) - HFSPLUS_I(inode).dev = linkid; + HFSPLUS_I(inode)->dev = linkid; out: d_add(dentry, inode); return NULL; @@ -219,7 +219,7 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) } filp->private_data = rd; rd->file = filp; - list_add(&rd->list, &HFSPLUS_I(inode).open_dir_list); + list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list); } memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); out: @@ -287,7 +287,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, if (res != -EEXIST) return res; } - HFSPLUS_I(inode).dev = id; + HFSPLUS_I(inode)->dev = id; cnid = sbi->next_cnid++; src_dentry->d_fsdata = (void *)(unsigned long)cnid; res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); @@ -326,7 +326,7 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) cnid = (u32)(unsigned long)dentry->d_fsdata; if (inode->i_ino == cnid && - atomic_read(&HFSPLUS_I(inode).opencnt)) { + atomic_read(&HFSPLUS_I(inode)->opencnt)) { str.name = name; str.len = sprintf(name, "temp%lu", inode->i_ino); res = hfsplus_rename_cat(inode->i_ino, @@ -347,7 +347,7 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) if (!inode->i_nlink) { if (inode->i_ino != cnid) { sbi->file_count--; - if (!atomic_read(&HFSPLUS_I(inode).opencnt)) { + if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) { res = hfsplus_delete_cat(inode->i_ino, sbi->hidden_dir, NULL); diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c index 18196981434..b1017eaa4fd 100644 --- a/fs/hfsplus/extents.c +++ b/fs/hfsplus/extents.c @@ -85,27 +85,32 @@ static u32 hfsplus_ext_lastblock(struct hfsplus_extent *ext) static void __hfsplus_ext_write_extent(struct inode *inode, struct hfs_find_data *fd) { + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); int res; - hfsplus_ext_build_key(fd->search_key, inode->i_ino, HFSPLUS_I(inode).cached_start, - HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); + hfsplus_ext_build_key(fd->search_key, inode->i_ino, hip->cached_start, + HFSPLUS_IS_RSRC(inode) ? + HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); + res = hfs_brec_find(fd); - if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_NEW) { + if (hip->flags & HFSPLUS_FLG_EXT_NEW) { if (res != -ENOENT) return; - hfs_brec_insert(fd, HFSPLUS_I(inode).cached_extents, sizeof(hfsplus_extent_rec)); - HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); + hfs_brec_insert(fd, hip->cached_extents, + sizeof(hfsplus_extent_rec)); + hip->flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); } else { if (res) return; - hfs_bnode_write(fd->bnode, HFSPLUS_I(inode).cached_extents, fd->entryoffset, fd->entrylength); - HFSPLUS_I(inode).flags &= ~HFSPLUS_FLG_EXT_DIRTY; + hfs_bnode_write(fd->bnode, hip->cached_extents, + fd->entryoffset, fd->entrylength); + hip->flags &= ~HFSPLUS_FLG_EXT_DIRTY; } } void hfsplus_ext_write_extent(struct inode *inode) { - if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_DIRTY) { + if (HFSPLUS_I(inode)->flags & HFSPLUS_FLG_EXT_DIRTY) { struct hfs_find_data fd; hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); @@ -136,30 +141,34 @@ static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd, static inline int __hfsplus_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block) { + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); int res; - if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_DIRTY) + if (hip->flags & HFSPLUS_FLG_EXT_DIRTY) __hfsplus_ext_write_extent(inode, fd); - res = __hfsplus_ext_read_extent(fd, HFSPLUS_I(inode).cached_extents, inode->i_ino, - block, HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); + res = __hfsplus_ext_read_extent(fd, hip->cached_extents, inode->i_ino, + block, HFSPLUS_IS_RSRC(inode) ? + HFSPLUS_TYPE_RSRC : + HFSPLUS_TYPE_DATA); if (!res) { - HFSPLUS_I(inode).cached_start = be32_to_cpu(fd->key->ext.start_block); - HFSPLUS_I(inode).cached_blocks = hfsplus_ext_block_count(HFSPLUS_I(inode).cached_extents); + hip->cached_start = be32_to_cpu(fd->key->ext.start_block); + hip->cached_blocks = hfsplus_ext_block_count(hip->cached_extents); } else { - HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).cached_blocks = 0; - HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); + hip->cached_start = hip->cached_blocks = 0; + hip->flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); } return res; } static int hfsplus_ext_read_extent(struct inode *inode, u32 block) { + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); struct hfs_find_data fd; int res; - if (block >= HFSPLUS_I(inode).cached_start && - block < HFSPLUS_I(inode).cached_start + HFSPLUS_I(inode).cached_blocks) + if (block >= hip->cached_start && + block < hip->cached_start + hip->cached_blocks) return 0; hfs_find_init(HFSPLUS_SB(inode->i_sb)->ext_tree, &fd); @@ -174,6 +183,7 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock, { struct super_block *sb = inode->i_sb; struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); int res = -EIO; u32 ablock, dblock, mask; int shift; @@ -182,10 +192,10 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock, shift = sbi->alloc_blksz_shift - sb->s_blocksize_bits; ablock = iblock >> sbi->fs_shift; - if (iblock >= HFSPLUS_I(inode).fs_blocks) { - if (iblock > HFSPLUS_I(inode).fs_blocks || !create) + if (iblock >= hip->fs_blocks) { + if (iblock > hip->fs_blocks || !create) return -EIO; - if (ablock >= HFSPLUS_I(inode).alloc_blocks) { + if (ablock >= hip->alloc_blocks) { res = hfsplus_file_extend(inode); if (res) return res; @@ -193,24 +203,24 @@ int hfsplus_get_block(struct inode *inode, sector_t iblock, } else create = 0; - if (ablock < HFSPLUS_I(inode).first_blocks) { - dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).first_extents, ablock); + if (ablock < hip->first_blocks) { + dblock = hfsplus_ext_find_block(hip->first_extents, ablock); goto done; } if (inode->i_ino == HFSPLUS_EXT_CNID) return -EIO; - mutex_lock(&HFSPLUS_I(inode).extents_lock); + mutex_lock(&hip->extents_lock); res = hfsplus_ext_read_extent(inode, ablock); if (!res) { - dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).cached_extents, ablock - - HFSPLUS_I(inode).cached_start); + dblock = hfsplus_ext_find_block(hip->cached_extents, + ablock - hip->cached_start); } else { - mutex_unlock(&HFSPLUS_I(inode).extents_lock); + mutex_unlock(&hip->extents_lock); return -EIO; } - mutex_unlock(&HFSPLUS_I(inode).extents_lock); + mutex_unlock(&hip->extents_lock); done: dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n", inode->i_ino, (long long)iblock, dblock); @@ -218,8 +228,8 @@ done: map_bh(bh_result, sb, (dblock << sbi->fs_shift) + sbi->blockoffset + (iblock & mask)); if (create) { set_buffer_new(bh_result); - HFSPLUS_I(inode).phys_size += sb->s_blocksize; - HFSPLUS_I(inode).fs_blocks++; + hip->phys_size += sb->s_blocksize; + hip->fs_blocks++; inode_add_bytes(inode, sb->s_blocksize); mark_inode_dirty(inode); } @@ -348,6 +358,7 @@ int hfsplus_file_extend(struct inode *inode) { struct super_block *sb = inode->i_sb; struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); u32 start, len, goal; int res; @@ -360,17 +371,17 @@ int hfsplus_file_extend(struct inode *inode) return -ENOSPC; } - mutex_lock(&HFSPLUS_I(inode).extents_lock); - if (HFSPLUS_I(inode).alloc_blocks == HFSPLUS_I(inode).first_blocks) - goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).first_extents); + mutex_lock(&hip->extents_lock); + if (hip->alloc_blocks == hip->first_blocks) + goal = hfsplus_ext_lastblock(hip->first_extents); else { - res = hfsplus_ext_read_extent(inode, HFSPLUS_I(inode).alloc_blocks); + res = hfsplus_ext_read_extent(inode, hip->alloc_blocks); if (res) goto out; - goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).cached_extents); + goal = hfsplus_ext_lastblock(hip->cached_extents); } - len = HFSPLUS_I(inode).clump_blocks; + len = hip->clump_blocks; start = hfsplus_block_allocate(sb, sbi->total_blocks, goal, &len); if (start >= sbi->total_blocks) { start = hfsplus_block_allocate(sb, goal, 0, &len); @@ -381,41 +392,41 @@ int hfsplus_file_extend(struct inode *inode) } dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len); - if (HFSPLUS_I(inode).alloc_blocks <= HFSPLUS_I(inode).first_blocks) { - if (!HFSPLUS_I(inode).first_blocks) { + + if (hip->alloc_blocks <= hip->first_blocks) { + if (!hip->first_blocks) { dprint(DBG_EXTENT, "first extents\n"); /* no extents yet */ - HFSPLUS_I(inode).first_extents[0].start_block = cpu_to_be32(start); - HFSPLUS_I(inode).first_extents[0].block_count = cpu_to_be32(len); + hip->first_extents[0].start_block = cpu_to_be32(start); + hip->first_extents[0].block_count = cpu_to_be32(len); res = 0; } else { /* try to append to extents in inode */ - res = hfsplus_add_extent(HFSPLUS_I(inode).first_extents, - HFSPLUS_I(inode).alloc_blocks, + res = hfsplus_add_extent(hip->first_extents, + hip->alloc_blocks, start, len); if (res == -ENOSPC) goto insert_extent; } if (!res) { - hfsplus_dump_extent(HFSPLUS_I(inode).first_extents); - HFSPLUS_I(inode).first_blocks += len; + hfsplus_dump_extent(hip->first_extents); + hip->first_blocks += len; } } else { - res = hfsplus_add_extent(HFSPLUS_I(inode).cached_extents, - HFSPLUS_I(inode).alloc_blocks - - HFSPLUS_I(inode).cached_start, + res = hfsplus_add_extent(hip->cached_extents, + hip->alloc_blocks - hip->cached_start, start, len); if (!res) { - hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); - HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY; - HFSPLUS_I(inode).cached_blocks += len; + hfsplus_dump_extent(hip->cached_extents); + hip->flags |= HFSPLUS_FLG_EXT_DIRTY; + hip->cached_blocks += len; } else if (res == -ENOSPC) goto insert_extent; } out: - mutex_unlock(&HFSPLUS_I(inode).extents_lock); + mutex_unlock(&hip->extents_lock); if (!res) { - HFSPLUS_I(inode).alloc_blocks += len; + hip->alloc_blocks += len; mark_inode_dirty(inode); } return res; @@ -424,13 +435,13 @@ insert_extent: dprint(DBG_EXTENT, "insert new extent\n"); hfsplus_ext_write_extent(inode); - memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); - HFSPLUS_I(inode).cached_extents[0].start_block = cpu_to_be32(start); - HFSPLUS_I(inode).cached_extents[0].block_count = cpu_to_be32(len); - hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); - HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW; - HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).alloc_blocks; - HFSPLUS_I(inode).cached_blocks = len; + memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec)); + hip->cached_extents[0].start_block = cpu_to_be32(start); + hip->cached_extents[0].block_count = cpu_to_be32(len); + hfsplus_dump_extent(hip->cached_extents); + hip->flags |= HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW; + hip->cached_start = hip->alloc_blocks; + hip->cached_blocks = len; res = 0; goto out; @@ -439,13 +450,15 @@ insert_extent: void hfsplus_file_truncate(struct inode *inode) { struct super_block *sb = inode->i_sb; + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); struct hfs_find_data fd; u32 alloc_cnt, blk_cnt, start; int res; - dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino, - (long long)HFSPLUS_I(inode).phys_size, inode->i_size); - if (inode->i_size > HFSPLUS_I(inode).phys_size) { + dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", + inode->i_ino, (long long)hip->phys_size, inode->i_size); + + if (inode->i_size > hip->phys_size) { struct address_space *mapping = inode->i_mapping; struct page *page; void *fsdata; @@ -462,48 +475,48 @@ void hfsplus_file_truncate(struct inode *inode) return; mark_inode_dirty(inode); return; - } else if (inode->i_size == HFSPLUS_I(inode).phys_size) + } else if (inode->i_size == hip->phys_size) return; blk_cnt = (inode->i_size + HFSPLUS_SB(sb)->alloc_blksz - 1) >> HFSPLUS_SB(sb)->alloc_blksz_shift; - alloc_cnt = HFSPLUS_I(inode).alloc_blocks; + alloc_cnt = hip->alloc_blocks; if (blk_cnt == alloc_cnt) goto out; - mutex_lock(&HFSPLUS_I(inode).extents_lock); + mutex_lock(&hip->extents_lock); hfs_find_init(HFSPLUS_SB(sb)->ext_tree, &fd); while (1) { - if (alloc_cnt == HFSPLUS_I(inode).first_blocks) { - hfsplus_free_extents(sb, HFSPLUS_I(inode).first_extents, + if (alloc_cnt == hip->first_blocks) { + hfsplus_free_extents(sb, hip->first_extents, alloc_cnt, alloc_cnt - blk_cnt); - hfsplus_dump_extent(HFSPLUS_I(inode).first_extents); - HFSPLUS_I(inode).first_blocks = blk_cnt; + hfsplus_dump_extent(hip->first_extents); + hip->first_blocks = blk_cnt; break; } res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt); if (res) break; - start = HFSPLUS_I(inode).cached_start; - hfsplus_free_extents(sb, HFSPLUS_I(inode).cached_extents, + start = hip->cached_start; + hfsplus_free_extents(sb, hip->cached_extents, alloc_cnt - start, alloc_cnt - blk_cnt); - hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents); + hfsplus_dump_extent(hip->cached_extents); if (blk_cnt > start) { - HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY; + hip->flags |= HFSPLUS_FLG_EXT_DIRTY; break; } alloc_cnt = start; - HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).cached_blocks = 0; - HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); + hip->cached_start = hip->cached_blocks = 0; + hip->flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW); hfs_brec_remove(&fd); } hfs_find_exit(&fd); - mutex_unlock(&HFSPLUS_I(inode).extents_lock); + mutex_unlock(&hip->extents_lock); - HFSPLUS_I(inode).alloc_blocks = blk_cnt; + hip->alloc_blocks = blk_cnt; out: - HFSPLUS_I(inode).phys_size = inode->i_size; - HFSPLUS_I(inode).fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; - inode_set_bytes(inode, HFSPLUS_I(inode).fs_blocks << sb->s_blocksize_bits); + hip->phys_size = inode->i_size; + hip->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits); mark_inode_dirty(inode); } diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 55f42b49f0f..169cef964b5 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -187,8 +187,8 @@ struct hfsplus_inode_info { #define HFSPLUS_FLG_EXT_DIRTY 0x0002 #define HFSPLUS_FLG_EXT_NEW 0x0004 -#define HFSPLUS_IS_DATA(inode) (!(HFSPLUS_I(inode).flags & HFSPLUS_FLG_RSRC)) -#define HFSPLUS_IS_RSRC(inode) (HFSPLUS_I(inode).flags & HFSPLUS_FLG_RSRC) +#define HFSPLUS_IS_DATA(inode) (!(HFSPLUS_I(inode)->flags & HFSPLUS_FLG_RSRC)) +#define HFSPLUS_IS_RSRC(inode) (HFSPLUS_I(inode)->flags & HFSPLUS_FLG_RSRC) struct hfs_find_data { /* filled by caller */ @@ -379,13 +379,11 @@ static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb) { return sb->s_fs_info; } -/* + static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode) { return list_entry(inode, struct hfsplus_inode_info, vfs_inode); } -*/ -#define HFSPLUS_I(inode) (*list_entry(inode, struct hfsplus_inode_info, vfs_inode)) #if 1 #define hfsplus_kmap(p) ({ struct page *__p = (p); kmap(__p); }) diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 050eee6c81b..309defb4f41 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -36,7 +36,7 @@ static int hfsplus_write_begin(struct file *file, struct address_space *mapping, *pagep = NULL; ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, hfsplus_get_block, - &HFSPLUS_I(mapping->host).phys_size); + &HFSPLUS_I(mapping->host)->phys_size); if (unlikely(ret)) { loff_t isize = mapping->host->i_size; if (pos + len > isize) @@ -172,12 +172,13 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dent struct hfs_find_data fd; struct super_block *sb = dir->i_sb; struct inode *inode = NULL; + struct hfsplus_inode_info *hip; int err; if (HFSPLUS_IS_RSRC(dir) || strcmp(dentry->d_name.name, "rsrc")) goto out; - inode = HFSPLUS_I(dir).rsrc_inode; + inode = HFSPLUS_I(dir)->rsrc_inode; if (inode) goto out; @@ -185,10 +186,11 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dent if (!inode) return ERR_PTR(-ENOMEM); + hip = HFSPLUS_I(inode); inode->i_ino = dir->i_ino; - INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list); - mutex_init(&HFSPLUS_I(inode).extents_lock); - HFSPLUS_I(inode).flags = HFSPLUS_FLG_RSRC; + INIT_LIST_HEAD(&hip->open_dir_list); + mutex_init(&hip->extents_lock); + hip->flags = HFSPLUS_FLG_RSRC; hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); err = hfsplus_find_cat(sb, dir->i_ino, &fd); @@ -199,8 +201,8 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dent iput(inode); return ERR_PTR(err); } - HFSPLUS_I(inode).rsrc_inode = dir; - HFSPLUS_I(dir).rsrc_inode = inode; + hip->rsrc_inode = dir; + HFSPLUS_I(dir)->rsrc_inode = inode; igrab(dir); hlist_add_head(&inode->i_hash, &HFSPLUS_SB(sb)->rsrc_inodes); mark_inode_dirty(inode); @@ -231,8 +233,8 @@ static void hfsplus_get_perms(struct inode *inode, struct hfsplus_perm *perms, i mode = S_IFREG | ((S_IRUGO|S_IWUGO) & ~(sbi->umask)); inode->i_mode = mode; - HFSPLUS_I(inode).rootflags = perms->rootflags; - HFSPLUS_I(inode).userflags = perms->userflags; + HFSPLUS_I(inode)->rootflags = perms->rootflags; + HFSPLUS_I(inode)->userflags = perms->userflags; if (perms->rootflags & HFSPLUS_FLG_IMMUTABLE) inode->i_flags |= S_IMMUTABLE; else @@ -253,20 +255,20 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) perms->rootflags |= HFSPLUS_FLG_APPEND; else perms->rootflags &= ~HFSPLUS_FLG_APPEND; - perms->userflags = HFSPLUS_I(inode).userflags; + perms->userflags = HFSPLUS_I(inode)->userflags; perms->mode = cpu_to_be16(inode->i_mode); perms->owner = cpu_to_be32(inode->i_uid); perms->group = cpu_to_be32(inode->i_gid); - perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev); + perms->dev = cpu_to_be32(HFSPLUS_I(inode)->dev); } static int hfsplus_file_open(struct inode *inode, struct file *file) { if (HFSPLUS_IS_RSRC(inode)) - inode = HFSPLUS_I(inode).rsrc_inode; + inode = HFSPLUS_I(inode)->rsrc_inode; if (!(file->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS) return -EOVERFLOW; - atomic_inc(&HFSPLUS_I(inode).opencnt); + atomic_inc(&HFSPLUS_I(inode)->opencnt); return 0; } @@ -275,8 +277,8 @@ static int hfsplus_file_release(struct inode *inode, struct file *file) struct super_block *sb = inode->i_sb; if (HFSPLUS_IS_RSRC(inode)) - inode = HFSPLUS_I(inode).rsrc_inode; - if (atomic_dec_and_test(&HFSPLUS_I(inode).opencnt)) { + inode = HFSPLUS_I(inode)->rsrc_inode; + if (atomic_dec_and_test(&HFSPLUS_I(inode)->opencnt)) { mutex_lock(&inode->i_mutex); hfsplus_file_truncate(inode); if (inode->i_flags & S_DEAD) { @@ -362,6 +364,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, int mode) { struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); struct inode *inode = new_inode(sb); + struct hfsplus_inode_info *hip; if (!inode) return NULL; @@ -372,19 +375,21 @@ struct inode *hfsplus_new_inode(struct super_block *sb, int mode) inode->i_gid = current_fsgid(); inode->i_nlink = 1; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; - INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list); - mutex_init(&HFSPLUS_I(inode).extents_lock); - atomic_set(&HFSPLUS_I(inode).opencnt, 0); - HFSPLUS_I(inode).flags = 0; - memset(HFSPLUS_I(inode).first_extents, 0, sizeof(hfsplus_extent_rec)); - memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); - HFSPLUS_I(inode).alloc_blocks = 0; - HFSPLUS_I(inode).first_blocks = 0; - HFSPLUS_I(inode).cached_start = 0; - HFSPLUS_I(inode).cached_blocks = 0; - HFSPLUS_I(inode).phys_size = 0; - HFSPLUS_I(inode).fs_blocks = 0; - HFSPLUS_I(inode).rsrc_inode = NULL; + + hip = HFSPLUS_I(inode); + INIT_LIST_HEAD(&hip->open_dir_list); + mutex_init(&hip->extents_lock); + atomic_set(&hip->opencnt, 0); + hip->flags = 0; + memset(hip->first_extents, 0, sizeof(hfsplus_extent_rec)); + memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec)); + hip->alloc_blocks = 0; + hip->first_blocks = 0; + hip->cached_start = 0; + hip->cached_blocks = 0; + hip->phys_size = 0; + hip->fs_blocks = 0; + hip->rsrc_inode = NULL; if (S_ISDIR(inode->i_mode)) { inode->i_size = 2; sbi->folder_count++; @@ -395,12 +400,12 @@ struct inode *hfsplus_new_inode(struct super_block *sb, int mode) inode->i_op = &hfsplus_file_inode_operations; inode->i_fop = &hfsplus_file_operations; inode->i_mapping->a_ops = &hfsplus_aops; - HFSPLUS_I(inode).clump_blocks = sbi->data_clump_blocks; + hip->clump_blocks = sbi->data_clump_blocks; } else if (S_ISLNK(inode->i_mode)) { sbi->file_count++; inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &hfsplus_aops; - HFSPLUS_I(inode).clump_blocks = 1; + hip->clump_blocks = 1; } else sbi->file_count++; insert_inode_hash(inode); @@ -436,26 +441,27 @@ void hfsplus_inode_read_fork(struct inode *inode, struct hfsplus_fork_raw *fork) { struct super_block *sb = inode->i_sb; struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); u32 count; int i; - memcpy(&HFSPLUS_I(inode).first_extents, &fork->extents, - sizeof(hfsplus_extent_rec)); + memcpy(&hip->first_extents, &fork->extents, sizeof(hfsplus_extent_rec)); for (count = 0, i = 0; i < 8; i++) count += be32_to_cpu(fork->extents[i].block_count); - HFSPLUS_I(inode).first_blocks = count; - memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec)); - HFSPLUS_I(inode).cached_start = 0; - HFSPLUS_I(inode).cached_blocks = 0; - - HFSPLUS_I(inode).alloc_blocks = be32_to_cpu(fork->total_blocks); - inode->i_size = HFSPLUS_I(inode).phys_size = be64_to_cpu(fork->total_size); - HFSPLUS_I(inode).fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; - inode_set_bytes(inode, HFSPLUS_I(inode).fs_blocks << sb->s_blocksize_bits); - HFSPLUS_I(inode).clump_blocks = + hip->first_blocks = count; + memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec)); + hip->cached_start = 0; + hip->cached_blocks = 0; + + hip->alloc_blocks = be32_to_cpu(fork->total_blocks); + hip->phys_size = inode->i_size = be64_to_cpu(fork->total_size); + hip->fs_blocks = + (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; + inode_set_bytes(inode, hip->fs_blocks << sb->s_blocksize_bits); + hip->clump_blocks = be32_to_cpu(fork->clump_size) >> sbi->alloc_blksz_shift; - if (!HFSPLUS_I(inode).clump_blocks) { - HFSPLUS_I(inode).clump_blocks = HFSPLUS_IS_RSRC(inode) ? + if (!hip->clump_blocks) { + hip->clump_blocks = HFSPLUS_IS_RSRC(inode) ? sbi->rsrc_clump_blocks : sbi->data_clump_blocks; } @@ -463,10 +469,10 @@ void hfsplus_inode_read_fork(struct inode *inode, struct hfsplus_fork_raw *fork) void hfsplus_inode_write_fork(struct inode *inode, struct hfsplus_fork_raw *fork) { - memcpy(&fork->extents, &HFSPLUS_I(inode).first_extents, + memcpy(&fork->extents, &HFSPLUS_I(inode)->first_extents, sizeof(hfsplus_extent_rec)); fork->total_size = cpu_to_be64(inode->i_size); - fork->total_blocks = cpu_to_be32(HFSPLUS_I(inode).alloc_blocks); + fork->total_blocks = cpu_to_be32(HFSPLUS_I(inode)->alloc_blocks); } int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) @@ -477,7 +483,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) type = hfs_bnode_read_u16(fd->bnode, fd->entryoffset); - HFSPLUS_I(inode).dev = 0; + HFSPLUS_I(inode)->dev = 0; if (type == HFSPLUS_FOLDER) { struct hfsplus_cat_folder *folder = &entry.folder; @@ -491,8 +497,8 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) inode->i_atime = hfsp_mt2ut(folder->access_date); inode->i_mtime = hfsp_mt2ut(folder->content_mod_date); inode->i_ctime = hfsp_mt2ut(folder->attribute_mod_date); - HFSPLUS_I(inode).create_date = folder->create_date; - HFSPLUS_I(inode).fs_blocks = 0; + HFSPLUS_I(inode)->create_date = folder->create_date; + HFSPLUS_I(inode)->fs_blocks = 0; inode->i_op = &hfsplus_dir_inode_operations; inode->i_fop = &hfsplus_dir_operations; } else if (type == HFSPLUS_FILE) { @@ -523,7 +529,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) inode->i_atime = hfsp_mt2ut(file->access_date); inode->i_mtime = hfsp_mt2ut(file->content_mod_date); inode->i_ctime = hfsp_mt2ut(file->attribute_mod_date); - HFSPLUS_I(inode).create_date = file->create_date; + HFSPLUS_I(inode)->create_date = file->create_date; } else { printk(KERN_ERR "hfs: bad catalog entry used to create inode\n"); res = -EIO; @@ -538,7 +544,7 @@ int hfsplus_cat_write_inode(struct inode *inode) hfsplus_cat_entry entry; if (HFSPLUS_IS_RSRC(inode)) - main_inode = HFSPLUS_I(inode).rsrc_inode; + main_inode = HFSPLUS_I(inode)->rsrc_inode; if (!main_inode->i_nlink) return 0; @@ -582,9 +588,9 @@ int hfsplus_cat_write_inode(struct inode *inode) sizeof(struct hfsplus_cat_file)); hfsplus_inode_write_fork(inode, &file->data_fork); if (S_ISREG(inode->i_mode)) - HFSPLUS_I(inode).dev = inode->i_nlink; + HFSPLUS_I(inode)->dev = inode->i_nlink; if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - HFSPLUS_I(inode).dev = kdev_t_to_nr(inode->i_rdev); + HFSPLUS_I(inode)->dev = kdev_t_to_nr(inode->i_rdev); hfsplus_set_perms(inode, &file->permissions); if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE) file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED); diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c index 66dd64b88b2..c9ac443d202 100644 --- a/fs/hfsplus/ioctl.c +++ b/fs/hfsplus/ioctl.c @@ -23,13 +23,14 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) { struct inode *inode = file->f_path.dentry->d_inode; + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); unsigned int flags = 0; - if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE) + if (hip->rootflags & HFSPLUS_FLG_IMMUTABLE) flags |= FS_IMMUTABLE_FL; - if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND) + if (hip->rootflags & HFSPLUS_FLG_APPEND) flags |= FS_APPEND_FL; - if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP) + if (hip->userflags & HFSPLUS_FLG_NODUMP) flags |= FS_NODUMP_FL; return put_user(flags, user_flags); @@ -38,6 +39,7 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) { struct inode *inode = file->f_path.dentry->d_inode; + struct hfsplus_inode_info *hip = HFSPLUS_I(inode); unsigned int flags; int err = 0; @@ -58,7 +60,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) mutex_lock(&inode->i_mutex); if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) || - HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) { + hip->rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) { if (!capable(CAP_LINUX_IMMUTABLE)) { err = -EPERM; goto out_unlock_inode; @@ -72,22 +74,22 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) } if (flags & FS_IMMUTABLE_FL) { inode->i_flags |= S_IMMUTABLE; - HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE; + hip->rootflags |= HFSPLUS_FLG_IMMUTABLE; } else { inode->i_flags &= ~S_IMMUTABLE; - HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE; + hip->rootflags &= ~HFSPLUS_FLG_IMMUTABLE; } if (flags & FS_APPEND_FL) { inode->i_flags |= S_APPEND; - HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND; + hip->rootflags |= HFSPLUS_FLG_APPEND; } else { inode->i_flags &= ~S_APPEND; - HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND; + hip->rootflags &= ~HFSPLUS_FLG_APPEND; } if (flags & FS_NODUMP_FL) - HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP; + hip->userflags |= HFSPLUS_FLG_NODUMP; else - HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP; + hip->userflags &= ~HFSPLUS_FLG_NODUMP; inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 1bf00b9ecc1..a7bf89e85b3 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -33,11 +33,11 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) if (!(inode->i_state & I_NEW)) return inode; - INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list); - mutex_init(&HFSPLUS_I(inode).extents_lock); - HFSPLUS_I(inode).flags = 0; - HFSPLUS_I(inode).rsrc_inode = NULL; - atomic_set(&HFSPLUS_I(inode).opencnt, 0); + INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); + mutex_init(&HFSPLUS_I(inode)->extents_lock); + HFSPLUS_I(inode)->flags = 0; + HFSPLUS_I(inode)->rsrc_inode = NULL; + atomic_set(&HFSPLUS_I(inode)->opencnt, 0); if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) { read_inode: @@ -151,8 +151,8 @@ static void hfsplus_evict_inode(struct inode *inode) truncate_inode_pages(&inode->i_data, 0); end_writeback(inode); if (HFSPLUS_IS_RSRC(inode)) { - HFSPLUS_I(HFSPLUS_I(inode).rsrc_inode).rsrc_inode = NULL; - iput(HFSPLUS_I(inode).rsrc_inode); + HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL; + iput(HFSPLUS_I(inode)->rsrc_inode); } } @@ -491,7 +491,7 @@ static struct inode *hfsplus_alloc_inode(struct super_block *sb) static void hfsplus_destroy_inode(struct inode *inode) { - kmem_cache_free(hfsplus_inode_cachep, &HFSPLUS_I(inode)); + kmem_cache_free(hfsplus_inode_cachep, HFSPLUS_I(inode)); } #define HFSPLUS_INODE_SIZE sizeof(struct hfsplus_inode_info) -- cgit v1.2.3-18-g5258 From fc4fff82104fa096eada73943fe5249500acd5fa Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:43:41 +0200 Subject: hfsplus: clean up hfsplus_iget Add a new hfsplus_system_read_inode for reading the special system inodes and streamline the fastpath iget code. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 77 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 37 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index a7bf89e85b3..bd09ea23435 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -20,12 +20,42 @@ static void hfsplus_destroy_inode(struct inode *inode); #include "hfsplus_fs.h" +static int hfsplus_system_read_inode(struct inode *inode) +{ + struct hfsplus_vh *vhdr = HFSPLUS_SB(inode->i_sb)->s_vhdr; + + switch (inode->i_ino) { + case HFSPLUS_EXT_CNID: + hfsplus_inode_read_fork(inode, &vhdr->ext_file); + inode->i_mapping->a_ops = &hfsplus_btree_aops; + break; + case HFSPLUS_CAT_CNID: + hfsplus_inode_read_fork(inode, &vhdr->cat_file); + inode->i_mapping->a_ops = &hfsplus_btree_aops; + break; + case HFSPLUS_ALLOC_CNID: + hfsplus_inode_read_fork(inode, &vhdr->alloc_file); + inode->i_mapping->a_ops = &hfsplus_aops; + break; + case HFSPLUS_START_CNID: + hfsplus_inode_read_fork(inode, &vhdr->start_file); + break; + case HFSPLUS_ATTR_CNID: + hfsplus_inode_read_fork(inode, &vhdr->attr_file); + inode->i_mapping->a_ops = &hfsplus_btree_aops; + break; + default: + return -EIO; + } + + return 0; +} + struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) { struct hfs_find_data fd; - struct hfsplus_vh *vhdr; struct inode *inode; - long err = -EIO; + int err; inode = iget_locked(sb, ino); if (!inode) @@ -39,51 +69,24 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) HFSPLUS_I(inode)->rsrc_inode = NULL; atomic_set(&HFSPLUS_I(inode)->opencnt, 0); - if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) { - read_inode: + if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID || + inode->i_ino == HFSPLUS_ROOT_CNID) { hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); if (!err) err = hfsplus_cat_read_inode(inode, &fd); hfs_find_exit(&fd); - if (err) - goto bad_inode; - goto done; + } else { + err = hfsplus_system_read_inode(inode); } - vhdr = HFSPLUS_SB(inode->i_sb)->s_vhdr; - switch(inode->i_ino) { - case HFSPLUS_ROOT_CNID: - goto read_inode; - case HFSPLUS_EXT_CNID: - hfsplus_inode_read_fork(inode, &vhdr->ext_file); - inode->i_mapping->a_ops = &hfsplus_btree_aops; - break; - case HFSPLUS_CAT_CNID: - hfsplus_inode_read_fork(inode, &vhdr->cat_file); - inode->i_mapping->a_ops = &hfsplus_btree_aops; - break; - case HFSPLUS_ALLOC_CNID: - hfsplus_inode_read_fork(inode, &vhdr->alloc_file); - inode->i_mapping->a_ops = &hfsplus_aops; - break; - case HFSPLUS_START_CNID: - hfsplus_inode_read_fork(inode, &vhdr->start_file); - break; - case HFSPLUS_ATTR_CNID: - hfsplus_inode_read_fork(inode, &vhdr->attr_file); - inode->i_mapping->a_ops = &hfsplus_btree_aops; - break; - default: - goto bad_inode; + + if (err) { + iget_failed(inode); + return ERR_PTR(err); } -done: unlock_new_inode(inode); return inode; - -bad_inode: - iget_failed(inode); - return ERR_PTR(err); } static int hfsplus_write_inode(struct inode *inode, -- cgit v1.2.3-18-g5258 From b5080f77ed2de3c8ac67a63044f8a781c75207d9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:43:43 +0200 Subject: hfsplus: clean up hfsplus_write_inode Add a new hfsplus_system_write_inode for writing the special system inodes and streamline the fastpath write_inode code. Signed-off-by: Christoph Hellwig --- fs/hfsplus/super.c | 80 +++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 43 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index bd09ea23435..e485a38b994 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -89,63 +89,57 @@ struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) return inode; } -static int hfsplus_write_inode(struct inode *inode, - struct writeback_control *wbc) +static int hfsplus_system_write_inode(struct inode *inode) { struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); - struct hfsplus_vh *vhdr; - int ret = 0; + struct hfsplus_vh *vhdr = sbi->s_vhdr; + struct hfsplus_fork_raw *fork; + struct hfs_btree *tree = NULL; - dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino); - hfsplus_ext_write_extent(inode); - if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) { - return hfsplus_cat_write_inode(inode); - } - vhdr = sbi->s_vhdr; switch (inode->i_ino) { - case HFSPLUS_ROOT_CNID: - ret = hfsplus_cat_write_inode(inode); - break; case HFSPLUS_EXT_CNID: - if (vhdr->ext_file.total_size != cpu_to_be64(inode->i_size)) { - sbi->flags |= HFSPLUS_SB_WRITEBACKUP; - inode->i_sb->s_dirt = 1; - } - hfsplus_inode_write_fork(inode, &vhdr->ext_file); - hfs_btree_write(sbi->ext_tree); + fork = &vhdr->ext_file; + tree = sbi->ext_tree; break; case HFSPLUS_CAT_CNID: - if (vhdr->cat_file.total_size != cpu_to_be64(inode->i_size)) { - sbi->flags |= HFSPLUS_SB_WRITEBACKUP; - inode->i_sb->s_dirt = 1; - } - hfsplus_inode_write_fork(inode, &vhdr->cat_file); - hfs_btree_write(sbi->cat_tree); + fork = &vhdr->cat_file; + tree = sbi->cat_tree; break; case HFSPLUS_ALLOC_CNID: - if (vhdr->alloc_file.total_size != cpu_to_be64(inode->i_size)) { - sbi->flags |= HFSPLUS_SB_WRITEBACKUP; - inode->i_sb->s_dirt = 1; - } - hfsplus_inode_write_fork(inode, &vhdr->alloc_file); + fork = &vhdr->alloc_file; break; case HFSPLUS_START_CNID: - if (vhdr->start_file.total_size != cpu_to_be64(inode->i_size)) { - sbi->flags |= HFSPLUS_SB_WRITEBACKUP; - inode->i_sb->s_dirt = 1; - } - hfsplus_inode_write_fork(inode, &vhdr->start_file); + fork = &vhdr->start_file; break; case HFSPLUS_ATTR_CNID: - if (vhdr->attr_file.total_size != cpu_to_be64(inode->i_size)) { - sbi->flags |= HFSPLUS_SB_WRITEBACKUP; - inode->i_sb->s_dirt = 1; - } - hfsplus_inode_write_fork(inode, &vhdr->attr_file); - hfs_btree_write(sbi->attr_tree); - break; + fork = &vhdr->attr_file; + tree = sbi->attr_tree; + default: + return -EIO; + } + + if (fork->total_size != cpu_to_be64(inode->i_size)) { + sbi->flags |= HFSPLUS_SB_WRITEBACKUP; + inode->i_sb->s_dirt = 1; } - return ret; + hfsplus_inode_write_fork(inode, fork); + if (tree) + hfs_btree_write(tree); + return 0; +} + +static int hfsplus_write_inode(struct inode *inode, + struct writeback_control *wbc) +{ + dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino); + + hfsplus_ext_write_extent(inode); + + if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID || + inode->i_ino == HFSPLUS_ROOT_CNID) + return hfsplus_cat_write_inode(inode); + else + return hfsplus_system_write_inode(inode); } static void hfsplus_evict_inode(struct inode *inode) -- cgit v1.2.3-18-g5258 From 30d3abbec730a5a9c954a6342271f7a7db155b08 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:43:50 +0200 Subject: hfsplus: merge mknod/mkdir/creat Make hfsplus_mkdir and hfsplus_create call hfsplus_mknod instead of duplicating the code. Signed-off-by: Christoph Hellwig --- fs/hfsplus/dir.c | 64 ++++++++++++++------------------------------------------ 1 file changed, 16 insertions(+), 48 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 5ca8308f9eb..7efcf75ea73 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -237,28 +237,6 @@ static int hfsplus_dir_release(struct inode *inode, struct file *file) return 0; } -static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode, - struct nameidata *nd) -{ - struct inode *inode; - int res; - - inode = hfsplus_new_inode(dir->i_sb, mode); - if (!inode) - return -ENOSPC; - - res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); - if (res) { - inode->i_nlink = 0; - hfsplus_delete_inode(inode); - iput(inode); - return res; - } - hfsplus_instantiate(dentry, inode, inode->i_ino); - mark_inode_dirty(inode); - return 0; -} - static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry) { @@ -365,27 +343,6 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) return res; } -static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - struct inode *inode; - int res; - - inode = hfsplus_new_inode(dir->i_sb, S_IFDIR | mode); - if (!inode) - return -ENOSPC; - - res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); - if (res) { - inode->i_nlink = 0; - hfsplus_delete_inode(inode); - iput(inode); - return res; - } - hfsplus_instantiate(dentry, inode, inode->i_ino); - mark_inode_dirty(inode); - return 0; -} - static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode; @@ -438,12 +395,10 @@ static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { - struct super_block *sb; struct inode *inode; int res; - sb = dir->i_sb; - inode = hfsplus_new_inode(sb, mode); + inode = hfsplus_new_inode(dir->i_sb, mode); if (!inode) return -ENOSPC; @@ -454,13 +409,26 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, iput(inode); return res; } - init_special_inode(inode, mode, rdev); + + if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) + init_special_inode(inode, mode, rdev); + hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); - return 0; } +static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + return hfsplus_mknod(dir, dentry, mode, 0); +} + +static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0); +} + static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { -- cgit v1.2.3-18-g5258 From f17c89bfcc9cccd405098eac3ec1ebfddf03279e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:43:54 +0200 Subject: hfsplus: fix error handling in hfsplus_symlink We need to free the inode again on a hfsplus_create_cat failure. Signed-off-by: Christoph Hellwig --- fs/hfsplus/dir.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 7efcf75ea73..f8ae468f4ab 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -364,31 +364,29 @@ static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { - struct super_block *sb; struct inode *inode; int res; - sb = dir->i_sb; - inode = hfsplus_new_inode(sb, S_IFLNK | S_IRWXUGO); + inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO); if (!inode) return -ENOSPC; res = page_symlink(inode, symname, strlen(symname) + 1); - if (res) { - inode->i_nlink = 0; - hfsplus_delete_inode(inode); - iput(inode); - return res; - } + if (res) + goto out_err; - mark_inode_dirty(inode); res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); + if (res) + goto out_err; - if (!res) { - hfsplus_instantiate(dentry, inode, inode->i_ino); - mark_inode_dirty(inode); - } + hfsplus_instantiate(dentry, inode, inode->i_ino); + mark_inode_dirty(inode); + return 0; +out_err: + inode->i_nlink = 0; + hfsplus_delete_inode(inode); + iput(inode); return res; } -- cgit v1.2.3-18-g5258 From 66e5db05bb6670f314d90aba5998e6a033e4d563 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:43:58 +0200 Subject: hfsplus: do not cache and write next_alloc We never look at it, nor change the next_alloc field in the superblock. So don't bother caching it or writing it out in hfsplus_sync_fs. Signed-off-by: Christoph Hellwig --- fs/hfsplus/hfsplus_fs.h | 1 - fs/hfsplus/super.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 169cef964b5..5e2418a7188 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -129,7 +129,6 @@ struct hfsplus_sb_info { int alloc_blksz_shift; u32 total_blocks; u32 free_blocks; - u32 next_alloc; u32 next_cnid; u32 file_count; u32 folder_count; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index e485a38b994..f310a1fdcd5 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -164,7 +164,6 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) sb->s_dirt = 0; vhdr->free_blocks = cpu_to_be32(sbi->free_blocks); - vhdr->next_alloc = cpu_to_be32(sbi->next_alloc); vhdr->next_cnid = cpu_to_be32(sbi->next_cnid); vhdr->folder_count = cpu_to_be32(sbi->folder_count); vhdr->file_count = cpu_to_be32(sbi->file_count); @@ -355,7 +354,6 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) } sbi->total_blocks = be32_to_cpu(vhdr->total_blocks); sbi->free_blocks = be32_to_cpu(vhdr->free_blocks); - sbi->next_alloc = be32_to_cpu(vhdr->next_alloc); sbi->next_cnid = be32_to_cpu(vhdr->next_cnid); sbi->file_count = be32_to_cpu(vhdr->file_count); sbi->folder_count = be32_to_cpu(vhdr->folder_count); -- cgit v1.2.3-18-g5258 From 58a818f532e83f337689358c102ba2048d1b37f5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:44:02 +0200 Subject: hfsplus: remove the rsrc_inodes list We never walk the list - the only reason for it is to make the resource fork inodes appear hashed to the writeback code. Borrow a trick from JFS to do that without needing a list head. Signed-off-by: Christoph Hellwig --- fs/hfsplus/hfsplus_fs.h | 2 -- fs/hfsplus/inode.c | 10 +++++++++- fs/hfsplus/super.c | 1 - 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 5e2418a7188..0cd9ba00f96 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -145,8 +145,6 @@ struct hfsplus_sb_info { int part, session; unsigned long flags; - - struct hlist_head rsrc_inodes; }; #define HFSPLUS_SB_WRITEBACKUP 0x0001 diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 309defb4f41..a05b3afa723 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -204,7 +204,15 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dent hip->rsrc_inode = dir; HFSPLUS_I(dir)->rsrc_inode = inode; igrab(dir); - hlist_add_head(&inode->i_hash, &HFSPLUS_SB(sb)->rsrc_inodes); + + /* + * __mark_inode_dirty expects inodes to be hashed. Since we don't + * want resource fork inodes in the regular inode space, we make them + * appear hashed, but do not put on any lists. hlist_del() + * will work fine and require no locking. + */ + inode->i_hash.pprev = &inode->i_hash.next; + mark_inode_dirty(inode); out: d_add(dentry, inode); diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index f310a1fdcd5..923f385b32c 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -318,7 +318,6 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) return -ENOMEM; sb->s_fs_info = sbi; - INIT_HLIST_HEAD(&sbi->rsrc_inodes); mutex_init(&sbi->alloc_mutex); hfsplus_fill_defaults(sbi); if (!hfsplus_parse_options(data, sbi)) { -- cgit v1.2.3-18-g5258 From 7ac9fb9c2a50963b699b3548e6f00698c1554dc6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:45:08 +0200 Subject: hfsplus: add per-superblock lock for volume header updates Lock updates to the mutal fields in the volume header, and document the locing in the hfsplus_sb_info structure. Signed-off-by: Christoph Hellwig --- fs/hfsplus/dir.c | 54 ++++++++++++++++++++++++++++++++----------------- fs/hfsplus/hfsplus_fs.h | 13 +++++++----- fs/hfsplus/super.c | 7 +++++++ 3 files changed, 50 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index f8ae468f4ab..1c81eedcab0 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -251,6 +251,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, if (HFSPLUS_IS_RSRC(inode)) return -EPERM; + mutex_lock(&sbi->vh_mutex); if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { for (;;) { get_random_bytes(&id, sizeof(cnid)); @@ -263,7 +264,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, if (!res) break; if (res != -EEXIST) - return res; + goto out; } HFSPLUS_I(inode)->dev = id; cnid = sbi->next_cnid++; @@ -271,13 +272,13 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); if (res) /* panic? */ - return res; + goto out; sbi->file_count++; } cnid = sbi->next_cnid++; res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); if (res) - return res; + goto out; inc_nlink(inode); hfsplus_instantiate(dst_dentry, inode, cnid); @@ -286,8 +287,9 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, mark_inode_dirty(inode); sbi->file_count++; dst_dir->i_sb->s_dirt = 1; - - return 0; +out: + mutex_unlock(&sbi->vh_mutex); + return res; } static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) @@ -302,6 +304,7 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) if (HFSPLUS_IS_RSRC(inode)) return -EPERM; + mutex_lock(&sbi->vh_mutex); cnid = (u32)(unsigned long)dentry->d_fsdata; if (inode->i_ino == cnid && atomic_read(&HFSPLUS_I(inode)->opencnt)) { @@ -312,11 +315,11 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) sbi->hidden_dir, &str); if (!res) inode->i_flags |= S_DEAD; - return res; + goto out; } res = hfsplus_delete_cat(cnid, dir, &dentry->d_name); if (res) - return res; + goto out; if (inode->i_nlink > 0) drop_nlink(inode); @@ -339,37 +342,44 @@ static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) sbi->file_count--; inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); - +out: + mutex_unlock(&sbi->vh_mutex); return res; } static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) { - struct inode *inode; + struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); + struct inode *inode = dentry->d_inode; int res; - inode = dentry->d_inode; if (inode->i_size != 2) return -ENOTEMPTY; + + mutex_lock(&sbi->vh_mutex); res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); if (res) - return res; + goto out; clear_nlink(inode); inode->i_ctime = CURRENT_TIME_SEC; hfsplus_delete_inode(inode); mark_inode_dirty(inode); - return 0; +out: + mutex_unlock(&sbi->vh_mutex); + return res; } static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); struct inode *inode; - int res; + int res = -ENOSPC; + mutex_lock(&sbi->vh_mutex); inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO); if (!inode) - return -ENOSPC; + goto out; res = page_symlink(inode, symname, strlen(symname) + 1); if (res) @@ -381,31 +391,35 @@ static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); - return 0; + goto out; out_err: inode->i_nlink = 0; hfsplus_delete_inode(inode); iput(inode); +out: + mutex_unlock(&sbi->vh_mutex); return res; } static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { + struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); struct inode *inode; - int res; + int res = -ENOSPC; + mutex_lock(&sbi->vh_mutex); inode = hfsplus_new_inode(dir->i_sb, mode); if (!inode) - return -ENOSPC; + goto out; res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(inode); iput(inode); - return res; + goto out; } if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) @@ -413,7 +427,9 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); - return 0; +out: + mutex_unlock(&sbi->vh_mutex); + return res; } static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode, diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 0cd9ba00f96..08865ed70f0 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -116,23 +116,26 @@ struct hfsplus_sb_info { struct inode *hidden_dir; struct nls_table *nls; - /* synchronize block allocations */ - struct mutex alloc_mutex; - /* Runtime variables */ u32 blockoffset; u32 sect_count; int fs_shift; - /* Stuff in host order from Vol Header */ + /* immutable data from the volume header */ u32 alloc_blksz; int alloc_blksz_shift; u32 total_blocks; + u32 data_clump_blocks, rsrc_clump_blocks; + + /* mutable data from the volume header, protected by alloc_mutex */ u32 free_blocks; + struct mutex alloc_mutex; + + /* mutable data from the volume header, protected by vh_mutex */ u32 next_cnid; u32 file_count; u32 folder_count; - u32 data_clump_blocks, rsrc_clump_blocks; + struct mutex vh_mutex; /* Config options */ u32 creator; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 923f385b32c..b766c170e4d 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -160,6 +160,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) dprint(DBG_SUPER, "hfsplus_write_super\n"); + mutex_lock(&sbi->vh_mutex); mutex_lock(&sbi->alloc_mutex); sb->s_dirt = 0; @@ -194,6 +195,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) sbi->flags &= ~HFSPLUS_SB_WRITEBACKUP; } mutex_unlock(&sbi->alloc_mutex); + mutex_unlock(&sbi->vh_mutex); return 0; } @@ -319,6 +321,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) sb->s_fs_info = sbi; mutex_init(&sbi->alloc_mutex); + mutex_init(&sbi->vh_mutex); hfsplus_fill_defaults(sbi); if (!hfsplus_parse_options(data, sbi)) { printk(KERN_ERR "hfs: unable to parse mount options\n"); @@ -453,9 +456,13 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) if (!sbi->hidden_dir) { printk(KERN_DEBUG "hfs: create hidden dir...\n"); + + mutex_lock(&sbi->vh_mutex); sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); hfsplus_create_cat(sbi->hidden_dir->i_ino, sb->s_root->d_inode, &str, sbi->hidden_dir); + mutex_unlock(&sbi->vh_mutex); + mark_inode_dirty(sbi->hidden_dir); } out: -- cgit v1.2.3-18-g5258 From 84adede31267af37141da2b2b04293c5ea8af7ae Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:45:20 +0200 Subject: hfsplus: use atomic bitops for the superblock flags The flags in the HFS+-specific superlock do get modified during runtime, use atomic bitops to make the modifications SMP safe. Signed-off-by: Christoph Hellwig --- fs/hfsplus/btree.c | 4 ++-- fs/hfsplus/hfsplus_fs.h | 10 +++++----- fs/hfsplus/options.c | 8 ++++---- fs/hfsplus/super.c | 10 ++++------ fs/hfsplus/unicode.c | 12 ++++++------ fs/hfsplus/wrapper.c | 14 +++++++++----- 6 files changed, 30 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index f75cf222b8b..27504f57a48 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -61,12 +61,12 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) if (id == HFSPLUS_EXT_CNID) { tree->keycmp = hfsplus_ext_cmp_key; } else if (id == HFSPLUS_CAT_CNID) { - if ((HFSPLUS_SB(sb)->flags & HFSPLUS_SB_HFSX) && + if (test_bit(HFSPLUS_SB_HFSX, &HFSPLUS_SB(sb)->flags) && (head->key_type == HFSPLUS_KEY_BINARY)) tree->keycmp = hfsplus_cat_bin_cmp_key; else { tree->keycmp = hfsplus_cat_case_cmp_key; - HFSPLUS_SB(sb)->flags |= HFSPLUS_SB_CASEFOLD; + set_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags); } } else { printk(KERN_ERR "hfs: unknown B*Tree requested\n"); diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 08865ed70f0..c521d44f074 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -150,11 +150,11 @@ struct hfsplus_sb_info { unsigned long flags; }; -#define HFSPLUS_SB_WRITEBACKUP 0x0001 -#define HFSPLUS_SB_NODECOMPOSE 0x0002 -#define HFSPLUS_SB_FORCE 0x0004 -#define HFSPLUS_SB_HFSX 0x0008 -#define HFSPLUS_SB_CASEFOLD 0x0010 +#define HFSPLUS_SB_WRITEBACKUP 0 +#define HFSPLUS_SB_NODECOMPOSE 1 +#define HFSPLUS_SB_FORCE 2 +#define HFSPLUS_SB_HFSX 3 +#define HFSPLUS_SB_CASEFOLD 4 struct hfsplus_inode_info { diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c index f2598aef206..f9ab276a4d8 100644 --- a/fs/hfsplus/options.c +++ b/fs/hfsplus/options.c @@ -143,13 +143,13 @@ int hfsplus_parse_options(char *input, struct hfsplus_sb_info *sbi) kfree(p); break; case opt_decompose: - sbi->flags &= ~HFSPLUS_SB_NODECOMPOSE; + clear_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags); break; case opt_nodecompose: - sbi->flags |= HFSPLUS_SB_NODECOMPOSE; + set_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags); break; case opt_force: - sbi->flags |= HFSPLUS_SB_FORCE; + set_bit(HFSPLUS_SB_FORCE, &sbi->flags); break; default: return 0; @@ -184,7 +184,7 @@ int hfsplus_show_options(struct seq_file *seq, struct vfsmount *mnt) seq_printf(seq, ",session=%u", sbi->session); if (sbi->nls) seq_printf(seq, ",nls=%s", sbi->nls->charset); - if (sbi->flags & HFSPLUS_SB_NODECOMPOSE) + if (test_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags)) seq_printf(seq, ",nodecompose"); return 0; } diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index b766c170e4d..9a88d753610 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -119,7 +119,7 @@ static int hfsplus_system_write_inode(struct inode *inode) } if (fork->total_size != cpu_to_be64(inode->i_size)) { - sbi->flags |= HFSPLUS_SB_WRITEBACKUP; + set_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags); inode->i_sb->s_dirt = 1; } hfsplus_inode_write_fork(inode, fork); @@ -170,7 +170,7 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) vhdr->file_count = cpu_to_be32(sbi->file_count); mark_buffer_dirty(sbi->s_vhbh); - if (sbi->flags & HFSPLUS_SB_WRITEBACKUP) { + if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) { if (sbi->sect_count) { struct buffer_head *bh; u32 block, offset; @@ -192,7 +192,6 @@ int hfsplus_sync_fs(struct super_block *sb, int wait) printk(KERN_WARNING "hfs: backup not found!\n"); } } - sbi->flags &= ~HFSPLUS_SB_WRITEBACKUP; } mutex_unlock(&sbi->alloc_mutex); mutex_unlock(&sbi->vh_mutex); @@ -276,7 +275,7 @@ static int hfsplus_remount(struct super_block *sb, int *flags, char *data) "running fsck.hfsplus is recommended. leaving read-only.\n"); sb->s_flags |= MS_RDONLY; *flags |= MS_RDONLY; - } else if (sbi.flags & HFSPLUS_SB_FORCE) { + } else if (test_bit(HFSPLUS_SB_FORCE, &sbi.flags)) { /* nothing */ } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { printk(KERN_WARNING "hfs: filesystem is marked locked, leaving read-only.\n"); @@ -376,7 +375,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) printk(KERN_WARNING "hfs: Filesystem was not cleanly unmounted, " "running fsck.hfsplus is recommended. mounting read-only.\n"); sb->s_flags |= MS_RDONLY; - } else if (sbi->flags & HFSPLUS_SB_FORCE) { + } else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) { /* nothing */ } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n"); @@ -386,7 +385,6 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) "use the force option at your own risk, mounting read-only.\n"); sb->s_flags |= MS_RDONLY; } - sbi->flags &= ~HFSPLUS_SB_FORCE; /* Load metadata objects (B*Trees) */ sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID); diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index a15fcffce74..b66d67de882 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -132,7 +132,7 @@ int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, c ustrlen = be16_to_cpu(ustr->length); len = *len_p; ce1 = NULL; - compose = !(HFSPLUS_SB(sb)->flags & HFSPLUS_SB_NODECOMPOSE); + compose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); while (ustrlen > 0) { c0 = be16_to_cpu(*ip++); @@ -293,7 +293,7 @@ int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, u16 *dstr, outlen = 0; wchar_t c; - decompose = !(HFSPLUS_SB(sb)->flags & HFSPLUS_SB_NODECOMPOSE); + decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); while (outlen < HFSPLUS_MAX_STRLEN && len > 0) { size = asc2unichar(sb, astr, len, &c); @@ -330,8 +330,8 @@ int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str) wchar_t c; u16 c2; - casefold = (HFSPLUS_SB(sb)->flags & HFSPLUS_SB_CASEFOLD); - decompose = !(HFSPLUS_SB(sb)->flags & HFSPLUS_SB_NODECOMPOSE); + casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags); + decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); hash = init_name_hash(); astr = str->name; len = str->len; @@ -373,8 +373,8 @@ int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr * u16 c1, c2; wchar_t c; - casefold = (HFSPLUS_SB(sb)->flags & HFSPLUS_SB_CASEFOLD); - decompose = !(HFSPLUS_SB(sb)->flags & HFSPLUS_SB_NODECOMPOSE); + casefold = test_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags); + decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); astr1 = s1->name; len1 = s1->len; astr2 = s2->name; diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c index c89e198e8a2..8972c20b321 100644 --- a/fs/hfsplus/wrapper.c +++ b/fs/hfsplus/wrapper.c @@ -123,7 +123,7 @@ int hfsplus_read_wrapper(struct super_block *sb) if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG)) break; if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) { - sbi->flags |= HFSPLUS_SB_HFSX; + set_bit(HFSPLUS_SB_HFSX, &sbi->flags); break; } brelse(bh); @@ -169,10 +169,14 @@ int hfsplus_read_wrapper(struct super_block *sb) return -EIO; /* should still be the same... */ - if (vhdr->signature != (sbi->flags & HFSPLUS_SB_HFSX ? - cpu_to_be16(HFSPLUS_VOLHEAD_SIGX) : - cpu_to_be16(HFSPLUS_VOLHEAD_SIG))) - goto error; + if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) { + if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) + goto error; + } else { + if (vhdr->signature != cpu_to_be16(HFSPLUS_VOLHEAD_SIG)) + goto error; + } + sbi->s_vhbh = bh; sbi->s_vhdr = vhdr; -- cgit v1.2.3-18-g5258 From 89755dcace09b44b3aa024bf302d9b19b4c24cad Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:45:25 +0200 Subject: hfsplus: protect readdir against removals from open_dir_list We already have i_mutex for readdir and the namespace operations that add entries to open_dir_list, the only thing that was missing was the removal in hfsplus_dir_release. Signed-off-by: Christoph Hellwig --- fs/hfsplus/dir.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 1c81eedcab0..93fa45cc481 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -231,7 +231,9 @@ static int hfsplus_dir_release(struct inode *inode, struct file *file) { struct hfsplus_readdir_data *rd = file->private_data; if (rd) { + mutex_lock(&inode->i_mutex); list_del(&rd->list); + mutex_unlock(&inode->i_mutex); kfree(rd); } return 0; -- cgit v1.2.3-18-g5258 From 7fcc99f4f2ddb1c39abc05fbb9b32f05b03c7f8f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 05:46:31 +0200 Subject: hfsplus: add missing extent locking in hfsplus_write_inode Most of the extent handling code already does proper SMP locking, but hfsplus_write_inode was calling into hfsplus_ext_write_extent without taking the extents_lock. Fix this by splitting hfsplus_ext_write_extent into an internal helper that expects the lock, and a public interface that first acquires it. Also add a few locking asserts and document the locking rules in hfsplus_fs.h. Signed-off-by: Christoph Hellwig --- fs/hfsplus/extents.c | 15 +++++++++++++-- fs/hfsplus/hfsplus_fs.h | 34 +++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c index b1017eaa4fd..0c9cb1820a5 100644 --- a/fs/hfsplus/extents.c +++ b/fs/hfsplus/extents.c @@ -88,6 +88,8 @@ static void __hfsplus_ext_write_extent(struct inode *inode, struct hfs_find_data struct hfsplus_inode_info *hip = HFSPLUS_I(inode); int res; + WARN_ON(!mutex_is_locked(&hip->extents_lock)); + hfsplus_ext_build_key(fd->search_key, inode->i_ino, hip->cached_start, HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); @@ -108,7 +110,7 @@ static void __hfsplus_ext_write_extent(struct inode *inode, struct hfs_find_data } } -void hfsplus_ext_write_extent(struct inode *inode) +static void hfsplus_ext_write_extent_locked(struct inode *inode) { if (HFSPLUS_I(inode)->flags & HFSPLUS_FLG_EXT_DIRTY) { struct hfs_find_data fd; @@ -119,6 +121,13 @@ void hfsplus_ext_write_extent(struct inode *inode) } } +void hfsplus_ext_write_extent(struct inode *inode) +{ + mutex_lock(&HFSPLUS_I(inode)->extents_lock); + hfsplus_ext_write_extent_locked(inode); + mutex_unlock(&HFSPLUS_I(inode)->extents_lock); +} + static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd, struct hfsplus_extent *extent, u32 cnid, u32 block, u8 type) @@ -144,6 +153,8 @@ static inline int __hfsplus_ext_cache_extent(struct hfs_find_data *fd, struct in struct hfsplus_inode_info *hip = HFSPLUS_I(inode); int res; + WARN_ON(!mutex_is_locked(&hip->extents_lock)); + if (hip->flags & HFSPLUS_FLG_EXT_DIRTY) __hfsplus_ext_write_extent(inode, fd); @@ -433,7 +444,7 @@ out: insert_extent: dprint(DBG_EXTENT, "insert new extent\n"); - hfsplus_ext_write_extent(inode); + hfsplus_ext_write_extent_locked(inode); memset(hip->cached_extents, 0, sizeof(hfsplus_extent_rec)); hip->cached_extents[0].start_block = cpu_to_be32(start); diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index c521d44f074..c007cc20127 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -158,28 +158,36 @@ struct hfsplus_sb_info { struct hfsplus_inode_info { - struct mutex extents_lock; - u32 clump_blocks, alloc_blocks; - sector_t fs_blocks; - /* Allocation extents from catalog record or volume header */ - hfsplus_extent_rec first_extents; - u32 first_blocks; - hfsplus_extent_rec cached_extents; - u32 cached_start, cached_blocks; atomic_t opencnt; - struct inode *rsrc_inode; + /* + * Extent allocation information, protected by extents_lock. + */ + u32 first_blocks; + u32 clump_blocks; + u32 alloc_blocks; + u32 cached_start; + u32 cached_blocks; + hfsplus_extent_rec first_extents; + hfsplus_extent_rec cached_extents; unsigned long flags; + struct mutex extents_lock; + /* + * Immutable data. + */ + struct inode *rsrc_inode; __be32 create_date; - /* Device number in hfsplus_permissions in catalog */ u32 dev; - /* BSD system and user file flags */ - u8 rootflags; - u8 userflags; + /* + * Protected by i_mutex. + */ + sector_t fs_blocks; + u8 rootflags, userflags; /* BSD system and user file flags */ struct list_head open_dir_list; loff_t phys_size; + struct inode vfs_inode; }; -- cgit v1.2.3-18-g5258 From 467c3d9cd541eef284ff8118069b088e015b8d6a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 1 Oct 2010 05:46:52 +0200 Subject: hfsplus: convert tree_lock to mutex tree_lock is used as mutex so make it a mutex. Signed-off-by: Thomas Gleixner Signed-off-by: Christoph Hellwig --- fs/hfsplus/bfind.c | 4 ++-- fs/hfsplus/btree.c | 2 +- fs/hfsplus/hfsplus_fs.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c index 5007a41f1be..68c7983f028 100644 --- a/fs/hfsplus/bfind.c +++ b/fs/hfsplus/bfind.c @@ -23,7 +23,7 @@ int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) fd->search_key = ptr; fd->key = ptr + tree->max_key_len + 2; dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n", tree->cnid, __builtin_return_address(0)); - down(&tree->tree_lock); + mutex_lock(&tree->tree_lock); return 0; } @@ -32,7 +32,7 @@ void hfs_find_exit(struct hfs_find_data *fd) hfs_bnode_put(fd->bnode); kfree(fd->search_key); dprint(DBG_BNODE_REFS, "find_exit: %d (%p)\n", fd->tree->cnid, __builtin_return_address(0)); - up(&fd->tree->tree_lock); + mutex_unlock(&fd->tree->tree_lock); fd->tree = NULL; } diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 27504f57a48..8306479f6b3 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -30,7 +30,7 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) if (!tree) return NULL; - init_MUTEX(&tree->tree_lock); + mutex_init(&tree->tree_lock); spin_lock_init(&tree->hash_lock); tree->sb = sb; tree->cnid = id; diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index c007cc20127..5cda96366ac 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -62,7 +62,7 @@ struct hfs_btree { unsigned int depth; //unsigned int map1_size, map_size; - struct semaphore tree_lock; + struct mutex tree_lock; unsigned int pages_per_bnode; spinlock_t hash_lock; -- cgit v1.2.3-18-g5258 From 40de9a7cebc4e0b23cd6863c84c2279f0ccadebb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 1 Oct 2010 09:12:08 +0200 Subject: hfsplus: fix rename over directories When renaming over a directory we need to use hfsplus_rmdir instead of hfsplus_unlink to evict the victim. This makes sure we properly error out on non-empty directory as required by Posix (BZ #16571), and it also makes sure we do the right thing in case i_nlink will every be set correctly for directories on hfsplus. Reported-by: Vlado Plaga Signed-off-by: Christoph Hellwig --- fs/hfsplus/dir.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 93fa45cc481..33aab211695 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -452,7 +452,10 @@ static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry, /* Unlink destination if it already exists */ if (new_dentry->d_inode) { - res = hfsplus_unlink(new_dir, new_dentry); + if (S_ISDIR(new_dentry->d_inode->i_mode)) + res = hfsplus_rmdir(new_dir, new_dentry); + else + res = hfsplus_unlink(new_dir, new_dentry); if (res) return res; } -- cgit v1.2.3-18-g5258 From f569599ae70f0899035f8d5876a7939f629c5976 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 29 Sep 2010 15:27:08 -0400 Subject: cifs: prevent infinite recursion in cifs_reconnect_tcon cifs_reconnect_tcon is called from smb_init. After a successful reconnect, cifs_reconnect_tcon will call reset_cifs_unix_caps. That function will, in turn call CIFSSMBQFSUnixInfo and CIFSSMBSetFSUnixInfo. Those functions also call smb_init. It's possible for the session and tcon reconnect to succeed, and then for another cifs_reconnect to occur before CIFSSMBQFSUnixInfo or CIFSSMBSetFSUnixInfo to be called. That'll cause those functions to call smb_init and cifs_reconnect_tcon again, ad infinitum... Break the infinite recursion by having those functions use a new smb_init variant that doesn't attempt to perform a reconnect. Reported-and-Tested-by: Michal Suchanek Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index c65c3419dd3..7e83b356cc9 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -232,7 +232,7 @@ static int small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, void **request_buf) { - int rc = 0; + int rc; rc = cifs_reconnect_tcon(tcon, smb_command); if (rc) @@ -250,7 +250,7 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, if (tcon != NULL) cifs_stats_inc(&tcon->num_smbs_sent); - return rc; + return 0; } int @@ -281,16 +281,9 @@ small_smb_init_no_tc(const int smb_command, const int wct, /* If the return code is zero, this function must fill in request_buf pointer */ static int -smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, - void **request_buf /* returned */ , - void **response_buf /* returned */ ) +__smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, + void **request_buf, void **response_buf) { - int rc = 0; - - rc = cifs_reconnect_tcon(tcon, smb_command); - if (rc) - return rc; - *request_buf = cifs_buf_get(); if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ @@ -309,7 +302,31 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, if (tcon != NULL) cifs_stats_inc(&tcon->num_smbs_sent); - return rc; + return 0; +} + +/* If the return code is zero, this function must fill in request_buf pointer */ +static int +smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, + void **request_buf, void **response_buf) +{ + int rc; + + rc = cifs_reconnect_tcon(tcon, smb_command); + if (rc) + return rc; + + return __smb_init(smb_command, wct, tcon, request_buf, response_buf); +} + +static int +smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon, + void **request_buf, void **response_buf) +{ + if (tcon->ses->need_reconnect || tcon->need_reconnect) + return -EHOSTDOWN; + + return __smb_init(smb_command, wct, tcon, request_buf, response_buf); } static int validate_t2(struct smb_t2_rsp *pSMB) @@ -4534,8 +4551,8 @@ CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon) cFYI(1, "In QFSUnixInfo"); QFSUnixRetry: - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); + rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon, + (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; @@ -4604,8 +4621,8 @@ CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap) cFYI(1, "In SETFSUnixInfo"); SETFSUnixRetry: /* BB switch to small buf init to save memory */ - rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, - (void **) &pSMBr); + rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon, + (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; -- cgit v1.2.3-18-g5258 From 3036e7b490bf7878c6dae952eec5fb87b1106589 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 30 Sep 2010 15:15:33 -0700 Subject: proc: make /proc/pid/limits world readable Having the limits file world readable will ease the task of system management on systems where root privileges might be restricted. Having admin restricted with root priviledges, he/she could not check other users process' limits. Also it'd align with most of the /proc stat files. Signed-off-by: Jiri Olsa Acked-by: Neil Horman Cc: Eugene Teo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/proc/base.c b/fs/proc/base.c index a1c43e7c8a7..8e4addaa542 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2675,7 +2675,7 @@ static const struct pid_entry tgid_base_stuff[] = { INF("auxv", S_IRUSR, proc_pid_auxv), ONE("status", S_IRUGO, proc_pid_status), ONE("personality", S_IRUSR, proc_pid_personality), - INF("limits", S_IRUSR, proc_pid_limits), + INF("limits", S_IRUGO, proc_pid_limits), #ifdef CONFIG_SCHED_DEBUG REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), #endif @@ -3011,7 +3011,7 @@ static const struct pid_entry tid_base_stuff[] = { INF("auxv", S_IRUSR, proc_pid_auxv), ONE("status", S_IRUGO, proc_pid_status), ONE("personality", S_IRUSR, proc_pid_personality), - INF("limits", S_IRUSR, proc_pid_limits), + INF("limits", S_IRUGO, proc_pid_limits), #ifdef CONFIG_SCHED_DEBUG REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), #endif -- cgit v1.2.3-18-g5258 From 3f259d092c7a2fdf217823e8f1838530adb0cdb0 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 30 Sep 2010 15:15:37 -0700 Subject: reiserfs: fix dependency inversion between inode and reiserfs mutexes The reiserfs mutex already depends on the inode mutex, so we can't lock the inode mutex in reiserfs_unpack() without using the safe locking API, because reiserfs_unpack() is always called with the reiserfs mutex locked. This fixes: ======================================================= [ INFO: possible circular locking dependency detected ] 2.6.35c #13 ------------------------------------------------------- lilo/1606 is trying to acquire lock: (&sb->s_type->i_mutex_key#8){+.+.+.}, at: [] reiserfs_unpack+0x60/0x110 [reiserfs] but task is already holding lock: (&REISERFS_SB(s)->lock){+.+.+.}, at: [] reiserfs_write_lock+0x28/0x40 [reiserfs] which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&REISERFS_SB(s)->lock){+.+.+.}: [] lock_acquire+0x67/0x80 [] __mutex_lock_common+0x4d/0x410 [] mutex_lock_nested+0x18/0x20 [] reiserfs_write_lock+0x28/0x40 [reiserfs] [] reiserfs_lookup_privroot+0x2a/0x90 [reiserfs] [] reiserfs_fill_super+0x941/0xe60 [reiserfs] [] get_sb_bdev+0x117/0x170 [] get_super_block+0x21/0x30 [reiserfs] [] vfs_kern_mount+0x6a/0x1b0 [] do_kern_mount+0x39/0xe0 [] do_mount+0x340/0x790 [] sys_mount+0x84/0xb0 [] syscall_call+0x7/0xb -> #0 (&sb->s_type->i_mutex_key#8){+.+.+.}: [] __lock_acquire+0x1026/0x1180 [] lock_acquire+0x67/0x80 [] __mutex_lock_common+0x4d/0x410 [] mutex_lock_nested+0x18/0x20 [] reiserfs_unpack+0x60/0x110 [reiserfs] [] reiserfs_ioctl+0x272/0x320 [reiserfs] [] vfs_ioctl+0x28/0xa0 [] do_vfs_ioctl+0x32d/0x5c0 [] sys_ioctl+0x63/0x70 [] syscall_call+0x7/0xb other info that might help us debug this: 1 lock held by lilo/1606: #0: (&REISERFS_SB(s)->lock){+.+.+.}, at: [] reiserfs_write_lock+0x28/0x40 [reiserfs] stack backtrace: Pid: 1606, comm: lilo Not tainted 2.6.35c #13 Call Trace: [] __lock_acquire+0x1026/0x1180 [] lock_acquire+0x67/0x80 [] __mutex_lock_common+0x4d/0x410 [] mutex_lock_nested+0x18/0x20 [] reiserfs_unpack+0x60/0x110 [reiserfs] [] reiserfs_ioctl+0x272/0x320 [reiserfs] [] vfs_ioctl+0x28/0xa0 [] do_vfs_ioctl+0x32d/0x5c0 [] sys_ioctl+0x63/0x70 [] syscall_call+0x7/0xb Reported-by: Jarek Poplawski Tested-by: Jarek Poplawski Signed-off-by: Frederic Weisbecker Cc: Jeff Mahoney Cc: [2.6.32 and later] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c index f53505de071..679d5029f50 100644 --- a/fs/reiserfs/ioctl.c +++ b/fs/reiserfs/ioctl.c @@ -188,7 +188,7 @@ int reiserfs_unpack(struct inode *inode, struct file *filp) /* we need to make sure nobody is changing the file size beneath ** us */ - mutex_lock(&inode->i_mutex); + reiserfs_mutex_lock_safe(&inode->i_mutex, inode->i_sb); reiserfs_write_lock(inode->i_sb); write_from = inode->i_size & (blocksize - 1); -- cgit v1.2.3-18-g5258 From 9d8117e72bf453dd9d85e0cd322ce4a0f8bccbc0 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 30 Sep 2010 15:15:38 -0700 Subject: reiserfs: fix unwanted reiserfs lock recursion Prevent from recursively locking the reiserfs lock in reiserfs_unpack() because we may call journal_begin() that requires the lock to be taken only once, otherwise it won't be able to release the lock while taking other mutexes, ending up in inverted dependencies between the journal mutex and the reiserfs lock for example. This fixes: ======================================================= [ INFO: possible circular locking dependency detected ] 2.6.35.4.4a #3 ------------------------------------------------------- lilo/1620 is trying to acquire lock: (&journal->j_mutex){+.+...}, at: [] do_journal_begin_r+0x7f/0x340 [reiserfs] but task is already holding lock: (&REISERFS_SB(s)->lock){+.+.+.}, at: [] reiserfs_write_lock+0x28/0x40 [reiserfs] which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&REISERFS_SB(s)->lock){+.+.+.}: [] lock_acquire+0x67/0x80 [] __mutex_lock_common+0x4d/0x410 [] mutex_lock_nested+0x18/0x20 [] reiserfs_write_lock+0x28/0x40 [reiserfs] [] do_journal_begin_r+0x86/0x340 [reiserfs] [] journal_begin+0x77/0x140 [reiserfs] [] reiserfs_remount+0x224/0x530 [reiserfs] [] do_remount_sb+0x60/0x110 [] do_mount+0x625/0x790 [] sys_mount+0x84/0xb0 [] syscall_call+0x7/0xb -> #0 (&journal->j_mutex){+.+...}: [] __lock_acquire+0x1026/0x1180 [] lock_acquire+0x67/0x80 [] __mutex_lock_common+0x4d/0x410 [] mutex_lock_nested+0x18/0x20 [] do_journal_begin_r+0x7f/0x340 [reiserfs] [] journal_begin+0x77/0x140 [reiserfs] [] reiserfs_persistent_transaction+0x41/0x90 [reiserfs] [] reiserfs_get_block+0x22c/0x1530 [reiserfs] [] __block_prepare_write+0x1bb/0x3a0 [] block_prepare_write+0x26/0x40 [] reiserfs_prepare_write+0x88/0x170 [reiserfs] [] reiserfs_unpack+0xe6/0x120 [reiserfs] [] reiserfs_ioctl+0x272/0x320 [reiserfs] [] vfs_ioctl+0x28/0xa0 [] do_vfs_ioctl+0x32d/0x5c0 [] sys_ioctl+0x63/0x70 [] syscall_call+0x7/0xb other info that might help us debug this: 2 locks held by lilo/1620: #0: (&sb->s_type->i_mutex_key#8){+.+.+.}, at: [] reiserfs_unpack+0x6a/0x120 [reiserfs] #1: (&REISERFS_SB(s)->lock){+.+.+.}, at: [] reiserfs_write_lock+0x28/0x40 [reiserfs] stack backtrace: Pid: 1620, comm: lilo Not tainted 2.6.35.4.4a #3 Call Trace: [] __lock_acquire+0x1026/0x1180 [] lock_acquire+0x67/0x80 [] __mutex_lock_common+0x4d/0x410 [] mutex_lock_nested+0x18/0x20 [] do_journal_begin_r+0x7f/0x340 [reiserfs] [] journal_begin+0x77/0x140 [reiserfs] [] reiserfs_persistent_transaction+0x41/0x90 [reiserfs] [] reiserfs_get_block+0x22c/0x1530 [reiserfs] [] __block_prepare_write+0x1bb/0x3a0 [] block_prepare_write+0x26/0x40 [] reiserfs_prepare_write+0x88/0x170 [reiserfs] [] reiserfs_unpack+0xe6/0x120 [reiserfs] [] reiserfs_ioctl+0x272/0x320 [reiserfs] [] vfs_ioctl+0x28/0xa0 [] do_vfs_ioctl+0x32d/0x5c0 [] sys_ioctl+0x63/0x70 [] syscall_call+0x7/0xb Reported-by: Jarek Poplawski Tested-by: Jarek Poplawski Signed-off-by: Frederic Weisbecker Cc: Jeff Mahoney Cc: All since 2.6.32 Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/ioctl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c index 679d5029f50..5cbb81e134a 100644 --- a/fs/reiserfs/ioctl.c +++ b/fs/reiserfs/ioctl.c @@ -170,6 +170,7 @@ int reiserfs_prepare_write(struct file *f, struct page *page, int reiserfs_unpack(struct inode *inode, struct file *filp) { int retval = 0; + int depth; int index; struct page *page; struct address_space *mapping; @@ -189,7 +190,7 @@ int reiserfs_unpack(struct inode *inode, struct file *filp) ** us */ reiserfs_mutex_lock_safe(&inode->i_mutex, inode->i_sb); - reiserfs_write_lock(inode->i_sb); + depth = reiserfs_write_lock_once(inode->i_sb); write_from = inode->i_size & (blocksize - 1); /* if we are on a block boundary, we are already unpacked. */ @@ -224,6 +225,6 @@ int reiserfs_unpack(struct inode *inode, struct file *filp) out: mutex_unlock(&inode->i_mutex); - reiserfs_write_unlock(inode->i_sb); + reiserfs_write_unlock_once(inode->i_sb, depth); return retval; } -- cgit v1.2.3-18-g5258 From 0157443c56bcc50be4933ebdff3ece723dfd535c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 30 Sep 2010 22:06:21 +0200 Subject: fuse: Initialize total_len in fuse_retrieve() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fs/fuse/dev.c:1357: warning: ‘total_len’ may be used uninitialized in this function Initialize total_len to zero, else its value will be undefined. Signed-off-by: Geert Uytterhoeven Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index d367af1514e..cde755cca56 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1354,7 +1354,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, loff_t file_size; unsigned int num; unsigned int offset; - size_t total_len; + size_t total_len = 0; req = fuse_get_req(fc); if (IS_ERR(req)) -- cgit v1.2.3-18-g5258 From aaead25b954879e1a708ff2f3602f494c18d20b5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 4 Oct 2010 14:25:33 +0200 Subject: writeback: always use sb->s_bdi for writeback purposes We currently use struct backing_dev_info for various different purposes. Originally it was introduced to describe a backing device which includes an unplug and congestion function and various bits of readahead information and VM-relevant flags. We're also using for tracking dirty inodes for writeback. To make writeback properly find all inodes we need to only access the per-filesystem backing_device pointed to by the superblock in ->s_bdi inside the writeback code, and not the instances pointeded to by inode->i_mapping->backing_dev which can be overriden by special devices or might not be set at all by some filesystems. Long term we should split out the writeback-relevant bits of struct backing_device_info (which includes more than the current bdi_writeback) and only point to it from the superblock while leaving the traditional backing device as a separate structure that can be overriden by devices. The one exception for now is the block device filesystem which really wants different writeback contexts for it's different (internal) inodes to handle the writeout more efficiently. For now we do this with a hack in fs-writeback.c because we're so late in the cycle, but in the future I plan to replace this with a superblock method that allows for multiple writeback contexts per filesystem. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 5581122bd2c..ab38fef1c9a 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -72,22 +72,11 @@ int writeback_in_progress(struct backing_dev_info *bdi) static inline struct backing_dev_info *inode_to_bdi(struct inode *inode) { struct super_block *sb = inode->i_sb; - struct backing_dev_info *bdi = inode->i_mapping->backing_dev_info; - /* - * For inodes on standard filesystems, we use superblock's bdi. For - * inodes on virtual filesystems, we want to use inode mapping's bdi - * because they can possibly point to something useful (think about - * block_dev filesystem). - */ - if (sb->s_bdi && sb->s_bdi != &noop_backing_dev_info) { - /* Some device inodes could play dirty tricks. Catch them... */ - WARN(bdi != sb->s_bdi && bdi_cap_writeback_dirty(bdi), - "Dirtiable inode bdi %s != sb bdi %s\n", - bdi->name, sb->s_bdi->name); - return sb->s_bdi; - } - return bdi; + if (strcmp(sb->s_type->name, "bdev") == 0) + return inode->i_mapping->backing_dev_info; + + return sb->s_bdi; } static void bdi_queue_work(struct backing_dev_info *bdi, -- cgit v1.2.3-18-g5258 From db71922217a214e5c9268448e537b54fc1f301ea Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Sun, 15 Aug 2010 22:51:10 +0200 Subject: BKL: Explicitly add BKL around get_sb/fill_super This patch is a preparation necessary to remove the BKL from do_new_mount(). It explicitly adds calls to lock_kernel()/unlock_kernel() around get_sb/fill_super operations for filesystems that still uses the BKL. I've read through all the code formerly covered by the BKL inside do_kern_mount() and have satisfied myself that it doesn't need the BKL any more. do_kern_mount() is already called without the BKL when mounting the rootfs and in nfsctl. do_kern_mount() calls vfs_kern_mount(), which is called from various places without BKL: simple_pin_fs(), nfs_do_clone_mount() through nfs_follow_mountpoint(), afs_mntpt_do_automount() through afs_mntpt_follow_link(). Both later functions are actually the filesystems follow_link inode operation. vfs_kern_mount() is calling the specified get_sb function and lets the filesystem do its job by calling the given fill_super function. Therefore I think it is safe to push down the BKL from the VFS to the low-level filesystems get_sb/fill_super operation. [arnd: do not add the BKL to those file systems that already don't use it elsewhere] Signed-off-by: Jan Blunck Signed-off-by: Arnd Bergmann Cc: Matthew Wilcox Cc: Christoph Hellwig --- fs/adfs/super.c | 8 +++++++- fs/affs/super.c | 9 ++++++++- fs/afs/super.c | 5 +++++ fs/bfs/inode.c | 8 +++++++- fs/cifs/cifsfs.c | 12 ++++++++++-- fs/coda/inode.c | 8 +++++++- fs/ecryptfs/main.c | 4 ++++ fs/ext2/super.c | 10 ++++++++-- fs/ext3/super.c | 9 ++++++++- fs/ext4/super.c | 8 ++++++-- fs/fat/namei_msdos.c | 7 ++++++- fs/fat/namei_vfat.c | 7 ++++++- fs/freevxfs/vxfs_super.c | 7 ++++++- fs/hfs/super.c | 8 +++++++- fs/hpfs/super.c | 8 +++++++- fs/isofs/inode.c | 8 +++++++- fs/jffs2/super.c | 11 +++++++++-- fs/jfs/super.c | 12 ++++++++++-- fs/nilfs2/super.c | 8 +++++++- fs/ntfs/super.c | 5 +++++ fs/ocfs2/dlmfs/dlmfs.c | 9 ++++++++- fs/ocfs2/super.c | 5 +++++ fs/qnx4/inode.c | 8 +++++++- fs/smbfs/inode.c | 5 +++++ fs/squashfs/super.c | 6 ++++++ fs/udf/super.c | 8 +++++++- fs/ufs/super.c | 5 +++++ 27 files changed, 183 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 4a3af7075c1..d9803f73236 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -352,11 +352,15 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) struct adfs_sb_info *asb; struct inode *root; + lock_kernel(); + sb->s_flags |= MS_NODIRATIME; asb = kzalloc(sizeof(*asb), GFP_KERNEL); - if (!asb) + if (!asb) { + unlock_kernel(); return -ENOMEM; + } sb->s_fs_info = asb; /* set default options */ @@ -474,6 +478,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent) goto error; } else sb->s_root->d_op = &adfs_dentry_operations; + unlock_kernel(); return 0; error_free_bh: @@ -481,6 +486,7 @@ error_free_bh: error: sb->s_fs_info = NULL; kfree(asb); + unlock_kernel(); return -EINVAL; } diff --git a/fs/affs/super.c b/fs/affs/super.c index 33c4e7eef47..3a6d1dee4a5 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -291,6 +291,8 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) u8 sig[4]; int ret = -EINVAL; + lock_kernel(); + save_mount_options(sb, data); pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options"); @@ -300,8 +302,10 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) sb->s_flags |= MS_NODIRATIME; sbi = kzalloc(sizeof(struct affs_sb_info), GFP_KERNEL); - if (!sbi) + if (!sbi) { + unlock_kernel(); return -ENOMEM; + } sb->s_fs_info = sbi; mutex_init(&sbi->s_bmlock); spin_lock_init(&sbi->symlink_lock); @@ -312,6 +316,7 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) printk(KERN_ERR "AFFS: Error parsing options\n"); kfree(sbi->s_prefix); kfree(sbi); + unlock_kernel(); return -EINVAL; } /* N.B. after this point s_prefix must be released */ @@ -482,6 +487,7 @@ got_root: sb->s_root->d_op = &affs_dentry_operations; pr_debug("AFFS: s_flags=%lX\n",sb->s_flags); + unlock_kernel(); return 0; /* @@ -496,6 +502,7 @@ out_error_noinode: kfree(sbi->s_prefix); kfree(sbi); sb->s_fs_info = NULL; + unlock_kernel(); return ret; } diff --git a/fs/afs/super.c b/fs/afs/super.c index 77e1e5a6115..6c2fef44d38 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -302,12 +302,15 @@ static int afs_fill_super(struct super_block *sb, void *data) struct inode *inode = NULL; int ret; + lock_kernel(); + _enter(""); /* allocate a superblock info record */ as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); if (!as) { _leave(" = -ENOMEM"); + unlock_kernel(); return -ENOMEM; } @@ -341,6 +344,7 @@ static int afs_fill_super(struct super_block *sb, void *data) sb->s_root = root; _leave(" = 0"); + unlock_kernel(); return 0; error_inode: @@ -354,6 +358,7 @@ error: sb->s_fs_info = NULL; _leave(" = %d", ret); + unlock_kernel(); return ret; } diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index c4daf0f5fc0..d2e09363dd9 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -322,9 +322,13 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) int ret = -EINVAL; unsigned long i_sblock, i_eblock, i_eoff, s_size; + lock_kernel(); + info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) + if (!info) { + unlock_kernel(); return -ENOMEM; + } mutex_init(&info->bfs_lock); s->s_fs_info = info; @@ -439,6 +443,7 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) brelse(bh); brelse(sbh); dump_imap("read_super", s); + unlock_kernel(); return 0; out3: @@ -452,6 +457,7 @@ out: mutex_destroy(&info->bfs_lock); kfree(info); s->s_fs_info = NULL; + unlock_kernel(); return ret; } diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index b7431afdd76..070bf1aecd2 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -514,22 +514,30 @@ cifs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { int rc; - struct super_block *sb = sget(fs_type, NULL, set_anon_super, NULL); + struct super_block *sb; + + lock_kernel(); + + sb = sget(fs_type, NULL, set_anon_super, NULL); cFYI(1, "Devname: %s flags: %d ", dev_name, flags); - if (IS_ERR(sb)) + if (IS_ERR(sb)) { + unlock_kernel(); return PTR_ERR(sb); + } sb->s_flags = flags; rc = cifs_read_super(sb, data, dev_name, flags & MS_SILENT ? 1 : 0); if (rc) { deactivate_locked_super(sb); + unlock_kernel(); return rc; } sb->s_flags |= MS_ACTIVE; simple_set_mnt(mnt, sb); + unlock_kernel(); return 0; } diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 6526e6f21ec..bfe8179b129 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -148,6 +148,8 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) int error; int idx; + lock_kernel(); + idx = get_device_index((struct coda_mount_data *) data); /* Ignore errors in data, for backward compatibility */ @@ -159,11 +161,13 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) vc = &coda_comms[idx]; if (!vc->vc_inuse) { printk("coda_read_super: No pseudo device\n"); + unlock_kernel(); return -EINVAL; } if ( vc->vc_sb ) { printk("coda_read_super: Device already mounted\n"); + unlock_kernel(); return -EBUSY; } @@ -202,7 +206,8 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) sb->s_root = d_alloc_root(root); if (!sb->s_root) goto error; - return 0; + unlock_kernel(); + return 0; error: bdi_destroy(&vc->bdi); @@ -212,6 +217,7 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) if (vc) vc->vc_sb = NULL; + unlock_kernel(); return -EINVAL; } diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index cbd4e18adb2..c4af92fa12c 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -36,6 +36,7 @@ #include #include #include +#include /* For lock_kernel() */ #include "ecryptfs_kernel.h" /** @@ -550,6 +551,7 @@ static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags, const char *err = "Getting sb failed"; int rc; + lock_kernel(); sbi = kmem_cache_zalloc(ecryptfs_sb_info_cache, GFP_KERNEL); if (!sbi) { rc = -ENOMEM; @@ -608,6 +610,7 @@ static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags, goto out; } simple_set_mnt(mnt, s); + unlock_kernel(); return 0; out: @@ -616,6 +619,7 @@ out: kmem_cache_free(ecryptfs_sb_info_cache, sbi); } printk(KERN_ERR "%s; rc = [%d]\n", err, rc); + unlock_kernel(); return rc; } diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 1ec602673ea..f98c390caf1 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -747,15 +747,18 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) __le32 features; int err; + lock_kernel(); + + err = -ENOMEM; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) - return -ENOMEM; + goto failed_unlock; sbi->s_blockgroup_lock = kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL); if (!sbi->s_blockgroup_lock) { kfree(sbi); - return -ENOMEM; + goto failed_unlock; } sb->s_fs_info = sbi; sbi->s_sb_block = sb_block; @@ -1083,6 +1086,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) if (ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY)) sb->s_flags |= MS_RDONLY; ext2_write_super(sb); + unlock_kernel(); return 0; cantfind_ext2: @@ -1107,6 +1111,8 @@ failed_sbi: sb->s_fs_info = NULL; kfree(sbi->s_blockgroup_lock); kfree(sbi); +failed_unlock: + unlock_kernel(); return ret; } diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 5dbf4dba03c..41f9dcd73e9 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -1611,14 +1611,19 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) __le32 features; int err; + lock_kernel(); + sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); - if (!sbi) + if (!sbi) { + unlock_kernel(); return -ENOMEM; + } sbi->s_blockgroup_lock = kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL); if (!sbi->s_blockgroup_lock) { kfree(sbi); + unlock_kernel(); return -ENOMEM; } sb->s_fs_info = sbi; @@ -2026,6 +2031,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) "writeback"); lock_kernel(); + unlock_kernel(); return 0; cantfind_ext3: @@ -2056,6 +2062,7 @@ out_fail: kfree(sbi->s_blockgroup_lock); kfree(sbi); lock_kernel(); + unlock_kernel(); return ret; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 26147746c27..0f0021f4990 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2568,6 +2568,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) int err; unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO; + lock_kernel(); + sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) goto out_free_orig; @@ -3166,7 +3168,6 @@ no_journal: if (es->s_error_count) mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */ - lock_kernel(); kfree(orig_data); return 0; @@ -3213,8 +3214,11 @@ out_fail: sb->s_fs_info = NULL; kfree(sbi->s_blockgroup_lock); kfree(sbi); - lock_kernel(); + kfree(orig_data); + return ret; + out_free_orig: + unlock_kernel(); kfree(orig_data); return ret; } diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index bbc94ae4fd7..e2b0b978340 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -9,6 +9,7 @@ #include #include #include +#include /* For lock_kernel() */ #include "fat.h" /* Characters that are undesirable in an MS-DOS file name */ @@ -662,12 +663,16 @@ static int msdos_fill_super(struct super_block *sb, void *data, int silent) { int res; + lock_kernel(); res = fat_fill_super(sb, data, silent, &msdos_dir_inode_operations, 0); - if (res) + if (res) { + unlock_kernel(); return res; + } sb->s_flags |= MS_NOATIME; sb->s_root->d_op = &msdos_dentry_operations; + unlock_kernel(); return 0; } diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 6fcc7e71fba..9006ad9c7b1 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -21,6 +21,7 @@ #include #include #include +#include /* For lock_kernel() */ #include "fat.h" /* @@ -1055,15 +1056,19 @@ static int vfat_fill_super(struct super_block *sb, void *data, int silent) { int res; + lock_kernel(); res = fat_fill_super(sb, data, silent, &vfat_dir_inode_operations, 1); - if (res) + if (res) { + unlock_kernel(); return res; + } if (MSDOS_SB(sb)->options.name_check != 's') sb->s_root->d_op = &vfat_ci_dentry_ops; else sb->s_root->d_op = &vfat_dentry_ops; + unlock_kernel(); return 0; } diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c index dc0c041e85c..eb2b9e09c99 100644 --- a/fs/freevxfs/vxfs_super.c +++ b/fs/freevxfs/vxfs_super.c @@ -148,7 +148,7 @@ static int vxfs_remount(struct super_block *sb, int *flags, char *data) * The superblock on success, else %NULL. * * Locking: - * We are under the bkl and @sbp->s_lock. + * We are under @sbp->s_lock. */ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent) { @@ -159,11 +159,14 @@ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent) struct inode *root; int ret = -EINVAL; + lock_kernel(); + sbp->s_flags |= MS_RDONLY; infp = kzalloc(sizeof(*infp), GFP_KERNEL); if (!infp) { printk(KERN_WARNING "vxfs: unable to allocate incore superblock\n"); + unlock_kernel(); return -ENOMEM; } @@ -236,6 +239,7 @@ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent) goto out_free_ilist; } + unlock_kernel(); return 0; out_free_ilist: @@ -245,6 +249,7 @@ out_free_ilist: out: brelse(bp); kfree(infp); + unlock_kernel(); return ret; } diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 34235d4bf08..3069416fa8e 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -382,9 +382,13 @@ static int hfs_fill_super(struct super_block *sb, void *data, int silent) struct inode *root_inode; int res; + lock_kernel(); + sbi = kzalloc(sizeof(struct hfs_sb_info), GFP_KERNEL); - if (!sbi) + if (!sbi) { + unlock_kernel(); return -ENOMEM; + } sb->s_fs_info = sbi; INIT_HLIST_HEAD(&sbi->rsrc_inodes); @@ -435,6 +439,7 @@ static int hfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_root->d_op = &hfs_dentry_operations; /* everything's okay */ + unlock_kernel(); return 0; bail_iput: @@ -443,6 +448,7 @@ bail_no_root: printk(KERN_ERR "hfs: get root inode failed.\n"); bail: hfs_mdb_put(sb); + unlock_kernel(); return res; } diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 2607010be2f..c969a1aa163 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -477,11 +477,15 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) int o; + lock_kernel(); + save_mount_options(s, options); sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); - if (!sbi) + if (!sbi) { + unlock_kernel(); return -ENOMEM; + } s->s_fs_info = sbi; sbi->sb_bmp_dir = NULL; @@ -666,6 +670,7 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) root->i_blocks = 5; hpfs_brelse4(&qbh); } + unlock_kernel(); return 0; bail4: brelse(bh2); @@ -677,6 +682,7 @@ bail0: kfree(sbi->sb_cp_table); s->s_fs_info = NULL; kfree(sbi); + unlock_kernel(); return -EINVAL; } diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 5a44811b502..05baf7721e8 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -571,11 +571,15 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) int table, error = -EINVAL; unsigned int vol_desc_start; + lock_kernel(); + save_mount_options(s, data); sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); - if (!sbi) + if (!sbi) { + unlock_kernel(); return -ENOMEM; + } s->s_fs_info = sbi; if (!parse_options((char *)data, &opt)) @@ -900,6 +904,7 @@ root_found: kfree(opt.iocharset); + unlock_kernel(); return 0; /* @@ -939,6 +944,7 @@ out_freesbi: kfree(opt.iocharset); kfree(sbi); s->s_fs_info = NULL; + unlock_kernel(); return error; } diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 662bba09950..58dd9cf0620 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -146,14 +146,19 @@ static const struct super_operations jffs2_super_operations = static int jffs2_fill_super(struct super_block *sb, void *data, int silent) { struct jffs2_sb_info *c; + int ret; + + lock_kernel(); D1(printk(KERN_DEBUG "jffs2_get_sb_mtd():" " New superblock for device %d (\"%s\")\n", sb->s_mtd->index, sb->s_mtd->name)); c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) + if (!c) { + unlock_kernel(); return -ENOMEM; + } c->mtd = sb->s_mtd; c->os_priv = sb; @@ -175,7 +180,9 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) #ifdef CONFIG_JFFS2_FS_POSIX_ACL sb->s_flags |= MS_POSIXACL; #endif - return jffs2_do_fill_super(sb, data, silent); + ret = jffs2_do_fill_super(sb, data, silent); + unlock_kernel(); + return ret; } static int jffs2_get_sb(struct file_system_type *fs_type, diff --git a/fs/jfs/super.c b/fs/jfs/super.c index ec8c3e4baca..eb31f677347 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -438,14 +438,20 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) s64 newLVSize = 0; int flag, ret = -EINVAL; + lock_kernel(); + jfs_info("In jfs_read_super: s_flags=0x%lx", sb->s_flags); - if (!new_valid_dev(sb->s_bdev->bd_dev)) + if (!new_valid_dev(sb->s_bdev->bd_dev)) { + unlock_kernel(); return -EOVERFLOW; + } sbi = kzalloc(sizeof (struct jfs_sb_info), GFP_KERNEL); - if (!sbi) + if (!sbi) { + unlock_kernel(); return -ENOMEM; + } sb->s_fs_info = sbi; sbi->sb = sb; sbi->uid = sbi->gid = sbi->umask = -1; @@ -542,6 +548,7 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_maxbytes = min(((u64) PAGE_CACHE_SIZE << 32) - 1, (u64)sb->s_maxbytes); #endif sb->s_time_gran = 1; + unlock_kernel(); return 0; out_no_root: @@ -564,6 +571,7 @@ out_unload: unload_nls(sbi->nls_tab); out_kfree: kfree(sbi); + unlock_kernel(); return ret; } diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 922263393c7..0d573c2a686 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -1113,9 +1113,12 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, if (!(flags & MS_RDONLY)) mode |= FMODE_WRITE; + lock_kernel(); sd.bdev = open_bdev_exclusive(dev_name, mode, fs_type); - if (IS_ERR(sd.bdev)) + if (IS_ERR(sd.bdev)) { + unlock_kernel(); return PTR_ERR(sd.bdev); + } /* * To get mount instance using sget() vfs-routine, NILFS needs @@ -1198,6 +1201,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, if (need_to_close) close_bdev_exclusive(sd.bdev, mode); simple_set_mnt(mnt, s); + unlock_kernel(); return 0; failed_unlock: @@ -1206,6 +1210,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, failed: close_bdev_exclusive(sd.bdev, mode); + unlock_kernel(); return err; cancel_new: @@ -1218,6 +1223,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, * We must finish all post-cleaning before this call; * put_nilfs() needs the block device. */ + unlock_kernel(); return err; } diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 512806171bf..1f31e77fc41 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -2732,6 +2732,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) struct inode *tmp_ino; int blocksize, result; + lock_kernel(); + /* * We do a pretty difficult piece of bootstrap by reading the * MFT (and other metadata) from disk into memory. We'll only @@ -2755,6 +2757,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ntfs_error(sb, "Allocation of NTFS volume structure " "failed. Aborting mount..."); lockdep_on(); + unlock_kernel(); return -ENOMEM; } /* Initialize ntfs_volume structure. */ @@ -2942,6 +2945,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) sb->s_export_op = &ntfs_export_ops; lock_kernel(); lockdep_on(); + unlock_kernel(); return 0; } ntfs_error(sb, "Failed to allocate root directory."); @@ -3062,6 +3066,7 @@ err_out_now: kfree(vol); ntfs_debug("Failed, returning -EINVAL."); lockdep_on(); + unlock_kernel(); return -EINVAL; } diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index c2903b84bb7..667d7ceba8c 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -588,21 +589,27 @@ static int dlmfs_fill_super(struct super_block * sb, struct inode * inode; struct dentry * root; + lock_kernel(); + sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = DLMFS_MAGIC; sb->s_op = &dlmfs_ops; inode = dlmfs_get_root_inode(sb); - if (!inode) + if (!inode) { + unlock_kernel(); return -ENOMEM; + } root = d_alloc_root(inode); if (!root) { iput(inode); + unlock_kernel(); return -ENOMEM; } sb->s_root = root; + unlock_kernel(); return 0; } diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index fa1be1b304d..b7e4f2d19d4 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1002,6 +1002,8 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) char nodestr[8]; struct ocfs2_blockcheck_stats stats; + lock_kernel(); + mlog_entry("%p, %p, %i", sb, data, silent); if (!ocfs2_parse_options(sb, data, &parsed_options, 0)) { @@ -1179,6 +1181,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) atomic_set(&osb->vol_state, VOLUME_DISABLED); wake_up(&osb->osb_mount_event); mlog_exit(status); + unlock_kernel(); return status; } } @@ -1193,6 +1196,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) ocfs2_orphan_scan_start(osb); mlog_exit(status); + unlock_kernel(); return status; read_super_error: @@ -1208,6 +1212,7 @@ read_super_error: } mlog_exit(status); + unlock_kernel(); return status; } diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 16829722be9..86a7be1399a 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -234,9 +234,13 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) struct qnx4_sb_info *qs; int ret = -EINVAL; + lock_kernel(); + qs = kzalloc(sizeof(struct qnx4_sb_info), GFP_KERNEL); - if (!qs) + if (!qs) { + unlock_kernel(); return -ENOMEM; + } s->s_fs_info = qs; sb_set_blocksize(s, QNX4_BLOCK_SIZE); @@ -284,6 +288,7 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) brelse(bh); + unlock_kernel(); return 0; outi: @@ -293,6 +298,7 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) outnobh: kfree(qs); s->s_fs_info = NULL; + unlock_kernel(); return ret; } diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 450c9194198..8fc5e50e142 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -501,6 +501,8 @@ static int smb_fill_super(struct super_block *sb, void *raw_data, int silent) void *mem; static int warn_count; + lock_kernel(); + if (warn_count < 5) { warn_count++; printk(KERN_EMERG "smbfs is deprecated and will be removed" @@ -621,6 +623,7 @@ static int smb_fill_super(struct super_block *sb, void *raw_data, int silent) smb_new_dentry(sb->s_root); + unlock_kernel(); return 0; out_no_root: @@ -643,9 +646,11 @@ out_wrong_data: out_no_data: printk(KERN_ERR "smb_fill_super: missing data argument\n"); out_fail: + unlock_kernel(); return -EINVAL; out_no_server: printk(KERN_ERR "smb_fill_super: cannot allocate struct smb_sb_info\n"); + unlock_kernel(); return -ENOMEM; } diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 88b4f860665..ab1a401a0e1 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -87,11 +87,14 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) u64 lookup_table_start, xattr_id_table_start; int err; + lock_kernel(); + TRACE("Entered squashfs_fill_superblock\n"); sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL); if (sb->s_fs_info == NULL) { ERROR("Failed to allocate squashfs_sb_info\n"); + unlock_kernel(); return -ENOMEM; } msblk = sb->s_fs_info; @@ -301,6 +304,7 @@ allocate_root: TRACE("Leaving squashfs_fill_super\n"); kfree(sblk); + unlock_kernel(); return 0; failed_mount: @@ -315,11 +319,13 @@ failed_mount: kfree(sb->s_fs_info); sb->s_fs_info = NULL; kfree(sblk); + unlock_kernel(); return err; failure: kfree(sb->s_fs_info); sb->s_fs_info = NULL; + unlock_kernel(); return -ENOMEM; } diff --git a/fs/udf/super.c b/fs/udf/super.c index 65412d84a45..76f3d6d97b4 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1880,6 +1880,8 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) struct kernel_lb_addr rootdir, fileset; struct udf_sb_info *sbi; + lock_kernel(); + uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT); uopt.uid = -1; uopt.gid = -1; @@ -1888,8 +1890,10 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) uopt.dmode = UDF_INVALID_MODE; sbi = kzalloc(sizeof(struct udf_sb_info), GFP_KERNEL); - if (!sbi) + if (!sbi) { + unlock_kernel(); return -ENOMEM; + } sb->s_fs_info = sbi; @@ -2035,6 +2039,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) goto error_out; } sb->s_maxbytes = MAX_LFS_FILESIZE; + unlock_kernel(); return 0; error_out: @@ -2055,6 +2060,7 @@ error_out: kfree(sbi); sb->s_fs_info = NULL; + unlock_kernel(); return -EINVAL; } diff --git a/fs/ufs/super.c b/fs/ufs/super.c index d510c1b9181..6b9be90dae7 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -696,6 +696,8 @@ static int ufs_fill_super(struct super_block *sb, void *data, int silent) unsigned maxsymlen; int ret = -EINVAL; + lock_kernel(); + uspi = NULL; ubh = NULL; flags = 0; @@ -1163,6 +1165,7 @@ magic_found: goto failed; UFSD("EXIT\n"); + unlock_kernel(); return 0; dalloc_failed: @@ -1174,10 +1177,12 @@ failed: kfree(sbi); sb->s_fs_info = NULL; UFSD("EXIT (FAILED)\n"); + unlock_kernel(); return ret; failed_nomem: UFSD("EXIT (NOMEM)\n"); + unlock_kernel(); return -ENOMEM; } -- cgit v1.2.3-18-g5258 From 74c41429ae5889933a2b9b9d0d2f687baa410766 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Sun, 15 Aug 2010 22:52:36 +0200 Subject: BKL: Remove BKL from Amiga FFS The BKL is only used in put_super, fill_super and remount_fs that are all three protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Jan Blunck Signed-off-by: Arnd Bergmann --- fs/affs/super.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/affs/super.c b/fs/affs/super.c index 3a6d1dee4a5..a167f96d79f 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include "affs.h" @@ -46,8 +45,6 @@ affs_put_super(struct super_block *sb) struct affs_sb_info *sbi = AFFS_SB(sb); pr_debug("AFFS: put_super()\n"); - lock_kernel(); - if (!(sb->s_flags & MS_RDONLY) && sb->s_dirt) affs_commit_super(sb, 1, 1); @@ -56,8 +53,6 @@ affs_put_super(struct super_block *sb) affs_brelse(sbi->s_root_bh); kfree(sbi); sb->s_fs_info = NULL; - - unlock_kernel(); } static void @@ -291,8 +286,6 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) u8 sig[4]; int ret = -EINVAL; - lock_kernel(); - save_mount_options(sb, data); pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options"); @@ -302,10 +295,9 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) sb->s_flags |= MS_NODIRATIME; sbi = kzalloc(sizeof(struct affs_sb_info), GFP_KERNEL); - if (!sbi) { - unlock_kernel(); + if (!sbi) return -ENOMEM; - } + sb->s_fs_info = sbi; mutex_init(&sbi->s_bmlock); spin_lock_init(&sbi->symlink_lock); @@ -316,7 +308,6 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) printk(KERN_ERR "AFFS: Error parsing options\n"); kfree(sbi->s_prefix); kfree(sbi); - unlock_kernel(); return -EINVAL; } /* N.B. after this point s_prefix must be released */ @@ -487,7 +478,6 @@ got_root: sb->s_root->d_op = &affs_dentry_operations; pr_debug("AFFS: s_flags=%lX\n",sb->s_flags); - unlock_kernel(); return 0; /* @@ -502,7 +492,6 @@ out_error_noinode: kfree(sbi->s_prefix); kfree(sbi); sb->s_fs_info = NULL; - unlock_kernel(); return ret; } @@ -534,7 +523,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) kfree(new_opts); return -EINVAL; } - lock_kernel(); + replace_mount_options(sb, new_opts); sbi->s_flags = mount_flags; @@ -550,17 +539,15 @@ affs_remount(struct super_block *sb, int *flags, char *data) memcpy(sbi->s_volume, volume, 32); spin_unlock(&sbi->symlink_lock); - if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) { - unlock_kernel(); + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; - } + if (*flags & MS_RDONLY) { affs_write_super(sb); affs_free_bitmap(sb); } else res = affs_init_bitmap(sb, flags); - unlock_kernel(); return res; } -- cgit v1.2.3-18-g5258 From ba13d597a60a1a26614f18b76c1a2cad1a548e46 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:29 +0100 Subject: BKL: Remove BKL from BFS The BKL is only used in put_super and fill_super that are both protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Jan Blunck Signed-off-by: Arnd Bergmann --- fs/bfs/inode.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index d2e09363dd9..883e77acd5a 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -215,14 +214,10 @@ static void bfs_put_super(struct super_block *s) if (!info) return; - lock_kernel(); - mutex_destroy(&info->bfs_lock); kfree(info->si_imap); kfree(info); s->s_fs_info = NULL; - - unlock_kernel(); } static int bfs_statfs(struct dentry *dentry, struct kstatfs *buf) @@ -322,13 +317,9 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) int ret = -EINVAL; unsigned long i_sblock, i_eblock, i_eoff, s_size; - lock_kernel(); - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - unlock_kernel(); + if (!info) return -ENOMEM; - } mutex_init(&info->bfs_lock); s->s_fs_info = info; @@ -443,7 +434,6 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) brelse(bh); brelse(sbh); dump_imap("read_super", s); - unlock_kernel(); return 0; out3: @@ -457,7 +447,6 @@ out: mutex_destroy(&info->bfs_lock); kfree(info); s->s_fs_info = NULL; - unlock_kernel(); return ret; } -- cgit v1.2.3-18-g5258 From b0991aa324b57dca8feef75ed75b24080ee4a9fc Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:29 +0100 Subject: BKL: Remove BKL from CifsFS The BKL is only used in put_super and fill_super that are both protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Jan Blunck Cc: Steve French Signed-off-by: Arnd Bergmann --- fs/cifs/cifsfs.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 070bf1aecd2..4e273f7793f 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -35,7 +35,6 @@ #include #include #include -#include #include "cifsfs.h" #include "cifspdu.h" #define DECLARE_GLOBALS_HERE @@ -200,8 +199,6 @@ cifs_put_super(struct super_block *sb) return; } - lock_kernel(); - rc = cifs_umount(sb, cifs_sb); if (rc) cERROR(1, "cifs_umount failed with return code %d", rc); @@ -215,8 +212,6 @@ cifs_put_super(struct super_block *sb) unload_nls(cifs_sb->local_nls); bdi_destroy(&cifs_sb->bdi); kfree(cifs_sb); - - unlock_kernel(); } static int @@ -516,28 +511,22 @@ cifs_get_sb(struct file_system_type *fs_type, int rc; struct super_block *sb; - lock_kernel(); - sb = sget(fs_type, NULL, set_anon_super, NULL); cFYI(1, "Devname: %s flags: %d ", dev_name, flags); - if (IS_ERR(sb)) { - unlock_kernel(); + if (IS_ERR(sb)) return PTR_ERR(sb); - } sb->s_flags = flags; rc = cifs_read_super(sb, data, dev_name, flags & MS_SILENT ? 1 : 0); if (rc) { deactivate_locked_super(sb); - unlock_kernel(); return rc; } sb->s_flags |= MS_ACTIVE; simple_set_mnt(mnt, sb); - unlock_kernel(); return 0; } -- cgit v1.2.3-18-g5258 From d646cf82e9b6a58ba2d748e66e5fc7223830c68c Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:29 +0100 Subject: BKL: Remove BKL from ext3 fill_super() The BKL is protecting nothing than two memory allocations here. Signed-off-by: Jan Blunck Acked-by: Jan Kara Signed-off-by: Arnd Bergmann --- fs/ext3/super.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 41f9dcd73e9..edde63fdb9a 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -1611,19 +1611,14 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) __le32 features; int err; - lock_kernel(); - sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); - if (!sbi) { - unlock_kernel(); + if (!sbi) return -ENOMEM; - } sbi->s_blockgroup_lock = kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL); if (!sbi->s_blockgroup_lock) { kfree(sbi); - unlock_kernel(); return -ENOMEM; } sb->s_fs_info = sbi; @@ -1632,8 +1627,6 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) sbi->s_resgid = EXT3_DEF_RESGID; sbi->s_sb_block = sb_block; - unlock_kernel(); - blocksize = sb_min_blocksize(sb, EXT3_MIN_BLOCK_SIZE); if (!blocksize) { ext3_msg(sb, KERN_ERR, "error: unable to set blocksize"); @@ -2030,8 +2023,6 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) test_opt(sb,DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA ? "ordered": "writeback"); - lock_kernel(); - unlock_kernel(); return 0; cantfind_ext3: @@ -2061,8 +2052,6 @@ out_fail: sb->s_fs_info = NULL; kfree(sbi->s_blockgroup_lock); kfree(sbi); - lock_kernel(); - unlock_kernel(); return ret; } -- cgit v1.2.3-18-g5258 From 77b54a46a83232d172d55013c3af838190615260 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:30 +0100 Subject: BKL: Remove BKL from ext3_put_super() and ext3_remount() The BKL lock is protecting the remounting against a potential call to ext3_put_super(). This could not happen, since this is protected by the s_umount rw semaphore of struct super_block. Therefore I think the BKL is protecting nothing here. Signed-off-by: Jan Blunck Acked-by: Jan Kara Signed-off-by: Arnd Bergmann --- fs/ext3/super.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'fs') diff --git a/fs/ext3/super.c b/fs/ext3/super.c index edde63fdb9a..2e20bd77133 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -411,9 +411,6 @@ static void ext3_put_super (struct super_block * sb) int i, err; dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED); - - lock_kernel(); - ext3_xattr_put_super(sb); err = journal_destroy(sbi->s_journal); sbi->s_journal = NULL; @@ -462,8 +459,6 @@ static void ext3_put_super (struct super_block * sb) sb->s_fs_info = NULL; kfree(sbi->s_blockgroup_lock); kfree(sbi); - - unlock_kernel(); } static struct kmem_cache *ext3_inode_cachep; @@ -2534,8 +2529,6 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data) int i; #endif - lock_kernel(); - /* Store the original options */ lock_super(sb); old_sb_flags = sb->s_flags; @@ -2644,7 +2637,6 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data) kfree(old_opts.s_qf_names[i]); #endif unlock_super(sb); - unlock_kernel(); if (enable_quota) dquot_resume(sb, -1); @@ -2665,7 +2657,6 @@ restore_opts: } #endif unlock_super(sb); - unlock_kernel(); return err; } -- cgit v1.2.3-18-g5258 From f2143c4e2ebc6be3f07b7c7527dae7313fde23e1 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:30 +0100 Subject: BKL: Remove BKL from ext4 filesystem The BKL is still used in ext4_put_super(), ext4_fill_super() and ext4_remount(). All three calles are protected against concurrent calls by the s_umount rw semaphore of struct super_block. Therefore the BKL is protecting nothing in this case. Signed-off-by: Jan Blunck Acked-by: "Theodore Ts'o" Signed-off-by: Arnd Bergmann --- fs/ext4/super.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'fs') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 0f0021f4990..24e7699f915 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -708,7 +707,6 @@ static void ext4_put_super(struct super_block *sb) destroy_workqueue(sbi->dio_unwritten_wq); lock_super(sb); - lock_kernel(); if (sb->s_dirt) ext4_commit_super(sb, 1); @@ -775,7 +773,6 @@ static void ext4_put_super(struct super_block *sb) * Now that we are completely done shutting down the * superblock, we need to actually destroy the kobject. */ - unlock_kernel(); unlock_super(sb); kobject_put(&sbi->s_kobj); wait_for_completion(&sbi->s_kobj_unregister); @@ -2568,8 +2565,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) int err; unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO; - lock_kernel(); - sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) goto out_free_orig; @@ -2590,8 +2585,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sbi->s_sectors_written_start = part_stat_read(sb->s_bdev->bd_part, sectors[1]); - unlock_kernel(); - /* Cleanup superblock name */ for (cp = sb->s_id; (cp = strchr(cp, '/'));) *cp = '!'; @@ -3214,11 +3207,7 @@ out_fail: sb->s_fs_info = NULL; kfree(sbi->s_blockgroup_lock); kfree(sbi); - kfree(orig_data); - return ret; - out_free_orig: - unlock_kernel(); kfree(orig_data); return ret; } @@ -3726,8 +3715,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) #endif char *orig_data = kstrdup(data, GFP_KERNEL); - lock_kernel(); - /* Store the original options */ lock_super(sb); old_sb_flags = sb->s_flags; @@ -3862,7 +3849,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) kfree(old_opts.s_qf_names[i]); #endif unlock_super(sb); - unlock_kernel(); if (enable_quota) dquot_resume(sb, -1); @@ -3888,7 +3874,6 @@ restore_opts: } #endif unlock_super(sb); - unlock_kernel(); kfree(orig_data); return err; } -- cgit v1.2.3-18-g5258 From 8526fb37c907f8f4eb9965367af6aeec76aedbce Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:30 +0100 Subject: BKL: Remove BKL from HFS The BKL is only used in put_super and fill_super that are both protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Jan Blunck Signed-off-by: Arnd Bergmann --- fs/hfs/super.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 3069416fa8e..33254160f65 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "hfs_fs.h" @@ -79,15 +78,11 @@ static int hfs_sync_fs(struct super_block *sb, int wait) */ static void hfs_put_super(struct super_block *sb) { - lock_kernel(); - if (sb->s_dirt) hfs_write_super(sb); hfs_mdb_close(sb); /* release the MDB's resources */ hfs_mdb_put(sb); - - unlock_kernel(); } /* @@ -382,13 +377,10 @@ static int hfs_fill_super(struct super_block *sb, void *data, int silent) struct inode *root_inode; int res; - lock_kernel(); - sbi = kzalloc(sizeof(struct hfs_sb_info), GFP_KERNEL); - if (!sbi) { - unlock_kernel(); + if (!sbi) return -ENOMEM; - } + sb->s_fs_info = sbi; INIT_HLIST_HEAD(&sbi->rsrc_inodes); @@ -439,7 +431,6 @@ static int hfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_root->d_op = &hfs_dentry_operations; /* everything's okay */ - unlock_kernel(); return 0; bail_iput: @@ -448,7 +439,6 @@ bail_no_root: printk(KERN_ERR "hfs: get root inode failed.\n"); bail: hfs_mdb_put(sb); - unlock_kernel(); return res; } -- cgit v1.2.3-18-g5258 From 22b26db6f82bfa9a7f2b44443af3b5541927a130 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:31 +0100 Subject: BKL: Remove BKL from JFS The BKL is only used in put_super, fill_super and remount_fs that are all three protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Jan Blunck Signed-off-by: Arnd Bergmann --- fs/jfs/super.c | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/jfs/super.c b/fs/jfs/super.c index eb31f677347..68eee2bf629 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -33,7 +33,6 @@ #include #include #include -#include #include "jfs_incore.h" #include "jfs_filsys.h" @@ -176,8 +175,6 @@ static void jfs_put_super(struct super_block *sb) dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED); - lock_kernel(); - rc = jfs_umount(sb); if (rc) jfs_err("jfs_umount failed with return code %d", rc); @@ -188,8 +185,6 @@ static void jfs_put_super(struct super_block *sb) iput(sbi->direct_inode); kfree(sbi); - - unlock_kernel(); } enum { @@ -369,19 +364,16 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data) if (!parse_options(data, sb, &newLVSize, &flag)) { return -EINVAL; } - lock_kernel(); + if (newLVSize) { if (sb->s_flags & MS_RDONLY) { printk(KERN_ERR "JFS: resize requires volume to be mounted read-write\n"); - unlock_kernel(); return -EROFS; } rc = jfs_extendfs(sb, newLVSize, 0); - if (rc) { - unlock_kernel(); + if (rc) return rc; - } } if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) { @@ -397,36 +389,30 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data) /* mark the fs r/w for quota activity */ sb->s_flags &= ~MS_RDONLY; - unlock_kernel(); dquot_resume(sb, -1); return ret; } if ((!(sb->s_flags & MS_RDONLY)) && (*flags & MS_RDONLY)) { rc = dquot_suspend(sb, -1); if (rc < 0) { - unlock_kernel(); return rc; } rc = jfs_umount_rw(sb); JFS_SBI(sb)->flag = flag; - unlock_kernel(); return rc; } if ((JFS_SBI(sb)->flag & JFS_NOINTEGRITY) != (flag & JFS_NOINTEGRITY)) if (!(sb->s_flags & MS_RDONLY)) { rc = jfs_umount_rw(sb); - if (rc) { - unlock_kernel(); + if (rc) return rc; - } + JFS_SBI(sb)->flag = flag; ret = jfs_mount_rw(sb, 1); - unlock_kernel(); return ret; } JFS_SBI(sb)->flag = flag; - unlock_kernel(); return 0; } @@ -438,20 +424,15 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) s64 newLVSize = 0; int flag, ret = -EINVAL; - lock_kernel(); - jfs_info("In jfs_read_super: s_flags=0x%lx", sb->s_flags); - if (!new_valid_dev(sb->s_bdev->bd_dev)) { - unlock_kernel(); + if (!new_valid_dev(sb->s_bdev->bd_dev)) return -EOVERFLOW; - } sbi = kzalloc(sizeof (struct jfs_sb_info), GFP_KERNEL); - if (!sbi) { - unlock_kernel(); + if (!sbi) return -ENOMEM; - } + sb->s_fs_info = sbi; sbi->sb = sb; sbi->uid = sbi->gid = sbi->umask = -1; @@ -548,7 +529,6 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_maxbytes = min(((u64) PAGE_CACHE_SIZE << 32) - 1, (u64)sb->s_maxbytes); #endif sb->s_time_gran = 1; - unlock_kernel(); return 0; out_no_root: @@ -571,7 +551,6 @@ out_unload: unload_nls(sbi->nls_tab); out_kfree: kfree(sbi); - unlock_kernel(); return ret; } -- cgit v1.2.3-18-g5258 From d6d4c19c5f9ac5972e30e89b3c81ad1fd6e11fee Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:32 +0100 Subject: BKL: Remove BKL from NILFS2 The BKL is only used in put_super, fill_super and remount_fs that are all three protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Jan Blunck Signed-off-by: Arnd Bergmann --- fs/nilfs2/ioctl.c | 1 - fs/nilfs2/super.c | 18 +----------------- 2 files changed, 1 insertion(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index f90a33d9a5b..0442ee3b394 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c @@ -22,7 +22,6 @@ #include #include -#include /* lock_kernel(), unlock_kernel() */ #include #include /* capable() */ #include /* copy_from_user(), copy_to_user() */ diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 0d573c2a686..9f4913f7840 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -342,8 +341,6 @@ static void nilfs_put_super(struct super_block *sb) struct nilfs_sb_info *sbi = NILFS_SB(sb); struct the_nilfs *nilfs = sbi->s_nilfs; - lock_kernel(); - nilfs_detach_segment_constructor(sbi); if (!(sb->s_flags & MS_RDONLY)) { @@ -361,8 +358,6 @@ static void nilfs_put_super(struct super_block *sb) sbi->s_super = NULL; sb->s_fs_info = NULL; nilfs_put_sbinfo(sbi); - - unlock_kernel(); } static int nilfs_sync_fs(struct super_block *sb, int wait) @@ -949,8 +944,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) struct nilfs_mount_options old_opts; int was_snapshot, err; - lock_kernel(); - down_write(&nilfs->ns_super_sem); old_sb_flags = sb->s_flags; old_opts.mount_opt = sbi->s_mount_opt; @@ -1024,7 +1017,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) } out: up_write(&nilfs->ns_super_sem); - unlock_kernel(); return 0; restore_opts: @@ -1032,7 +1024,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) sbi->s_mount_opt = old_opts.mount_opt; sbi->s_snapshot_cno = old_opts.snapshot_cno; up_write(&nilfs->ns_super_sem); - unlock_kernel(); return err; } @@ -1113,12 +1104,9 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, if (!(flags & MS_RDONLY)) mode |= FMODE_WRITE; - lock_kernel(); sd.bdev = open_bdev_exclusive(dev_name, mode, fs_type); - if (IS_ERR(sd.bdev)) { - unlock_kernel(); + if (IS_ERR(sd.bdev)) return PTR_ERR(sd.bdev); - } /* * To get mount instance using sget() vfs-routine, NILFS needs @@ -1201,7 +1189,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, if (need_to_close) close_bdev_exclusive(sd.bdev, mode); simple_set_mnt(mnt, s); - unlock_kernel(); return 0; failed_unlock: @@ -1209,8 +1196,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, put_nilfs(nilfs); failed: close_bdev_exclusive(sd.bdev, mode); - - unlock_kernel(); return err; cancel_new: @@ -1223,7 +1208,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, * We must finish all post-cleaning before this call; * put_nilfs() needs the block device. */ - unlock_kernel(); return err; } -- cgit v1.2.3-18-g5258 From efdffb54034a6fc96bc7fafa33ca2a5503113eee Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:33 +0100 Subject: BKL: Remove BKL from NTFS The BKL is only used in put_super, fill_super and remount_fs that are all three protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Jan Blunck Signed-off-by: Arnd Bergmann --- fs/ntfs/super.c | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) (limited to 'fs') diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 1f31e77fc41..19c5180f8a2 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include "sysctl.h" @@ -445,7 +444,6 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) ntfs_debug("Entering with remount options string: %s", opt); - lock_kernel(); #ifndef NTFS_RW /* For read-only compiled driver, enforce read-only flag. */ *flags |= MS_RDONLY; @@ -469,18 +467,15 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) if (NVolErrors(vol)) { ntfs_error(sb, "Volume has errors and is read-only%s", es); - unlock_kernel(); return -EROFS; } if (vol->vol_flags & VOLUME_IS_DIRTY) { ntfs_error(sb, "Volume is dirty and read-only%s", es); - unlock_kernel(); return -EROFS; } if (vol->vol_flags & VOLUME_MODIFIED_BY_CHKDSK) { ntfs_error(sb, "Volume has been modified by chkdsk " "and is read-only%s", es); - unlock_kernel(); return -EROFS; } if (vol->vol_flags & VOLUME_MUST_MOUNT_RO_MASK) { @@ -488,13 +483,11 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) "(0x%x) and is read-only%s", (unsigned)le16_to_cpu(vol->vol_flags), es); - unlock_kernel(); return -EROFS; } if (ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY)) { ntfs_error(sb, "Failed to set dirty bit in volume " "information flags%s", es); - unlock_kernel(); return -EROFS; } #if 0 @@ -514,21 +507,18 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) ntfs_error(sb, "Failed to empty journal $LogFile%s", es); NVolSetErrors(vol); - unlock_kernel(); return -EROFS; } if (!ntfs_mark_quotas_out_of_date(vol)) { ntfs_error(sb, "Failed to mark quotas out of date%s", es); NVolSetErrors(vol); - unlock_kernel(); return -EROFS; } if (!ntfs_stamp_usnjrnl(vol)) { ntfs_error(sb, "Failed to stamp transation log " "($UsnJrnl)%s", es); NVolSetErrors(vol); - unlock_kernel(); return -EROFS; } } else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) { @@ -544,11 +534,9 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt) // TODO: Deal with *flags. - if (!parse_options(vol, opt)) { - unlock_kernel(); + if (!parse_options(vol, opt)) return -EINVAL; - } - unlock_kernel(); + ntfs_debug("Done."); return 0; } @@ -2261,8 +2249,6 @@ static void ntfs_put_super(struct super_block *sb) ntfs_debug("Entering."); - lock_kernel(); - #ifdef NTFS_RW /* * Commit all inodes while they are still open in case some of them @@ -2433,8 +2419,6 @@ static void ntfs_put_super(struct super_block *sb) sb->s_fs_info = NULL; kfree(vol); - - unlock_kernel(); } /** @@ -2732,8 +2716,6 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) struct inode *tmp_ino; int blocksize, result; - lock_kernel(); - /* * We do a pretty difficult piece of bootstrap by reading the * MFT (and other metadata) from disk into memory. We'll only @@ -2757,7 +2739,6 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ntfs_error(sb, "Allocation of NTFS volume structure " "failed. Aborting mount..."); lockdep_on(); - unlock_kernel(); return -ENOMEM; } /* Initialize ntfs_volume structure. */ @@ -2775,8 +2756,6 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) init_rwsem(&vol->mftbmp_lock); init_rwsem(&vol->lcnbmp_lock); - unlock_kernel(); - /* By default, enable sparse support. */ NVolSetSparseEnabled(vol); @@ -2943,9 +2922,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) } mutex_unlock(&ntfs_lock); sb->s_export_op = &ntfs_export_ops; - lock_kernel(); lockdep_on(); - unlock_kernel(); return 0; } ntfs_error(sb, "Failed to allocate root directory."); @@ -3061,12 +3038,10 @@ iput_tmp_ino_err_out_now: } /* Errors at this stage are irrelevant. */ err_out_now: - lock_kernel(); sb->s_fs_info = NULL; kfree(vol); ntfs_debug("Failed, returning -EINVAL."); lockdep_on(); - unlock_kernel(); return -EINVAL; } -- cgit v1.2.3-18-g5258 From 6841c05021236b8d394cc6c41aa6ae17623aef13 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:35 +0100 Subject: BKL: Remove BKL from do_new_mount() After pushing down the BKL to the get_sb/fill_super operations of the filesystems that still make usage of the BKL it is safe to remove it from do_new_mount(). I've read through all the code formerly covered by the BKL inside do_kern_mount() and have satisfied myself that it doesn't need the BKL any more. Signed-off-by: Jan Blunck Cc: Matthew Wilcox Signed-off-by: Arnd Bergmann --- fs/namespace.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index a72eaabfe8f..7ca5182c0be 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1744,9 +1744,7 @@ static int do_new_mount(struct path *path, char *type, int flags, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - lock_kernel(); mnt = do_kern_mount(type, flags, name, data); - unlock_kernel(); if (IS_ERR(mnt)) return PTR_ERR(mnt); -- cgit v1.2.3-18-g5258 From 3e44f9f1dc19e2b8d4f7ca3b3c63e976b22ad372 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 24 Feb 2010 13:25:35 +0100 Subject: BKL: Remove BKL from ext2 filesystem The BKL is still used in ext2_put_super(), ext2_fill_super(), ext2_sync_fs() ext2_remount() and ext2_write_inode(). From these calls ext2_put_super(), ext2_fill_super() and ext2_remount() are protected against each other by the struct super_block s_umount rw semaphore. The call in ext2_write_inode() could only protect the modification of the ext2_sb_info through ext2_update_dynamic_rev() against concurrent ext2_sync_fs() or ext2_remount(). ext2_fill_super() and ext2_put_super() can be left out because you need a valid filesystem reference in all three cases, which you do not have when you are one of these functions. If the BKL is only protecting the modification of the ext2_sb_info it can safely be removed since this is protected by the struct ext2_sb_info s_lock. Signed-off-by: Jan Blunck Cc: Jan Kara Signed-off-by: Arnd Bergmann --- fs/ext2/super.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'fs') diff --git a/fs/ext2/super.c b/fs/ext2/super.c index f98c390caf1..85df87d0f7b 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -747,8 +747,6 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) __le32 features; int err; - lock_kernel(); - err = -ENOMEM; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -1086,7 +1084,6 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) if (ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY)) sb->s_flags |= MS_RDONLY; ext2_write_super(sb); - unlock_kernel(); return 0; cantfind_ext2: @@ -1112,7 +1109,6 @@ failed_sbi: kfree(sbi->s_blockgroup_lock); kfree(sbi); failed_unlock: - unlock_kernel(); return ret; } -- cgit v1.2.3-18-g5258 From 3768744cfea7b995dce27f02341161fbfdfee80c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 14 Sep 2010 23:07:27 +0200 Subject: BKL: Remove BKL from fat The lock_kernel in fat_put_super is not needed because it only protects the super block itself and we know that no other thread can reach it because we are about to kfree the object. In the two fill_super functions, this converts the locking to use lock_super like elsewhere in the fat code. This is probably not needed either, but is consistent and puts us on the safe side. Signed-off-by: Arnd Bergmann Cc: OGAWA Hirofumi Cc: Jan Blunck --- fs/fat/inode.c | 5 ----- fs/fat/namei_msdos.c | 7 +++---- fs/fat/namei_vfat.c | 7 +++---- 3 files changed, 6 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 830058057d3..ad6998a92c3 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -489,8 +488,6 @@ static void fat_put_super(struct super_block *sb) { struct msdos_sb_info *sbi = MSDOS_SB(sb); - lock_kernel(); - if (sb->s_dirt) fat_write_super(sb); @@ -504,8 +501,6 @@ static void fat_put_super(struct super_block *sb) sb->s_fs_info = NULL; kfree(sbi); - - unlock_kernel(); } static struct kmem_cache *fat_inode_cachep; diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index e2b0b978340..bbca5c186ae 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -9,7 +9,6 @@ #include #include #include -#include /* For lock_kernel() */ #include "fat.h" /* Characters that are undesirable in an MS-DOS file name */ @@ -663,16 +662,16 @@ static int msdos_fill_super(struct super_block *sb, void *data, int silent) { int res; - lock_kernel(); + lock_super(sb); res = fat_fill_super(sb, data, silent, &msdos_dir_inode_operations, 0); if (res) { - unlock_kernel(); + unlock_super(sb); return res; } sb->s_flags |= MS_NOATIME; sb->s_root->d_op = &msdos_dentry_operations; - unlock_kernel(); + unlock_super(sb); return 0; } diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 9006ad9c7b1..6f0f6c9a015 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -21,7 +21,6 @@ #include #include #include -#include /* For lock_kernel() */ #include "fat.h" /* @@ -1056,10 +1055,10 @@ static int vfat_fill_super(struct super_block *sb, void *data, int silent) { int res; - lock_kernel(); + lock_super(sb); res = fat_fill_super(sb, data, silent, &vfat_dir_inode_operations, 1); if (res) { - unlock_kernel(); + unlock_super(sb); return res; } @@ -1068,7 +1067,7 @@ static int vfat_fill_super(struct super_block *sb, void *data, int silent) else sb->s_root->d_op = &vfat_dentry_ops; - unlock_kernel(); + unlock_super(sb); return 0; } -- cgit v1.2.3-18-g5258 From 4f819a7899b06afcd7623ab9d00fd81503ad3e24 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 12 Sep 2010 19:05:56 +0200 Subject: BKL: Remove BKL from isofs As in other file systems, we can replace the big kernel lock with a private mutex in isofs. This means we can now access multiple file systems concurrently, but it also means that we serialize readdir and lookup across sleeping operations which previously released the big kernel lock. This should not matter though, as these operations are in practice serialized through the hardware access. The isofs_get_blocks functions now does not take any lock any more, it used to recursively get the BKL. After looking at the code for hours, I convinced myself that it was never needed here anyway, because it only reads constant fields of the inode and writes to a buffer head array that is at this time only visible to the caller. The get_sb and fill_super operations do not need the locking at all because they operate on a file system that is either about to be created or to be destroyed but in either case is not visible to other threads. Signed-off-by: Arnd Bergmann --- fs/isofs/dir.c | 6 +++--- fs/isofs/inode.c | 17 ++--------------- fs/isofs/isofs.h | 1 + fs/isofs/namei.c | 8 ++++---- fs/isofs/rock.c | 10 +++++----- 5 files changed, 15 insertions(+), 27 deletions(-) (limited to 'fs') diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index e0aca9a0ac6..0542b6eedf8 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -10,7 +10,6 @@ * * isofs directory handling functions */ -#include #include #include "isofs.h" @@ -255,18 +254,19 @@ static int isofs_readdir(struct file *filp, char *tmpname; struct iso_directory_record *tmpde; struct inode *inode = filp->f_path.dentry->d_inode; + struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb); tmpname = (char *)__get_free_page(GFP_KERNEL); if (tmpname == NULL) return -ENOMEM; - lock_kernel(); + mutex_lock(&sbi->s_mutex); tmpde = (struct iso_directory_record *) (tmpname+1024); result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde); free_page((unsigned long) tmpname); - unlock_kernel(); + mutex_unlock(&sbi->s_mutex); return result; } diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 05baf7721e8..09ff41a752a 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -44,11 +43,7 @@ static void isofs_put_super(struct super_block *sb) struct isofs_sb_info *sbi = ISOFS_SB(sb); #ifdef CONFIG_JOLIET - lock_kernel(); - unload_nls(sbi->s_nls_iocharset); - - unlock_kernel(); #endif kfree(sbi); @@ -571,15 +566,11 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) int table, error = -EINVAL; unsigned int vol_desc_start; - lock_kernel(); - save_mount_options(s, data); sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); - if (!sbi) { - unlock_kernel(); + if (!sbi) return -ENOMEM; - } s->s_fs_info = sbi; if (!parse_options((char *)data, &opt)) @@ -827,6 +818,7 @@ root_found: sbi->s_utf8 = opt.utf8; sbi->s_nocompress = opt.nocompress; sbi->s_overriderockperm = opt.overriderockperm; + mutex_init(&sbi->s_mutex); /* * It would be incredibly stupid to allow people to mark every file * on the disk as suid, so we merely allow them to set the default @@ -904,7 +896,6 @@ root_found: kfree(opt.iocharset); - unlock_kernel(); return 0; /* @@ -944,7 +935,6 @@ out_freesbi: kfree(opt.iocharset); kfree(sbi); s->s_fs_info = NULL; - unlock_kernel(); return error; } @@ -983,8 +973,6 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, int section, rv, error; struct iso_inode_info *ei = ISOFS_I(inode); - lock_kernel(); - error = -EIO; rv = 0; if (iblock < 0 || iblock != iblock_s) { @@ -1060,7 +1048,6 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, error = 0; abort: - unlock_kernel(); return rv != 0 ? rv : error; } diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index 7d33de84f52..2882dc089f8 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -55,6 +55,7 @@ struct isofs_sb_info { gid_t s_gid; uid_t s_uid; struct nls_table *s_nls_iocharset; /* Native language support table */ + struct mutex s_mutex; /* replaces BKL, please remove if possible */ }; #define ISOFS_INVALID_MODE ((mode_t) -1) diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index ab438beb867..0d23abfd428 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -6,7 +6,6 @@ * (C) 1991 Linus Torvalds - minix filesystem */ -#include #include #include "isofs.h" @@ -168,6 +167,7 @@ struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, struct nam int found; unsigned long uninitialized_var(block); unsigned long uninitialized_var(offset); + struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb); struct inode *inode; struct page *page; @@ -177,7 +177,7 @@ struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, struct nam if (!page) return ERR_PTR(-ENOMEM); - lock_kernel(); + mutex_lock(&sbi->s_mutex); found = isofs_find_entry(dir, dentry, &block, &offset, page_address(page), @@ -188,10 +188,10 @@ struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, struct nam if (found) { inode = isofs_iget(dir->i_sb, block, offset); if (IS_ERR(inode)) { - unlock_kernel(); + mutex_unlock(&sbi->s_mutex); return ERR_CAST(inode); } } - unlock_kernel(); + mutex_unlock(&sbi->s_mutex); return d_splice_alias(inode, dentry); } diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 96a685c550f..f9cd04db6ea 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -8,7 +8,6 @@ #include #include -#include #include "isofs.h" #include "rock.h" @@ -661,6 +660,7 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; struct iso_inode_info *ei = ISOFS_I(inode); + struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb); char *link = kmap(page); unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); struct buffer_head *bh; @@ -673,12 +673,12 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) struct rock_state rs; int ret; - if (!ISOFS_SB(inode->i_sb)->s_rock) + if (!sbi->s_rock) goto error; init_rock_state(&rs, inode); block = ei->i_iget5_block; - lock_kernel(); + mutex_lock(&sbi->s_mutex); bh = sb_bread(inode->i_sb, block); if (!bh) goto out_noread; @@ -748,7 +748,7 @@ repeat: goto fail; brelse(bh); *rpnt = '\0'; - unlock_kernel(); + mutex_unlock(&sbi->s_mutex); SetPageUptodate(page); kunmap(page); unlock_page(page); @@ -765,7 +765,7 @@ out_bad_span: printk("symlink spans iso9660 blocks\n"); fail: brelse(bh); - unlock_kernel(); + mutex_unlock(&sbi->s_mutex); error: SetPageError(page); kunmap(page); -- cgit v1.2.3-18-g5258 From 00e300e1b682ea86fcb5216250722afd42686ab3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 14 Sep 2010 23:00:34 +0200 Subject: BKL: Remove BKL from autofs4 autofs4 uses the BKL only to guard its ioctl operations. This can be trivially converted to use a mutex, as we have done with most device drivers before. Signed-off-by: Arnd Bergmann Cc: "H. Peter Anvin" Cc: Ian Kent --- fs/autofs4/root.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index cb1bd38dc08..9dd29c29fd1 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include "autofs_i.h" @@ -978,15 +978,17 @@ static int autofs4_root_ioctl_unlocked(struct inode *inode, struct file *filp, } } +static DEFINE_MUTEX(autofs4_ioctl_mutex); + static long autofs4_root_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { long ret; struct inode *inode = filp->f_dentry->d_inode; - lock_kernel(); + mutex_lock(&autofs4_ioctl_mutex); ret = autofs4_root_ioctl_unlocked(inode, filp, cmd, arg); - unlock_kernel(); + mutex_unlock(&autofs4_ioctl_mutex); return ret; } @@ -998,13 +1000,13 @@ static long autofs4_root_compat_ioctl(struct file *filp, struct inode *inode = filp->f_path.dentry->d_inode; int ret; - lock_kernel(); + mutex_lock(&autofs4_ioctl_mutex); if (cmd == AUTOFS_IOC_READY || cmd == AUTOFS_IOC_FAIL) ret = autofs4_root_ioctl_unlocked(inode, filp, cmd, arg); else ret = autofs4_root_ioctl_unlocked(inode, filp, cmd, (unsigned long)compat_ptr(arg)); - unlock_kernel(); + mutex_unlock(&autofs4_ioctl_mutex); return ret; } -- cgit v1.2.3-18-g5258 From 77f2fe036cd51300c80d1aca76bcf23a09977f13 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 16 Sep 2010 15:35:54 +0200 Subject: BKL: Remove BKL from afs The BKL is only used in put_super and fill_super, which are both protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Arnd Bergmann Cc: linux-afs@lists.infradead.org Cc: David Howells --- fs/afs/super.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'fs') diff --git a/fs/afs/super.c b/fs/afs/super.c index 6c2fef44d38..eacf76d98ae 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -302,15 +301,12 @@ static int afs_fill_super(struct super_block *sb, void *data) struct inode *inode = NULL; int ret; - lock_kernel(); - _enter(""); /* allocate a superblock info record */ as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); if (!as) { _leave(" = -ENOMEM"); - unlock_kernel(); return -ENOMEM; } @@ -344,7 +340,6 @@ static int afs_fill_super(struct super_block *sb, void *data) sb->s_root = root; _leave(" = 0"); - unlock_kernel(); return 0; error_inode: @@ -358,7 +353,6 @@ error: sb->s_fs_info = NULL; _leave(" = %d", ret); - unlock_kernel(); return ret; } @@ -458,12 +452,8 @@ static void afs_put_super(struct super_block *sb) _enter(""); - lock_kernel(); - afs_put_volume(as->volume); - unlock_kernel(); - _leave(""); } -- cgit v1.2.3-18-g5258 From 18dfe89d7c394b56e20e06dde8b7a80ff96cf02c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 16 Sep 2010 15:35:54 +0200 Subject: BKL: Remove BKL from ecryptfs The BKL is only used in fill_super, which is protected by the superblocks s_umount rw_semaphorei, and in fasync, which does not do anything that could require the BKL. Therefore it is safe to remove the BKL entirely. Signed-off-by: Arnd Bergmann Cc: Dustin Kirkland Cc: Tyler Hicks Cc: ecryptfs-devel@lists.launchpad.net --- fs/ecryptfs/file.c | 3 --- fs/ecryptfs/main.c | 4 ---- 2 files changed, 7 deletions(-) (limited to 'fs') diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 622c9514080..2b9a644b758 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "ecryptfs_kernel.h" /** @@ -284,11 +283,9 @@ static int ecryptfs_fasync(int fd, struct file *file, int flag) int rc = 0; struct file *lower_file = NULL; - lock_kernel(); lower_file = ecryptfs_file_to_lower(file); if (lower_file->f_op && lower_file->f_op->fasync) rc = lower_file->f_op->fasync(fd, lower_file, flag); - unlock_kernel(); return rc; } diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index c4af92fa12c..cbd4e18adb2 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -36,7 +36,6 @@ #include #include #include -#include /* For lock_kernel() */ #include "ecryptfs_kernel.h" /** @@ -551,7 +550,6 @@ static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags, const char *err = "Getting sb failed"; int rc; - lock_kernel(); sbi = kmem_cache_zalloc(ecryptfs_sb_info_cache, GFP_KERNEL); if (!sbi) { rc = -ENOMEM; @@ -610,7 +608,6 @@ static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags, goto out; } simple_set_mnt(mnt, s); - unlock_kernel(); return 0; out: @@ -619,7 +616,6 @@ out: kmem_cache_free(ecryptfs_sb_info_cache, sbi); } printk(KERN_ERR "%s; rc = [%d]\n", err, rc); - unlock_kernel(); return rc; } -- cgit v1.2.3-18-g5258 From 1a028dd2dd589c3924d9711a1b073a13c820b6b5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 16 Sep 2010 16:11:09 +0200 Subject: BKL: Remove BKL from jffs2 The BKL is only used in put_super, fill_super and remount_fs that are all three protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Arnd Bergmann Cc: David Woodhouse --- fs/jffs2/fs.c | 4 ---- fs/jffs2/super.c | 12 +----------- 2 files changed, 1 insertion(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 6b2964a1985..d9beb06e6fc 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -21,7 +21,6 @@ #include #include #include -#include #include "nodelist.h" static int jffs2_flash_setup(struct jffs2_sb_info *c); @@ -391,7 +390,6 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) This also catches the case where it was stopped and this is just a remount to restart it. Flush the writebuffer, if neccecary, else we loose it */ - lock_kernel(); if (!(sb->s_flags & MS_RDONLY)) { jffs2_stop_garbage_collect_thread(c); mutex_lock(&c->alloc_sem); @@ -403,8 +401,6 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) jffs2_start_garbage_collect_thread(c); *flags |= MS_NOATIME; - - unlock_kernel(); return 0; } diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 58dd9cf0620..d1ae5dfc22b 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -148,17 +147,13 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) struct jffs2_sb_info *c; int ret; - lock_kernel(); - D1(printk(KERN_DEBUG "jffs2_get_sb_mtd():" " New superblock for device %d (\"%s\")\n", sb->s_mtd->index, sb->s_mtd->name)); c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) { - unlock_kernel(); + if (!c) return -ENOMEM; - } c->mtd = sb->s_mtd; c->os_priv = sb; @@ -181,7 +176,6 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) sb->s_flags |= MS_POSIXACL; #endif ret = jffs2_do_fill_super(sb, data, silent); - unlock_kernel(); return ret; } @@ -199,8 +193,6 @@ static void jffs2_put_super (struct super_block *sb) D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); - lock_kernel(); - if (sb->s_dirt) jffs2_write_super(sb); @@ -222,8 +214,6 @@ static void jffs2_put_super (struct super_block *sb) if (c->mtd->sync) c->mtd->sync(c->mtd); - unlock_kernel(); - D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); } -- cgit v1.2.3-18-g5258 From 3dbc4b32d0b39701cbec65582e196a20889155fb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 16 Sep 2010 15:35:54 +0200 Subject: BKL: Remove BKL from squashfs The BKL is only used in put_super and fill_super, which are both protected by the superblocks s_umount rw_semaphore. Therefore it is safe to remove the BKL entirely. Signed-off-by: Arnd Bergmann Cc: Phillip Lougher --- fs/squashfs/super.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'fs') diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index ab1a401a0e1..07a4f115604 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -87,14 +86,11 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) u64 lookup_table_start, xattr_id_table_start; int err; - lock_kernel(); - TRACE("Entered squashfs_fill_superblock\n"); sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL); if (sb->s_fs_info == NULL) { ERROR("Failed to allocate squashfs_sb_info\n"); - unlock_kernel(); return -ENOMEM; } msblk = sb->s_fs_info; @@ -304,7 +300,6 @@ allocate_root: TRACE("Leaving squashfs_fill_super\n"); kfree(sblk); - unlock_kernel(); return 0; failed_mount: @@ -319,13 +314,11 @@ failed_mount: kfree(sb->s_fs_info); sb->s_fs_info = NULL; kfree(sblk); - unlock_kernel(); return err; failure: kfree(sb->s_fs_info); sb->s_fs_info = NULL; - unlock_kernel(); return -ENOMEM; } @@ -360,8 +353,6 @@ static int squashfs_remount(struct super_block *sb, int *flags, char *data) static void squashfs_put_super(struct super_block *sb) { - lock_kernel(); - if (sb->s_fs_info) { struct squashfs_sb_info *sbi = sb->s_fs_info; squashfs_cache_delete(sbi->block_cache); @@ -376,8 +367,6 @@ static void squashfs_put_super(struct super_block *sb) kfree(sb->s_fs_info); sb->s_fs_info = NULL; } - - unlock_kernel(); } -- cgit v1.2.3-18-g5258 From 60056794127a25d641465b706e8828186f7a2e1f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 24 Feb 2010 13:25:33 +0100 Subject: BKL: Remove BKL from OCFS2 The BKL in ocfs2/dlmfs is used in put_super, fill_super and remount_fs that are all three protected by the superblocks s_umount rw_semaphore. The use in ocfs2_control_open is evidently unrelated and the function is protected by ocfs2_control_lock. Therefore it is safe to remove the BKL entirely. Signed-off-by: Arnd Bergmann Cc: Mark Fasheh Cc: Joel Becker --- fs/ocfs2/dlmfs/dlmfs.c | 9 +-------- fs/ocfs2/stack_user.c | 3 --- fs/ocfs2/super.c | 12 ------------ 3 files changed, 1 insertion(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index 667d7ceba8c..c2903b84bb7 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -44,7 +44,6 @@ #include #include #include -#include #include @@ -589,27 +588,21 @@ static int dlmfs_fill_super(struct super_block * sb, struct inode * inode; struct dentry * root; - lock_kernel(); - sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = DLMFS_MAGIC; sb->s_op = &dlmfs_ops; inode = dlmfs_get_root_inode(sb); - if (!inode) { - unlock_kernel(); + if (!inode) return -ENOMEM; - } root = d_alloc_root(inode); if (!root) { iput(inode); - unlock_kernel(); return -ENOMEM; } sb->s_root = root; - unlock_kernel(); return 0; } diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index 2dc57bca068..0e68f542ef2 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -612,12 +611,10 @@ static int ocfs2_control_open(struct inode *inode, struct file *file) return -ENOMEM; p->op_this_node = -1; - lock_kernel(); mutex_lock(&ocfs2_control_lock); file->private_data = p; list_add(&p->op_list, &ocfs2_control_private_list); mutex_unlock(&ocfs2_control_lock); - unlock_kernel(); return 0; } diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index b7e4f2d19d4..b7d724393b5 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -609,8 +609,6 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) struct mount_options parsed_options; struct ocfs2_super *osb = OCFS2_SB(sb); - lock_kernel(); - if (!ocfs2_parse_options(sb, data, &parsed_options, 1) || !ocfs2_check_set_options(sb, &parsed_options)) { ret = -EINVAL; @@ -717,7 +715,6 @@ unlock_osb: MS_POSIXACL : 0); } out: - unlock_kernel(); return ret; } @@ -1002,8 +999,6 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) char nodestr[8]; struct ocfs2_blockcheck_stats stats; - lock_kernel(); - mlog_entry("%p, %p, %i", sb, data, silent); if (!ocfs2_parse_options(sb, data, &parsed_options, 0)) { @@ -1181,7 +1176,6 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) atomic_set(&osb->vol_state, VOLUME_DISABLED); wake_up(&osb->osb_mount_event); mlog_exit(status); - unlock_kernel(); return status; } } @@ -1196,7 +1190,6 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) ocfs2_orphan_scan_start(osb); mlog_exit(status); - unlock_kernel(); return status; read_super_error: @@ -1212,7 +1205,6 @@ read_super_error: } mlog_exit(status); - unlock_kernel(); return status; } @@ -1645,13 +1637,9 @@ static void ocfs2_put_super(struct super_block *sb) { mlog_entry("(0x%p)\n", sb); - lock_kernel(); - ocfs2_sync_blockdev(sb); ocfs2_dismount_volume(sb, 0); - unlock_kernel(); - mlog_exit_void(); } -- cgit v1.2.3-18-g5258 From 2e54eb96e2c801f33d95b5dade15212ac4d6c4a5 Mon Sep 17 00:00:00 2001 From: Petr Vandrovec Date: Mon, 27 Sep 2010 01:47:33 +0200 Subject: BKL: Remove BKL from ncpfs Dozen of changes in ncpfs to provide some locking other than BKL. In readdir cache unlock and mark complete first page as last operation, so it can be used for synchronization, as code intended. When updating dentry name on case insensitive filesystems do at least some basic locking... Hold i_mutex when updating inode fields. Push some ncp_conn_is_valid down to ncp_request. Connection can become invalid at any moment, and fewer error code paths to test the better. Use i_size_{read,write} to modify file size. Set inode's backing_dev_info as ncpfs has its own special bdi. In ioctl unbreak ioctls invoked on filesystem mounted 'ro' - tests are for inode writeable or owner match, but were turned to filesystem writeable and inode writeable or owner match. Also collect all permission checks in single place. Add some locking, and remove comments saying that it would be cool to add some locks to the code. Constify some pointers. Signed-off-by: Petr Vandrovec Signed-off-by: Arnd Bergmann --- fs/ncpfs/dir.c | 221 +++++++++++++--------- fs/ncpfs/file.c | 25 +-- fs/ncpfs/inode.c | 41 ++-- fs/ncpfs/ioctl.c | 470 +++++++++++++++++++++++++--------------------- fs/ncpfs/ncplib_kernel.c | 101 +++++----- fs/ncpfs/ncplib_kernel.h | 15 +- fs/ncpfs/ncpsign_kernel.c | 10 +- fs/ncpfs/sock.c | 1 - 8 files changed, 484 insertions(+), 400 deletions(-) (limited to 'fs') diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 9578cbe0cd5..aac8832e919 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -95,6 +95,34 @@ const struct dentry_operations ncp_root_dentry_operations = }; +#define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber]) + +static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator) +{ +#ifdef CONFIG_NCPFS_SMALLDOS + int ns = ncp_namespace(i); + + if ((ns == NW_NS_DOS) +#ifdef CONFIG_NCPFS_OS2_NS + || ((ns == NW_NS_OS2) && (nscreator == NW_NS_DOS)) +#endif /* CONFIG_NCPFS_OS2_NS */ + ) + return 0; +#endif /* CONFIG_NCPFS_SMALLDOS */ + return 1; +} + +#define ncp_preserve_case(i) (ncp_namespace(i) != NW_NS_DOS) + +static inline int ncp_case_sensitive(struct dentry *dentry) +{ +#ifdef CONFIG_NCPFS_NFS_NS + return ncp_namespace(dentry->d_inode) == NW_NS_NFS; +#else + return 0; +#endif /* CONFIG_NCPFS_NFS_NS */ +} + /* * Note: leave the hash unchanged if the directory * is case-sensitive. @@ -102,13 +130,12 @@ const struct dentry_operations ncp_root_dentry_operations = static int ncp_hash_dentry(struct dentry *dentry, struct qstr *this) { - struct nls_table *t; - unsigned long hash; - int i; - - t = NCP_IO_TABLE(dentry); + if (!ncp_case_sensitive(dentry)) { + struct nls_table *t; + unsigned long hash; + int i; - if (!ncp_case_sensitive(dentry->d_inode)) { + t = NCP_IO_TABLE(dentry); hash = init_name_hash(); for (i=0; ilen ; i++) hash = partial_name_hash(ncp_tolower(t, this->name[i]), @@ -124,7 +151,7 @@ ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) if (a->len != b->len) return 1; - if (ncp_case_sensitive(dentry->d_inode)) + if (ncp_case_sensitive(dentry)) return strncmp(a->name, b->name, a->len); return ncp_strnicmp(NCP_IO_TABLE(dentry), a->name, b->name, a->len); @@ -266,7 +293,7 @@ leave_me:; static int -__ncp_lookup_validate(struct dentry *dentry) +ncp_lookup_validate(struct dentry *dentry, struct nameidata *nd) { struct ncp_server *server; struct dentry *parent; @@ -283,9 +310,6 @@ __ncp_lookup_validate(struct dentry *dentry) server = NCP_SERVER(dir); - if (!ncp_conn_valid(server)) - goto finished; - /* * Inspired by smbfs: * The default validation is based on dentry age: @@ -304,8 +328,11 @@ __ncp_lookup_validate(struct dentry *dentry) if (ncp_is_server_root(dir)) { res = ncp_io2vol(server, __name, &len, dentry->d_name.name, dentry->d_name.len, 1); - if (!res) + if (!res) { res = ncp_lookup_volume(server, __name, &(finfo.i)); + if (!res) + ncp_update_known_namespace(server, finfo.i.volNumber, NULL); + } } else { res = ncp_io2vol(server, __name, &len, dentry->d_name.name, dentry->d_name.len, !ncp_preserve_case(dir)); @@ -320,13 +347,17 @@ __ncp_lookup_validate(struct dentry *dentry) * what we remember, it's not valid any more. */ if (!res) { - if (finfo.i.dirEntNum == NCP_FINFO(dentry->d_inode)->dirEntNum) { + struct inode *inode = dentry->d_inode; + + mutex_lock(&inode->i_mutex); + if (finfo.i.dirEntNum == NCP_FINFO(inode)->dirEntNum) { ncp_new_dentry(dentry); val=1; } else DDPRINTK("ncp_lookup_validate: found, but dirEntNum changed\n"); - ncp_update_inode2(dentry->d_inode, &finfo); + ncp_update_inode2(inode, &finfo); + mutex_unlock(&inode->i_mutex); } finished: @@ -335,16 +366,6 @@ finished: return val; } -static int -ncp_lookup_validate(struct dentry * dentry, struct nameidata *nd) -{ - int res; - lock_kernel(); - res = __ncp_lookup_validate(dentry); - unlock_kernel(); - return res; -} - static struct dentry * ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) { @@ -411,8 +432,6 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) int result, mtime_valid = 0; time_t mtime = 0; - lock_kernel(); - ctl.page = NULL; ctl.cache = NULL; @@ -421,6 +440,7 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) (int) filp->f_pos); result = -EIO; + /* Do not generate '.' and '..' when server is dead. */ if (!ncp_conn_valid(server)) goto out; @@ -532,6 +552,12 @@ read_really: ctl.head.end = ctl.fpos - 1; ctl.head.eof = ctl.valid; finished: + if (ctl.page) { + kunmap(ctl.page); + SetPageUptodate(ctl.page); + unlock_page(ctl.page); + page_cache_release(ctl.page); + } if (page) { cache->head = ctl.head; kunmap(page); @@ -539,23 +565,17 @@ finished: unlock_page(page); page_cache_release(page); } - if (ctl.page) { - kunmap(ctl.page); - SetPageUptodate(ctl.page); - unlock_page(ctl.page); - page_cache_release(ctl.page); - } out: - unlock_kernel(); return result; } static int ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, - struct ncp_cache_control *ctrl, struct ncp_entry_info *entry) + struct ncp_cache_control *ctrl, struct ncp_entry_info *entry, + int inval_childs) { struct dentry *newdent, *dentry = filp->f_path.dentry; - struct inode *newino, *inode = dentry->d_inode; + struct inode *dir = dentry->d_inode; struct ncp_cache_control ctl = *ctrl; struct qstr qname; int valid = 0; @@ -564,9 +584,9 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, __u8 __name[NCP_MAXPATHLEN + 1]; qname.len = sizeof(__name); - if (ncp_vol2io(NCP_SERVER(inode), __name, &qname.len, + if (ncp_vol2io(NCP_SERVER(dir), __name, &qname.len, entry->i.entryName, entry->i.nameLen, - !ncp_preserve_entry_case(inode, entry->i.NSCreator))) + !ncp_preserve_entry_case(dir, entry->i.NSCreator))) return 1; /* I'm not sure */ qname.name = __name; @@ -584,22 +604,64 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, goto end_advance; } else { hashed = 1; - memcpy((char *) newdent->d_name.name, qname.name, - newdent->d_name.len); + + /* If case sensitivity changed for this volume, all entries below this one + should be thrown away. This entry itself is not affected, as its case + sensitivity is controlled by its own parent. */ + if (inval_childs) + shrink_dcache_parent(newdent); + + /* + * It is not as dangerous as it looks. NetWare's OS2 namespace is + * case preserving yet case insensitive. So we update dentry's name + * as received from server. We found dentry via d_lookup with our + * hash, so we know that hash does not change, and so replacing name + * should be reasonably safe. + */ + if (qname.len == newdent->d_name.len && + memcmp(newdent->d_name.name, qname.name, newdent->d_name.len)) { + struct inode *inode = newdent->d_inode; + + /* + * Inside ncpfs all uses of d_name are either for debugging, + * or on functions which acquire inode mutex (mknod, creat, + * lookup). So grab i_mutex here, to be sure. d_path + * uses dcache_lock when generating path, so we should too. + * And finally d_compare is protected by dentry's d_lock, so + * here we go. + */ + if (inode) + mutex_lock(&inode->i_mutex); + spin_lock(&dcache_lock); + spin_lock(&newdent->d_lock); + memcpy((char *) newdent->d_name.name, qname.name, + newdent->d_name.len); + spin_unlock(&newdent->d_lock); + spin_unlock(&dcache_lock); + if (inode) + mutex_unlock(&inode->i_mutex); + } } if (!newdent->d_inode) { + struct inode *inode; + entry->opened = 0; - entry->ino = iunique(inode->i_sb, 2); - newino = ncp_iget(inode->i_sb, entry); - if (newino) { + entry->ino = iunique(dir->i_sb, 2); + inode = ncp_iget(dir->i_sb, entry); + if (inode) { newdent->d_op = &ncp_dentry_operations; - d_instantiate(newdent, newino); + d_instantiate(newdent, inode); if (!hashed) d_rehash(newdent); } - } else - ncp_update_inode2(newdent->d_inode, entry); + } else { + struct inode *inode = newdent->d_inode; + + mutex_lock(&inode->i_mutex); + ncp_update_inode2(inode, entry); + mutex_unlock(&inode->i_mutex); + } if (newdent->d_inode) { ino = newdent->d_inode->i_ino; @@ -617,7 +679,7 @@ ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, ctl.cache = NULL; ctl.idx -= NCP_DIRCACHE_SIZE; ctl.ofs += 1; - ctl.page = grab_cache_page(&inode->i_data, ctl.ofs); + ctl.page = grab_cache_page(&dir->i_data, ctl.ofs); if (ctl.page) ctl.cache = kmap(ctl.page); } @@ -633,7 +695,7 @@ end_advance: if (!ino) ino = find_inode_number(dentry, &qname); if (!ino) - ino = iunique(inode->i_sb, 2); + ino = iunique(dir->i_sb, 2); ctl.filled = filldir(dirent, qname.name, qname.len, filp->f_pos, ino, DT_UNKNOWN); if (!ctl.filled) @@ -660,6 +722,7 @@ ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir, (unsigned long) filp->f_pos); for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) { + int inval_dentry; if (ncp_get_volume_info_with_number(server, i, &info) != 0) return; @@ -675,8 +738,9 @@ ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir, info.volume_name); continue; } + inval_dentry = ncp_update_known_namespace(server, entry.i.volNumber, NULL); entry.volume = entry.i.volNumber; - if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry)) + if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, inval_dentry)) return; } } @@ -739,7 +803,7 @@ ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir, rpl += onerpl; rpls -= onerpl; entry.volume = entry.i.volNumber; - if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry)) + if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry, 0)) break; } } while (more); @@ -775,17 +839,19 @@ int ncp_conn_logged_in(struct super_block *sb) if (dent) { struct inode* ino = dent->d_inode; if (ino) { + ncp_update_known_namespace(server, volNumber, NULL); NCP_FINFO(ino)->volNumber = volNumber; NCP_FINFO(ino)->dirEntNum = dirEntNum; NCP_FINFO(ino)->DosDirNum = DosDirNum; + result = 0; } else { DPRINTK("ncpfs: sb->s_root->d_inode == NULL!\n"); } } else { DPRINTK("ncpfs: sb->s_root == NULL!\n"); } - } - result = 0; + } else + result = 0; out: return result; @@ -799,7 +865,6 @@ static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struc int error, res, len; __u8 __name[NCP_MAXPATHLEN + 1]; - lock_kernel(); error = -EIO; if (!ncp_conn_valid(server)) goto finished; @@ -813,6 +878,8 @@ static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struc dentry->d_name.len, 1); if (!res) res = ncp_lookup_volume(server, __name, &(finfo.i)); + if (!res) + ncp_update_known_namespace(server, finfo.i.volNumber, NULL); } else { res = ncp_io2vol(server, __name, &len, dentry->d_name.name, dentry->d_name.len, !ncp_preserve_case(dir)); @@ -846,7 +913,6 @@ add_entry: finished: PPRINTK("ncp_lookup: result=%d\n", error); - unlock_kernel(); return ERR_PTR(error); } @@ -887,11 +953,6 @@ int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n", dentry->d_parent->d_name.name, dentry->d_name.name, mode); - error = -EIO; - lock_kernel(); - if (!ncp_conn_valid(server)) - goto out; - ncp_age_dentry(server, dentry); len = sizeof(__name); error = ncp_io2vol(server, __name, &len, dentry->d_name.name, @@ -917,6 +978,8 @@ int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, if (result) { if (result == 0x87) error = -ENAMETOOLONG; + else if (result < 0) + error = result; DPRINTK("ncp_create: %s/%s failed\n", dentry->d_parent->d_name.name, dentry->d_name.name); goto out; @@ -935,7 +998,6 @@ int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, error = ncp_instantiate(dir, dentry, &finfo); out: - unlock_kernel(); return error; } @@ -955,11 +1017,6 @@ static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) DPRINTK("ncp_mkdir: making %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - error = -EIO; - lock_kernel(); - if (!ncp_conn_valid(server)) - goto out; - ncp_age_dentry(server, dentry); len = sizeof(__name); error = ncp_io2vol(server, __name, &len, dentry->d_name.name, @@ -967,12 +1024,11 @@ static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) if (error) goto out; - error = -EACCES; - if (ncp_open_create_file_or_subdir(server, dir, __name, + error = ncp_open_create_file_or_subdir(server, dir, __name, OC_MODE_CREATE, aDIR, cpu_to_le16(0xffff), - &finfo) == 0) - { + &finfo); + if (error == 0) { if (ncp_is_nfs_extras(server, finfo.volume)) { mode |= S_IFDIR; finfo.i.nfs.mode = mode; @@ -983,9 +1039,10 @@ static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) goto out; } error = ncp_instantiate(dir, dentry, &finfo); + } else if (error > 0) { + error = -EACCES; } out: - unlock_kernel(); return error; } @@ -998,11 +1055,6 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry) DPRINTK("ncp_rmdir: removing %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - error = -EIO; - lock_kernel(); - if (!ncp_conn_valid(server)) - goto out; - error = -EBUSY; if (!d_unhashed(dentry)) goto out; @@ -1036,11 +1088,10 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry) error = -ENOENT; break; default: - error = -EACCES; + error = result < 0 ? result : -EACCES; break; } out: - unlock_kernel(); return error; } @@ -1050,15 +1101,10 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) struct ncp_server *server; int error; - lock_kernel(); server = NCP_SERVER(dir); DPRINTK("ncp_unlink: unlinking %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - error = -EIO; - if (!ncp_conn_valid(server)) - goto out; - /* * Check whether to close the file ... */ @@ -1097,12 +1143,9 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry) error = -ENOENT; break; default: - error = -EACCES; + error = error < 0 ? error : -EACCES; break; } - -out: - unlock_kernel(); return error; } @@ -1118,11 +1161,6 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, old_dentry->d_parent->d_name.name, old_dentry->d_name.name, new_dentry->d_parent->d_name.name, new_dentry->d_name.name); - error = -EIO; - lock_kernel(); - if (!ncp_conn_valid(server)) - goto out; - ncp_age_dentry(server, old_dentry); ncp_age_dentry(server, new_dentry); @@ -1161,11 +1199,10 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, error = -ENOENT; break; default: - error = -EACCES; + error = error < 0 ? error : -EACCES; break; } out: - unlock_kernel(); return error; } diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 3639cc5cbda..6c754f70c52 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -113,9 +113,6 @@ ncp_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) DPRINTK("ncp_file_read: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - if (!ncp_conn_valid(NCP_SERVER(inode))) - return -EIO; - pos = *ppos; if ((ssize_t) count < 0) { @@ -192,13 +189,11 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t * DPRINTK("ncp_file_write: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); - if (!ncp_conn_valid(NCP_SERVER(inode))) - return -EIO; if ((ssize_t) count < 0) return -EINVAL; pos = *ppos; if (file->f_flags & O_APPEND) { - pos = inode->i_size; + pos = i_size_read(inode); } if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) { @@ -264,8 +259,11 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t * *ppos = pos; - if (pos > inode->i_size) { - inode->i_size = pos; + if (pos > i_size_read(inode)) { + mutex_lock(&inode->i_mutex); + if (pos > i_size_read(inode)) + i_size_write(inode, pos); + mutex_unlock(&inode->i_mutex); } DPRINTK("ncp_file_write: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -281,18 +279,9 @@ static int ncp_release(struct inode *inode, struct file *file) { return 0; } -static loff_t ncp_remote_llseek(struct file *file, loff_t offset, int origin) -{ - loff_t ret; - lock_kernel(); - ret = generic_file_llseek_unlocked(file, offset, origin); - unlock_kernel(); - return ret; -} - const struct file_operations ncp_file_operations = { - .llseek = ncp_remote_llseek, + .llseek = generic_file_llseek, .read = ncp_file_read, .write = ncp_file_write, .unlocked_ioctl = ncp_ioctl, diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index b4de38cf49f..5f4e58d93fd 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -139,7 +139,7 @@ static void ncp_update_dates(struct inode *inode, struct nw_info_struct *nwi) inode->i_mode = nwi->nfs.mode; } - inode->i_blocks = (inode->i_size + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT; + inode->i_blocks = (i_size_read(inode) + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT; inode->i_mtime.tv_sec = ncp_date_dos2unix(nwi->modifyTime, nwi->modifyDate); inode->i_ctime.tv_sec = ncp_date_dos2unix(nwi->creationTime, nwi->creationDate); @@ -158,18 +158,21 @@ static void ncp_update_attrs(struct inode *inode, struct ncp_entry_info *nwinfo) inode->i_mode = server->m.dir_mode; /* for directories dataStreamSize seems to be some Object ID ??? */ - inode->i_size = NCP_BLOCK_SIZE; + i_size_write(inode, NCP_BLOCK_SIZE); } else { + u32 size; + inode->i_mode = server->m.file_mode; - inode->i_size = le32_to_cpu(nwi->dataStreamSize); + size = le32_to_cpu(nwi->dataStreamSize); + i_size_write(inode, size); #ifdef CONFIG_NCPFS_EXTRAS if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS)) && (nwi->attributes & aSHARED)) { switch (nwi->attributes & (aHIDDEN|aSYSTEM)) { case aHIDDEN: if (server->m.flags & NCP_MOUNT_SYMLINKS) { - if (/* (inode->i_size >= NCP_MIN_SYMLINK_SIZE) - && */ (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) { + if (/* (size >= NCP_MIN_SYMLINK_SIZE) + && */ (size <= NCP_MAX_SYMLINK_SIZE)) { inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK; NCP_FINFO(inode)->flags |= NCPI_KLUDGE_SYMLINK; break; @@ -208,7 +211,7 @@ void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo) } /* - * Fill in the inode based on the ncp_entry_info structure. + * Fill in the inode based on the ncp_entry_info structure. Used only for brand new inodes. */ static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo) { @@ -254,6 +257,7 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info) if (inode) { atomic_set(&NCP_FINFO(inode)->opened, info->opened); + inode->i_mapping->backing_dev_info = sb->s_bdi; inode->i_ino = info->ino; ncp_set_attr(inode, info); if (S_ISREG(inode->i_mode)) { @@ -565,10 +569,12 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) /* server->conn_status = 0; */ /* server->root_dentry = NULL; */ /* server->root_setuped = 0; */ + mutex_init(&server->root_setup_lock); #ifdef CONFIG_NCPFS_PACKET_SIGNING /* server->sign_wanted = 0; */ /* server->sign_active = 0; */ #endif + init_rwsem(&server->auth_rwsem); server->auth.auth_type = NCP_AUTH_NONE; /* server->auth.object_name_len = 0; */ /* server->auth.object_name = NULL; */ @@ -593,7 +599,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) server->nls_io = load_nls_default(); #endif /* CONFIG_NCPFS_NLS */ - server->dentry_ttl = 0; /* no caching */ + atomic_set(&server->dentry_ttl, 0); /* no caching */ INIT_LIST_HEAD(&server->tx.requests); mutex_init(&server->rcv.creq_mutex); @@ -658,8 +664,10 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) goto out_disconnect; } } + ncp_lock_server(server); if (options & 2) server->sign_wanted = 1; + ncp_unlock_server(server); } else #endif /* CONFIG_NCPFS_PACKET_SIGNING */ @@ -720,6 +728,9 @@ out_nls: unload_nls(server->nls_io); unload_nls(server->nls_vol); #endif + mutex_destroy(&server->rcv.creq_mutex); + mutex_destroy(&server->root_setup_lock); + mutex_destroy(&server->mutex); out_fput2: if (server->info_filp) fput(server->info_filp); @@ -743,8 +754,6 @@ static void ncp_put_super(struct super_block *sb) { struct ncp_server *server = NCP_SBP(sb); - lock_kernel(); - ncp_lock_server(server); ncp_disconnect(server); ncp_unlock_server(server); @@ -756,6 +765,9 @@ static void ncp_put_super(struct super_block *sb) unload_nls(server->nls_vol); unload_nls(server->nls_io); #endif /* CONFIG_NCPFS_NLS */ + mutex_destroy(&server->rcv.creq_mutex); + mutex_destroy(&server->root_setup_lock); + mutex_destroy(&server->mutex); if (server->info_filp) fput(server->info_filp); @@ -771,8 +783,6 @@ static void ncp_put_super(struct super_block *sb) vfree(server->packet); sb->s_fs_info = NULL; kfree(server); - - unlock_kernel(); } static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf) @@ -851,10 +861,8 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) result = -EIO; - lock_kernel(); - server = NCP_SERVER(inode); - if ((!server) || !ncp_conn_valid(server)) + if (!server) /* How this could happen? */ goto out; /* ageing the dentry to force validation */ @@ -981,8 +989,6 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), inode, info_mask, &info); if (result != 0) { - result = -EACCES; - if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) { /* NetWare seems not to allow this. I do not know why. So, just tell the @@ -1005,7 +1011,8 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) mark_inode_dirty(inode); out: - unlock_kernel(); + if (result > 0) + result = -EACCES; return result; } diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 84a8cfc4e38..c2a1f9a155c 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -35,16 +35,11 @@ #define NCP_PACKET_SIZE_INTERNAL 65536 static int -ncp_get_fs_info(struct ncp_server * server, struct file *file, +ncp_get_fs_info(struct ncp_server * server, struct inode *inode, struct ncp_fs_info __user *arg) { - struct inode *inode = file->f_path.dentry->d_inode; struct ncp_fs_info info; - if (file_permission(file, MAY_WRITE) != 0 - && current_uid() != server->m.mounted_uid) - return -EACCES; - if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; @@ -65,16 +60,11 @@ ncp_get_fs_info(struct ncp_server * server, struct file *file, } static int -ncp_get_fs_info_v2(struct ncp_server * server, struct file *file, +ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode, struct ncp_fs_info_v2 __user * arg) { - struct inode *inode = file->f_path.dentry->d_inode; struct ncp_fs_info_v2 info2; - if (file_permission(file, MAY_WRITE) != 0 - && current_uid() != server->m.mounted_uid) - return -EACCES; - if (copy_from_user(&info2, arg, sizeof(info2))) return -EFAULT; @@ -136,16 +126,11 @@ struct compat_ncp_privatedata_ioctl #define NCP_IOC_SETPRIVATEDATA_32 _IOR('n', 10, struct compat_ncp_privatedata_ioctl) static int -ncp_get_compat_fs_info_v2(struct ncp_server * server, struct file *file, +ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode, struct compat_ncp_fs_info_v2 __user * arg) { - struct inode *inode = file->f_path.dentry->d_inode; struct compat_ncp_fs_info_v2 info2; - if (file_permission(file, MAY_WRITE) != 0 - && current_uid() != server->m.mounted_uid) - return -EACCES; - if (copy_from_user(&info2, arg, sizeof(info2))) return -EFAULT; @@ -182,11 +167,8 @@ ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg) struct nls_table *iocharset; struct nls_table *oldset_io; struct nls_table *oldset_cp; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - if (server->root_setuped) - return -EBUSY; + int utf8; + int err; if (copy_from_user(&user, arg, sizeof(user))) return -EFAULT; @@ -206,28 +188,40 @@ ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg) user.iocharset[NCP_IOCSNAME_LEN] = 0; if (!user.iocharset[0] || !strcmp(user.iocharset, "default")) { iocharset = load_nls_default(); - NCP_CLR_FLAG(server, NCP_FLAG_UTF8); + utf8 = 0; } else if (!strcmp(user.iocharset, "utf8")) { iocharset = load_nls_default(); - NCP_SET_FLAG(server, NCP_FLAG_UTF8); + utf8 = 1; } else { iocharset = load_nls(user.iocharset); if (!iocharset) { unload_nls(codepage); return -EBADRQC; } - NCP_CLR_FLAG(server, NCP_FLAG_UTF8); + utf8 = 0; } - oldset_cp = server->nls_vol; - server->nls_vol = codepage; - oldset_io = server->nls_io; - server->nls_io = iocharset; - + mutex_lock(&server->root_setup_lock); + if (server->root_setuped) { + oldset_cp = codepage; + oldset_io = iocharset; + err = -EBUSY; + } else { + if (utf8) + NCP_SET_FLAG(server, NCP_FLAG_UTF8); + else + NCP_CLR_FLAG(server, NCP_FLAG_UTF8); + oldset_cp = server->nls_vol; + server->nls_vol = codepage; + oldset_io = server->nls_io; + server->nls_io = iocharset; + err = 0; + } + mutex_unlock(&server->root_setup_lock); unload_nls(oldset_cp); unload_nls(oldset_io); - return 0; + return err; } static int @@ -237,6 +231,7 @@ ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg) int len; memset(&user, 0, sizeof(user)); + mutex_lock(&server->root_setup_lock); if (server->nls_vol && server->nls_vol->charset) { len = strlen(server->nls_vol->charset); if (len > NCP_IOCSNAME_LEN) @@ -254,6 +249,7 @@ ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg) strncpy(user.iocharset, server->nls_io->charset, len); user.iocharset[len] = 0; } + mutex_unlock(&server->root_setup_lock); if (copy_to_user(arg, &user, sizeof(user))) return -EFAULT; @@ -261,25 +257,19 @@ ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg) } #endif /* CONFIG_NCPFS_NLS */ -static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg) { - struct inode *inode = filp->f_dentry->d_inode; struct ncp_server *server = NCP_SERVER(inode); int result; struct ncp_ioctl_request request; char* bouncebuffer; void __user *argp = (void __user *)arg; - uid_t uid = current_uid(); switch (cmd) { #ifdef CONFIG_COMPAT case NCP_IOC_NCPREQUEST_32: #endif case NCP_IOC_NCPREQUEST: - if (file_permission(filp, MAY_WRITE) != 0 - && uid != server->m.mounted_uid) - return -EACCES; - #ifdef CONFIG_COMPAT if (cmd == NCP_IOC_NCPREQUEST_32) { struct compat_ncp_ioctl_request request32; @@ -314,7 +304,7 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) server->current_size = request.size; memcpy(server->packet, bouncebuffer, request.size); - result = ncp_request2(server, request.function, + result = ncp_request2(server, request.function, bouncebuffer, NCP_PACKET_SIZE_INTERNAL); if (result < 0) result = -EIO; @@ -331,69 +321,69 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case NCP_IOC_CONN_LOGGED_IN: - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE)) return -EINVAL; + mutex_lock(&server->root_setup_lock); if (server->root_setuped) - return -EBUSY; - server->root_setuped = 1; - return ncp_conn_logged_in(inode->i_sb); + result = -EBUSY; + else { + result = ncp_conn_logged_in(inode->i_sb); + if (result == 0) + server->root_setuped = 1; + } + mutex_unlock(&server->root_setup_lock); + return result; case NCP_IOC_GET_FS_INFO: - return ncp_get_fs_info(server, filp, argp); + return ncp_get_fs_info(server, inode, argp); case NCP_IOC_GET_FS_INFO_V2: - return ncp_get_fs_info_v2(server, filp, argp); + return ncp_get_fs_info_v2(server, inode, argp); #ifdef CONFIG_COMPAT case NCP_IOC_GET_FS_INFO_V2_32: - return ncp_get_compat_fs_info_v2(server, filp, argp); + return ncp_get_compat_fs_info_v2(server, inode, argp); #endif /* we have too many combinations of CONFIG_COMPAT, * CONFIG_64BIT and CONFIG_UID16, so just handle * any of the possible ioctls */ case NCP_IOC_GETMOUNTUID16: - case NCP_IOC_GETMOUNTUID32: - case NCP_IOC_GETMOUNTUID64: - if (file_permission(filp, MAY_READ) != 0 - && uid != server->m.mounted_uid) - return -EACCES; - - if (cmd == NCP_IOC_GETMOUNTUID16) { + { u16 uid; + SET_UID(uid, server->m.mounted_uid); if (put_user(uid, (u16 __user *)argp)) return -EFAULT; - } else if (cmd == NCP_IOC_GETMOUNTUID32) { - if (put_user(server->m.mounted_uid, - (u32 __user *)argp)) - return -EFAULT; - } else { - if (put_user(server->m.mounted_uid, - (u64 __user *)argp)) - return -EFAULT; + return 0; } + case NCP_IOC_GETMOUNTUID32: + if (put_user(server->m.mounted_uid, + (u32 __user *)argp)) + return -EFAULT; + return 0; + case NCP_IOC_GETMOUNTUID64: + if (put_user(server->m.mounted_uid, + (u64 __user *)argp)) + return -EFAULT; return 0; case NCP_IOC_GETROOT: { struct ncp_setroot_ioctl sr; - if (file_permission(filp, MAY_READ) != 0 - && uid != server->m.mounted_uid) - return -EACCES; - + result = -EACCES; + mutex_lock(&server->root_setup_lock); if (server->m.mounted_vol[0]) { struct dentry* dentry = inode->i_sb->s_root; if (dentry) { struct inode* s_inode = dentry->d_inode; - + if (s_inode) { sr.volNumber = NCP_FINFO(s_inode)->volNumber; sr.dirEntNum = NCP_FINFO(s_inode)->dirEntNum; sr.namespace = server->name_space[sr.volNumber]; + result = 0; } else DPRINTK("ncpfs: s_root->d_inode==NULL\n"); } else @@ -402,10 +392,12 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) sr.volNumber = -1; sr.namespace = 0; sr.dirEntNum = 0; + result = 0; } - if (copy_to_user(argp, &sr, sizeof(sr))) - return -EFAULT; - return 0; + mutex_unlock(&server->root_setup_lock); + if (!result && copy_to_user(argp, &sr, sizeof(sr))) + result = -EFAULT; + return result; } case NCP_IOC_SETROOT: @@ -416,103 +408,114 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) __le32 dosde; struct dentry* dentry; - if (!capable(CAP_SYS_ADMIN)) - { - return -EACCES; - } - if (server->root_setuped) return -EBUSY; if (copy_from_user(&sr, argp, sizeof(sr))) return -EFAULT; - if (sr.volNumber < 0) { - server->m.mounted_vol[0] = 0; - vnum = NCP_NUMBER_OF_VOLUMES; - de = 0; - dosde = 0; - } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) { - return -EINVAL; - } else if (ncp_mount_subdir(server, sr.volNumber, - sr.namespace, sr.dirEntNum, - &vnum, &de, &dosde)) { - return -ENOENT; - } - - dentry = inode->i_sb->s_root; - server->root_setuped = 1; - if (dentry) { - struct inode* s_inode = dentry->d_inode; - - if (s_inode) { - NCP_FINFO(s_inode)->volNumber = vnum; - NCP_FINFO(s_inode)->dirEntNum = de; - NCP_FINFO(s_inode)->DosDirNum = dosde; + mutex_lock(&server->root_setup_lock); + if (server->root_setuped) + result = -EBUSY; + else { + if (sr.volNumber < 0) { + server->m.mounted_vol[0] = 0; + vnum = NCP_NUMBER_OF_VOLUMES; + de = 0; + dosde = 0; + result = 0; + } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) { + result = -EINVAL; + } else if (ncp_mount_subdir(server, sr.volNumber, + sr.namespace, sr.dirEntNum, + &vnum, &de, &dosde)) { + result = -ENOENT; } else - DPRINTK("ncpfs: s_root->d_inode==NULL\n"); - } else - DPRINTK("ncpfs: s_root==NULL\n"); + result = 0; + + if (result == 0) { + dentry = inode->i_sb->s_root; + if (dentry) { + struct inode* s_inode = dentry->d_inode; + + if (s_inode) { + NCP_FINFO(s_inode)->volNumber = vnum; + NCP_FINFO(s_inode)->dirEntNum = de; + NCP_FINFO(s_inode)->DosDirNum = dosde; + server->root_setuped = 1; + } else { + DPRINTK("ncpfs: s_root->d_inode==NULL\n"); + result = -EIO; + } + } else { + DPRINTK("ncpfs: s_root==NULL\n"); + result = -EIO; + } + } + result = 0; + } + mutex_unlock(&server->root_setup_lock); - return 0; + return result; } -#ifdef CONFIG_NCPFS_PACKET_SIGNING +#ifdef CONFIG_NCPFS_PACKET_SIGNING case NCP_IOC_SIGN_INIT: - if (file_permission(filp, MAY_WRITE) != 0 - && uid != server->m.mounted_uid) - return -EACCES; - - if (argp) { - if (server->sign_wanted) - { - struct ncp_sign_init sign; + { + struct ncp_sign_init sign; + if (argp) if (copy_from_user(&sign, argp, sizeof(sign))) return -EFAULT; - memcpy(server->sign_root,sign.sign_root,8); - memcpy(server->sign_last,sign.sign_last,16); - server->sign_active = 1; + ncp_lock_server(server); + mutex_lock(&server->rcv.creq_mutex); + if (argp) { + if (server->sign_wanted) { + memcpy(server->sign_root,sign.sign_root,8); + memcpy(server->sign_last,sign.sign_last,16); + server->sign_active = 1; + } + /* ignore when signatures not wanted */ + } else { + server->sign_active = 0; } - /* ignore when signatures not wanted */ - } else { - server->sign_active = 0; + mutex_unlock(&server->rcv.creq_mutex); + ncp_unlock_server(server); + return 0; } - return 0; - + case NCP_IOC_SIGN_WANTED: - if (file_permission(filp, MAY_READ) != 0 - && uid != server->m.mounted_uid) - return -EACCES; - - if (put_user(server->sign_wanted, (int __user *)argp)) - return -EFAULT; - return 0; + { + int state; + + ncp_lock_server(server); + state = server->sign_wanted; + ncp_unlock_server(server); + if (put_user(state, (int __user *)argp)) + return -EFAULT; + return 0; + } case NCP_IOC_SET_SIGN_WANTED: { int newstate; - if (file_permission(filp, MAY_WRITE) != 0 - && uid != server->m.mounted_uid) - return -EACCES; - /* get only low 8 bits... */ if (get_user(newstate, (unsigned char __user *)argp)) return -EFAULT; + result = 0; + ncp_lock_server(server); if (server->sign_active) { /* cannot turn signatures OFF when active */ - if (!newstate) return -EINVAL; + if (!newstate) + result = -EINVAL; } else { server->sign_wanted = newstate != 0; } - return 0; + ncp_unlock_server(server); + return result; } #endif /* CONFIG_NCPFS_PACKET_SIGNING */ #ifdef CONFIG_NCPFS_IOCTL_LOCKING case NCP_IOC_LOCKUNLOCK: - if (file_permission(filp, MAY_WRITE) != 0 - && uid != server->m.mounted_uid) - return -EACCES; - { struct ncp_lock_ioctl rqdata; @@ -541,16 +544,13 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { return result; } - result = -EIO; - if (!ncp_conn_valid(server)) - goto outrel; result = -EISDIR; if (!S_ISREG(inode->i_mode)) goto outrel; if (rqdata.cmd == NCP_LOCK_CLEAR) { result = ncp_ClearPhysicalRecord(NCP_SERVER(inode), - NCP_FINFO(inode)->file_handle, + NCP_FINFO(inode)->file_handle, rqdata.offset, rqdata.length); if (result > 0) result = 0; /* no such lock */ @@ -573,7 +573,7 @@ static long __ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) rqdata.timeout); if (result > 0) result = -EAGAIN; } -outrel: +outrel: ncp_inode_close(inode); return result; } @@ -581,60 +581,62 @@ outrel: #ifdef CONFIG_COMPAT case NCP_IOC_GETOBJECTNAME_32: - if (uid != server->m.mounted_uid) - return -EACCES; { struct compat_ncp_objectname_ioctl user; size_t outl; if (copy_from_user(&user, argp, sizeof(user))) return -EFAULT; + down_read(&server->auth_rwsem); user.auth_type = server->auth.auth_type; outl = user.object_name_len; user.object_name_len = server->auth.object_name_len; if (outl > user.object_name_len) outl = user.object_name_len; + result = 0; if (outl) { if (copy_to_user(compat_ptr(user.object_name), server->auth.object_name, - outl)) return -EFAULT; + outl)) + result = -EFAULT; } - if (copy_to_user(argp, &user, sizeof(user))) - return -EFAULT; - return 0; + up_read(&server->auth_rwsem); + if (!result && copy_to_user(argp, &user, sizeof(user))) + result = -EFAULT; + return result; } #endif case NCP_IOC_GETOBJECTNAME: - if (uid != server->m.mounted_uid) - return -EACCES; { struct ncp_objectname_ioctl user; size_t outl; if (copy_from_user(&user, argp, sizeof(user))) return -EFAULT; + down_read(&server->auth_rwsem); user.auth_type = server->auth.auth_type; outl = user.object_name_len; user.object_name_len = server->auth.object_name_len; if (outl > user.object_name_len) outl = user.object_name_len; + result = 0; if (outl) { if (copy_to_user(user.object_name, server->auth.object_name, - outl)) return -EFAULT; + outl)) + result = -EFAULT; } - if (copy_to_user(argp, &user, sizeof(user))) - return -EFAULT; - return 0; + up_read(&server->auth_rwsem); + if (!result && copy_to_user(argp, &user, sizeof(user))) + result = -EFAULT; + return result; } #ifdef CONFIG_COMPAT case NCP_IOC_SETOBJECTNAME_32: #endif case NCP_IOC_SETOBJECTNAME: - if (uid != server->m.mounted_uid) - return -EACCES; { struct ncp_objectname_ioctl user; void* newname; @@ -666,9 +668,7 @@ outrel: } else { newname = NULL; } - /* enter critical section */ - /* maybe that kfree can sleep so do that this way */ - /* it is at least more SMP friendly (in future...) */ + down_write(&server->auth_rwsem); oldname = server->auth.object_name; oldnamelen = server->auth.object_name_len; oldprivate = server->priv.data; @@ -678,7 +678,7 @@ outrel: server->auth.object_name = newname; server->priv.len = 0; server->priv.data = NULL; - /* leave critical section */ + up_write(&server->auth_rwsem); kfree(oldprivate); kfree(oldname); return 0; @@ -688,8 +688,6 @@ outrel: case NCP_IOC_GETPRIVATEDATA_32: #endif case NCP_IOC_GETPRIVATEDATA: - if (uid != server->m.mounted_uid) - return -EACCES; { struct ncp_privatedata_ioctl user; size_t outl; @@ -706,14 +704,20 @@ outrel: if (copy_from_user(&user, argp, sizeof(user))) return -EFAULT; + down_read(&server->auth_rwsem); outl = user.len; user.len = server->priv.len; if (outl > user.len) outl = user.len; + result = 0; if (outl) { if (copy_to_user(user.data, server->priv.data, - outl)) return -EFAULT; + outl)) + result = -EFAULT; } + up_read(&server->auth_rwsem); + if (result) + return result; #ifdef CONFIG_COMPAT if (cmd == NCP_IOC_GETPRIVATEDATA_32) { struct compat_ncp_privatedata_ioctl user32; @@ -733,8 +737,6 @@ outrel: case NCP_IOC_SETPRIVATEDATA_32: #endif case NCP_IOC_SETPRIVATEDATA: - if (uid != server->m.mounted_uid) - return -EACCES; { struct ncp_privatedata_ioctl user; void* new; @@ -762,12 +764,12 @@ outrel: } else { new = NULL; } - /* enter critical section */ + down_write(&server->auth_rwsem); old = server->priv.data; oldlen = server->priv.len; server->priv.len = user.len; server->priv.data = new; - /* leave critical section */ + up_write(&server->auth_rwsem); kfree(old); return 0; } @@ -775,17 +777,13 @@ outrel: #ifdef CONFIG_NCPFS_NLS case NCP_IOC_SETCHARSETS: return ncp_set_charsets(server, argp); - + case NCP_IOC_GETCHARSETS: return ncp_get_charsets(server, argp); #endif /* CONFIG_NCPFS_NLS */ case NCP_IOC_SETDENTRYTTL: - if (file_permission(filp, MAY_WRITE) != 0 && - uid != server->m.mounted_uid) - return -EACCES; - { u_int32_t user; @@ -795,13 +793,13 @@ outrel: if (user > 20000) return -EINVAL; user = (user * HZ) / 1000; - server->dentry_ttl = user; + atomic_set(&server->dentry_ttl, user); return 0; } - + case NCP_IOC_GETDENTRYTTL: { - u_int32_t user = (server->dentry_ttl * 1000) / HZ; + u_int32_t user = (atomic_read(&server->dentry_ttl) * 1000) / HZ; if (copy_to_user(argp, &user, sizeof(user))) return -EFAULT; return 0; @@ -811,59 +809,103 @@ outrel: return -EINVAL; } -static int ncp_ioctl_need_write(unsigned int cmd) +long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { + struct inode *inode = filp->f_dentry->d_inode; + struct ncp_server *server = NCP_SERVER(inode); + uid_t uid = current_uid(); + int need_drop_write = 0; + long ret; + switch (cmd) { - case NCP_IOC_GET_FS_INFO: - case NCP_IOC_GET_FS_INFO_V2: - case NCP_IOC_NCPREQUEST: - case NCP_IOC_SETDENTRYTTL: - case NCP_IOC_SIGN_INIT: - case NCP_IOC_LOCKUNLOCK: - case NCP_IOC_SET_SIGN_WANTED: - return 1; - case NCP_IOC_GETOBJECTNAME: - case NCP_IOC_SETOBJECTNAME: - case NCP_IOC_GETPRIVATEDATA: - case NCP_IOC_SETPRIVATEDATA: case NCP_IOC_SETCHARSETS: - case NCP_IOC_GETCHARSETS: case NCP_IOC_CONN_LOGGED_IN: - case NCP_IOC_GETDENTRYTTL: - case NCP_IOC_GETMOUNTUID2: - case NCP_IOC_SIGN_WANTED: - case NCP_IOC_GETROOT: case NCP_IOC_SETROOT: - return 0; - default: - /* unknown IOCTL command, assume write */ - return 1; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EACCES; + goto out; + } + break; } -} - -long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - long ret; - - lock_kernel(); - if (ncp_ioctl_need_write(cmd)) { + if (server->m.mounted_uid != uid) { + switch (cmd) { /* - * inside the ioctl(), any failures which - * are because of file_permission() are - * -EACCESS, so it seems consistent to keep - * that here. + * Only mount owner can issue these ioctls. Information + * necessary to authenticate to other NDS servers are + * stored here. */ - if (mnt_want_write(filp->f_path.mnt)) { + case NCP_IOC_GETOBJECTNAME: + case NCP_IOC_SETOBJECTNAME: + case NCP_IOC_GETPRIVATEDATA: + case NCP_IOC_SETPRIVATEDATA: +#ifdef CONFIG_COMPAT + case NCP_IOC_GETOBJECTNAME_32: + case NCP_IOC_SETOBJECTNAME_32: + case NCP_IOC_GETPRIVATEDATA_32: + case NCP_IOC_SETPRIVATEDATA_32: +#endif ret = -EACCES; goto out; + /* + * These require write access on the inode if user id + * does not match. Note that they do not write to the + * file... But old code did mnt_want_write, so I keep + * it as is. Of course not for mountpoint owner, as + * that breaks read-only mounts altogether as ncpmount + * needs working NCP_IOC_NCPREQUEST and + * NCP_IOC_GET_FS_INFO. Some of these codes (setdentryttl, + * signinit, setsignwanted) should be probably restricted + * to owner only, or even more to CAP_SYS_ADMIN). + */ + case NCP_IOC_GET_FS_INFO: + case NCP_IOC_GET_FS_INFO_V2: + case NCP_IOC_NCPREQUEST: + case NCP_IOC_SETDENTRYTTL: + case NCP_IOC_SIGN_INIT: + case NCP_IOC_LOCKUNLOCK: + case NCP_IOC_SET_SIGN_WANTED: +#ifdef CONFIG_COMPAT + case NCP_IOC_GET_FS_INFO_V2_32: + case NCP_IOC_NCPREQUEST_32: +#endif + ret = mnt_want_write_file(filp); + if (ret) + goto out; + need_drop_write = 1; + ret = inode_permission(inode, MAY_WRITE); + if (ret) + goto outDropWrite; + break; + /* + * Read access required. + */ + case NCP_IOC_GETMOUNTUID16: + case NCP_IOC_GETMOUNTUID32: + case NCP_IOC_GETMOUNTUID64: + case NCP_IOC_GETROOT: + case NCP_IOC_SIGN_WANTED: + ret = inode_permission(inode, MAY_READ); + if (ret) + goto out; + break; + /* + * Anybody can read these. + */ + case NCP_IOC_GETCHARSETS: + case NCP_IOC_GETDENTRYTTL: + default: + /* Three codes below are protected by CAP_SYS_ADMIN above. */ + case NCP_IOC_SETCHARSETS: + case NCP_IOC_CONN_LOGGED_IN: + case NCP_IOC_SETROOT: + break; } } - ret = __ncp_ioctl(filp, cmd, arg); - if (ncp_ioctl_need_write(cmd)) + ret = __ncp_ioctl(inode, cmd, arg); +outDropWrite: + if (need_drop_write) mnt_drop_write(filp->f_path.mnt); - out: - unlock_kernel(); return ret; } @@ -872,10 +914,8 @@ long ncp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; - lock_kernel(); arg = (unsigned long) compat_ptr(arg); ret = ncp_ioctl(file, cmd, arg); - unlock_kernel(); return ret; } #endif diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index 0ec6237a597..a95615a0b6a 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -107,17 +107,17 @@ ncp_reply_data(struct ncp_server *server, int offset) return &(server->packet[sizeof(struct ncp_reply_header) + offset]); } -static inline u8 BVAL(void *data) +static inline u8 BVAL(const void *data) { - return *(u8 *)data; + return *(const u8 *)data; } static u8 ncp_reply_byte(struct ncp_server *server, int offset) { - return *(u8 *)ncp_reply_data(server, offset); + return *(const u8 *)ncp_reply_data(server, offset); } -static inline u16 WVAL_LH(void *data) +static inline u16 WVAL_LH(const void *data) { return get_unaligned_le16(data); } @@ -134,7 +134,7 @@ ncp_reply_be16(struct ncp_server *server, int offset) return get_unaligned_be16(ncp_reply_data(server, offset)); } -static inline u32 DVAL_LH(void *data) +static inline u32 DVAL_LH(const void *data) { return get_unaligned_le32(data); } @@ -349,9 +349,9 @@ int ncp_dirhandle_free(struct ncp_server* server, __u8 dirhandle) { return result; } -void ncp_extract_file_info(void *structure, struct nw_info_struct *target) +void ncp_extract_file_info(const void *structure, struct nw_info_struct *target) { - __u8 *name_len; + const __u8 *name_len; const int info_struct_size = offsetof(struct nw_info_struct, nameLen); memcpy(target, structure, info_struct_size); @@ -364,7 +364,7 @@ void ncp_extract_file_info(void *structure, struct nw_info_struct *target) } #ifdef CONFIG_NCPFS_NFS_NS -static inline void ncp_extract_nfs_info(unsigned char *structure, +static inline void ncp_extract_nfs_info(const unsigned char *structure, struct nw_nfs_info *target) { target->mode = DVAL_LH(structure); @@ -417,7 +417,7 @@ int ncp_obtain_nfs_info(struct ncp_server *server, * Returns information for a (one-component) name relative to * the specified directory. */ -int ncp_obtain_info(struct ncp_server *server, struct inode *dir, char *path, +int ncp_obtain_info(struct ncp_server *server, struct inode *dir, const char *path, struct nw_info_struct *target) { __u8 volnum = NCP_FINFO(dir)->volNumber; @@ -452,16 +452,16 @@ out: #ifdef CONFIG_NCPFS_NFS_NS static int ncp_obtain_DOS_dir_base(struct ncp_server *server, - __u8 volnum, __le32 dirent, - char *path, /* At most 1 component */ + __u8 ns, __u8 volnum, __le32 dirent, + const char *path, /* At most 1 component */ __le32 *DOS_dir_base) { int result; ncp_init_request(server); ncp_add_byte(server, 6); /* subfunction */ - ncp_add_byte(server, server->name_space[volnum]); - ncp_add_byte(server, server->name_space[volnum]); + ncp_add_byte(server, ns); + ncp_add_byte(server, ns); ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */ ncp_add_dword(server, RIM_DIRECTORY); ncp_add_handle_path(server, volnum, dirent, 1, path); @@ -523,10 +523,27 @@ ncp_get_known_namespace(struct ncp_server *server, __u8 volume) #endif /* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */ } +int +ncp_update_known_namespace(struct ncp_server *server, __u8 volume, int *ret_ns) +{ + int ns = ncp_get_known_namespace(server, volume); + + if (ret_ns) + *ret_ns = ns; + + DPRINTK("lookup_vol: namespace[%d] = %d\n", + volume, server->name_space[volume]); + + if (server->name_space[volume] == ns) + return 0; + server->name_space[volume] = ns; + return 1; +} + static int ncp_ObtainSpecificDirBase(struct ncp_server *server, __u8 nsSrc, __u8 nsDst, __u8 vol_num, __le32 dir_base, - char *path, /* At most 1 component */ + const char *path, /* At most 1 component */ __le32 *dirEntNum, __le32 *DosDirNum) { int result; @@ -560,14 +577,13 @@ ncp_mount_subdir(struct ncp_server *server, { int dstNS; int result; - - dstNS = ncp_get_known_namespace(server, volNumber); + + ncp_update_known_namespace(server, volNumber, &dstNS); if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber, dirEntNum, NULL, newDirEnt, newDosEnt)) != 0) { return result; } - server->name_space[volNumber] = dstNS; *volume = volNumber; server->m.mounted_vol[1] = 0; server->m.mounted_vol[0] = 'X'; @@ -575,11 +591,10 @@ ncp_mount_subdir(struct ncp_server *server, } int -ncp_get_volume_root(struct ncp_server *server, const char *volname, - __u32* volume, __le32* dirent, __le32* dosdirent) +ncp_get_volume_root(struct ncp_server *server, + const char *volname, __u32* volume, __le32* dirent, __le32* dosdirent) { int result; - __u8 volnum; DPRINTK("ncp_get_volume_root: looking up vol %s\n", volname); @@ -601,21 +616,14 @@ ncp_get_volume_root(struct ncp_server *server, const char *volname, return result; } *dirent = *dosdirent = ncp_reply_dword(server, 4); - volnum = ncp_reply_byte(server, 8); + *volume = ncp_reply_byte(server, 8); ncp_unlock_server(server); - *volume = volnum; - - server->name_space[volnum] = ncp_get_known_namespace(server, volnum); - - DPRINTK("lookup_vol: namespace[%d] = %d\n", - volnum, server->name_space[volnum]); - return 0; } int -ncp_lookup_volume(struct ncp_server *server, const char *volname, - struct nw_info_struct *target) +ncp_lookup_volume(struct ncp_server *server, + const char *volname, struct nw_info_struct *target) { int result; @@ -625,6 +633,7 @@ ncp_lookup_volume(struct ncp_server *server, const char *volname, if (result) { return result; } + ncp_update_known_namespace(server, target->volNumber, NULL); target->nameLen = strlen(volname); memcpy(target->entryName, volname, target->nameLen+1); target->attributes = aDIR; @@ -676,8 +685,8 @@ int ncp_modify_nfs_info(struct ncp_server *server, __u8 volnum, __le32 dirent, { int result = 0; + ncp_init_request(server); if (server->name_space[volnum] == NW_NS_NFS) { - ncp_init_request(server); ncp_add_byte(server, 25); /* subfunction */ ncp_add_byte(server, server->name_space[volnum]); ncp_add_byte(server, NW_NS_NFS); @@ -690,8 +699,8 @@ int ncp_modify_nfs_info(struct ncp_server *server, __u8 volnum, __le32 dirent, ncp_add_dword_lh(server, 1); /* nlinks */ ncp_add_dword_lh(server, rdev); result = ncp_request(server, 87); - ncp_unlock_server(server); } + ncp_unlock_server(server); return result; } #endif @@ -700,7 +709,7 @@ int ncp_modify_nfs_info(struct ncp_server *server, __u8 volnum, __le32 dirent, static int ncp_DeleteNSEntry(struct ncp_server *server, __u8 have_dir_base, __u8 volnum, __le32 dirent, - char* name, __u8 ns, __le16 attr) + const char* name, __u8 ns, __le16 attr) { int result; @@ -734,23 +743,25 @@ ncp_del_file_or_subdir2(struct ncp_server *server, int ncp_del_file_or_subdir(struct ncp_server *server, - struct inode *dir, char *name) + struct inode *dir, const char *name) { __u8 volnum = NCP_FINFO(dir)->volNumber; __le32 dirent = NCP_FINFO(dir)->dirEntNum; + int name_space; + name_space = server->name_space[volnum]; #ifdef CONFIG_NCPFS_NFS_NS - if (server->name_space[volnum]==NW_NS_NFS) + if (name_space == NW_NS_NFS) { int result; - result=ncp_obtain_DOS_dir_base(server, volnum, dirent, name, &dirent); + result=ncp_obtain_DOS_dir_base(server, name_space, volnum, dirent, name, &dirent); if (result) return result; - return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, cpu_to_le16(0x8006)); + name = NULL; + name_space = NW_NS_DOS; } - else #endif /* CONFIG_NCPFS_NFS_NS */ - return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, server->name_space[volnum], cpu_to_le16(0x8006)); + return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, name_space, cpu_to_le16(0x8006)); } static inline void ConvertToNWfromDWORD(__u16 v0, __u16 v1, __u8 ret[6]) @@ -765,7 +776,7 @@ static inline void ConvertToNWfromDWORD(__u16 v0, __u16 v1, __u8 ret[6]) /* If both dir and name are NULL, then in target there's already a looked-up entry that wants to be opened. */ int ncp_open_create_file_or_subdir(struct ncp_server *server, - struct inode *dir, char *name, + struct inode *dir, const char *name, int open_create_mode, __le32 create_attributes, __le16 desired_acc_rights, @@ -890,8 +901,8 @@ int ncp_search_for_fileset(struct ncp_server *server, static int ncp_RenameNSEntry(struct ncp_server *server, - struct inode *old_dir, char *old_name, __le16 old_type, - struct inode *new_dir, char *new_name) + struct inode *old_dir, const char *old_name, __le16 old_type, + struct inode *new_dir, const char *new_name) { int result = -EINVAL; @@ -929,8 +940,8 @@ out: } int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, - struct inode *old_dir, char *old_name, - struct inode *new_dir, char *new_name) + struct inode *old_dir, const char *old_name, + struct inode *new_dir, const char *new_name) { int result; __le16 old_type = cpu_to_le16(0x06); @@ -958,7 +969,7 @@ int ncp_read_kernel(struct ncp_server *server, const char *file_id, __u32 offset, __u16 to_read, char *target, int *bytes_read) { - char *source; + const char *source; int result; ncp_init_request(server); diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 2441d1ab57d..3c57eca634c 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -65,10 +65,11 @@ static inline void ncp_inode_close(struct inode *inode) { atomic_dec(&NCP_FINFO(inode)->opened); } -void ncp_extract_file_info(void* src, struct nw_info_struct* target); -int ncp_obtain_info(struct ncp_server *server, struct inode *, char *, +void ncp_extract_file_info(const void* src, struct nw_info_struct* target); +int ncp_obtain_info(struct ncp_server *server, struct inode *, const char *, struct nw_info_struct *target); int ncp_obtain_nfs_info(struct ncp_server *server, struct nw_info_struct *target); +int ncp_update_known_namespace(struct ncp_server *server, __u8 volume, int *ret_ns); int ncp_get_volume_root(struct ncp_server *server, const char *volname, __u32 *volume, __le32 *dirent, __le32 *dosdirent); int ncp_lookup_volume(struct ncp_server *, const char *, struct nw_info_struct *); @@ -80,8 +81,8 @@ int ncp_modify_nfs_info(struct ncp_server *, __u8 volnum, __le32 dirent, __u32 mode, __u32 rdev); int ncp_del_file_or_subdir2(struct ncp_server *, struct dentry*); -int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *); -int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, char *, +int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, const char *); +int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, const char *, int, __le32, __le16, struct ncp_entry_info *); int ncp_initialize_search(struct ncp_server *, struct inode *, @@ -93,7 +94,7 @@ int ncp_search_for_fileset(struct ncp_server *server, char** rbuf, size_t* rsize); int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, - struct inode *, char *, struct inode *, char *); + struct inode *, const char *, struct inode *, const char *); int @@ -170,13 +171,13 @@ static inline int ncp_strnicmp(struct nls_table *t, const unsigned char *s1, #endif /* CONFIG_NCPFS_NLS */ #define NCP_GET_AGE(dentry) (jiffies - (dentry)->d_time) -#define NCP_MAX_AGE(server) ((server)->dentry_ttl) +#define NCP_MAX_AGE(server) atomic_read(&(server)->dentry_ttl) #define NCP_TEST_AGE(server,dentry) (NCP_GET_AGE(dentry) < NCP_MAX_AGE(server)) static inline void ncp_age_dentry(struct ncp_server* server, struct dentry* dentry) { - dentry->d_time = jiffies - server->dentry_ttl; + dentry->d_time = jiffies - NCP_MAX_AGE(server); } static inline void diff --git a/fs/ncpfs/ncpsign_kernel.c b/fs/ncpfs/ncpsign_kernel.c index 7c0b5c21e6c..d8b2d7e6910 100644 --- a/fs/ncpfs/ncpsign_kernel.c +++ b/fs/ncpfs/ncpsign_kernel.c @@ -15,21 +15,21 @@ /* i386: 32-bit, little endian, handles mis-alignment */ #ifdef __i386__ -#define GET_LE32(p) (*(int *)(p)) +#define GET_LE32(p) (*(const int *)(p)) #define PUT_LE32(p,v) { *(int *)(p)=v; } #else /* from include/ncplib.h */ -#define BVAL(buf,pos) (((__u8 *)(buf))[pos]) +#define BVAL(buf,pos) (((const __u8 *)(buf))[pos]) #define PVAL(buf,pos) ((unsigned)BVAL(buf,pos)) -#define BSET(buf,pos,val) (BVAL(buf,pos) = (val)) +#define BSET(buf,pos,val) (((__u8 *)(buf))[pos] = (val)) static inline __u16 -WVAL_LH(__u8 * buf, int pos) +WVAL_LH(const __u8 * buf, int pos) { return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8; } static inline __u32 -DVAL_LH(__u8 * buf, int pos) +DVAL_LH(const __u8 * buf, int pos) { return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16; } diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index c7ff6c700a6..668bd267346 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -746,7 +746,6 @@ static int ncp_do_request(struct ncp_server *server, int size, return -EIO; } if (!ncp_conn_valid(server)) { - printk(KERN_ERR "ncpfs: Connection invalid!\n"); return -EIO; } { -- cgit v1.2.3-18-g5258 From b89f432133851a01c0d28822f11cbdcc15781a75 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 18 Sep 2010 15:09:31 +0200 Subject: fs/locks.c: prepare for BKL removal This prepares the removal of the big kernel lock from the file locking code. We still use the BKL as long as fs/lockd uses it and ceph might sleep, but we can flip the definition to a private spinlock as soon as that's done. All users outside of fs/lockd get converted to use lock_flocks() instead of lock_kernel() where appropriate. Based on an earlier patch to use a spinlock from Matthew Wilcox, who has attempted this a few times before, the earliest patch from over 10 years ago turned it into a semaphore, which ended up being slower than the BKL and was subsequently reverted. Someone should do some serious performance testing when this becomes a spinlock, since this has caused problems before. Using a spinlock should be at least as good as the BKL in theory, but who knows... Signed-off-by: Arnd Bergmann Acked-by: Matthew Wilcox Cc: Christoph Hellwig Cc: Trond Myklebust Cc: "J. Bruce Fields" Cc: Andrew Morton Cc: Miklos Szeredi Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: John Kacur Cc: Sage Weil Cc: linux-kernel@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org --- fs/afs/flock.c | 5 +-- fs/cifs/cifsfs.c | 4 +- fs/gfs2/file.c | 2 + fs/locks.c | 112 +++++++++++++++++++++++++++++++--------------------- fs/nfs/delegation.c | 10 ++--- fs/nfs/nfs4state.c | 10 ++--- fs/nfsd/nfs4state.c | 6 +-- 7 files changed, 87 insertions(+), 62 deletions(-) (limited to 'fs') diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 0931bc1325e..757d664575d 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include "internal.h" #define AFS_LOCK_GRANTED 0 @@ -274,7 +273,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; - lock_kernel(); + lock_flocks(); /* make sure we've got a callback on this file and that our view of the * data version is up to date */ @@ -421,7 +420,7 @@ given_lock: afs_vnode_fetch_status(vnode, NULL, key); error: - unlock_kernel(); + unlock_flocks(); _leave(" = %d", ret); return ret; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 4e273f7793f..50208c15309 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -562,8 +562,8 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) { - /* note that this is called by vfs setlease with the BKL held - although I doubt that BKL is needed here in cifs */ + /* note that this is called by vfs setlease with lock_flocks held + to protect *lease from going away */ struct inode *inode = file->f_path.dentry->d_inode; if (!(S_ISREG(inode->i_mode))) diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 4edd662c823..8fcfefb9607 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -620,6 +620,8 @@ static ssize_t gfs2_file_aio_write(struct kiocb *iocb, const struct iovec *iov, * cluster; until we do, disable leases (by just returning -EINVAL), * unless the administrator has requested purely local locking. * + * Locking: called under lock_flocks + * * Returns: errno */ diff --git a/fs/locks.c b/fs/locks.c index ab24d49fc04..8b2b6ad56a0 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -143,6 +143,22 @@ int lease_break_time = 45; static LIST_HEAD(file_lock_list); static LIST_HEAD(blocked_list); +/* + * Protects the two list heads above, plus the inode->i_flock list + * FIXME: should use a spinlock, once lockd and ceph are ready. + */ +void lock_flocks(void) +{ + lock_kernel(); +} +EXPORT_SYMBOL_GPL(lock_flocks); + +void unlock_flocks(void) +{ + unlock_kernel(); +} +EXPORT_SYMBOL_GPL(unlock_flocks); + static struct kmem_cache *filelock_cache __read_mostly; /* Allocate an empty lock structure. */ @@ -511,9 +527,9 @@ static void __locks_delete_block(struct file_lock *waiter) */ static void locks_delete_block(struct file_lock *waiter) { - lock_kernel(); + lock_flocks(); __locks_delete_block(waiter); - unlock_kernel(); + unlock_flocks(); } /* Insert waiter into blocker's block list. @@ -644,7 +660,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl) { struct file_lock *cfl; - lock_kernel(); + lock_flocks(); for (cfl = filp->f_path.dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { if (!IS_POSIX(cfl)) continue; @@ -657,7 +673,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl) fl->fl_pid = pid_vnr(cfl->fl_nspid); } else fl->fl_type = F_UNLCK; - unlock_kernel(); + unlock_flocks(); return; } EXPORT_SYMBOL(posix_test_lock); @@ -730,18 +746,16 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) int error = 0; int found = 0; - lock_kernel(); - if (request->fl_flags & FL_ACCESS) - goto find_conflict; - - if (request->fl_type != F_UNLCK) { - error = -ENOMEM; + if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { new_fl = locks_alloc_lock(); - if (new_fl == NULL) - goto out; - error = 0; + if (!new_fl) + return -ENOMEM; } + lock_flocks(); + if (request->fl_flags & FL_ACCESS) + goto find_conflict; + for_each_lock(inode, before) { struct file_lock *fl = *before; if (IS_POSIX(fl)) @@ -767,8 +781,11 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) * If a higher-priority process was blocked on the old file lock, * give it the opportunity to lock the file. */ - if (found) + if (found) { + unlock_flocks(); cond_resched(); + lock_flocks(); + } find_conflict: for_each_lock(inode, before) { @@ -794,7 +811,7 @@ find_conflict: error = 0; out: - unlock_kernel(); + unlock_flocks(); if (new_fl) locks_free_lock(new_fl); return error; @@ -823,7 +840,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str new_fl2 = locks_alloc_lock(); } - lock_kernel(); + lock_flocks(); if (request->fl_type != F_UNLCK) { for_each_lock(inode, before) { fl = *before; @@ -991,7 +1008,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str locks_wake_up_blocks(left); } out: - unlock_kernel(); + unlock_flocks(); /* * Free any unused locks. */ @@ -1066,14 +1083,14 @@ int locks_mandatory_locked(struct inode *inode) /* * Search the lock list for this inode for any POSIX locks. */ - lock_kernel(); + lock_flocks(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!IS_POSIX(fl)) continue; if (fl->fl_owner != owner) break; } - unlock_kernel(); + unlock_flocks(); return fl ? -EAGAIN : 0; } @@ -1186,7 +1203,7 @@ int __break_lease(struct inode *inode, unsigned int mode) new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK); - lock_kernel(); + lock_flocks(); time_out_leases(inode); @@ -1247,8 +1264,10 @@ restart: break_time++; } locks_insert_block(flock, new_fl); + unlock_flocks(); error = wait_event_interruptible_timeout(new_fl->fl_wait, !new_fl->fl_next, break_time); + lock_flocks(); __locks_delete_block(new_fl); if (error >= 0) { if (error == 0) @@ -1263,7 +1282,7 @@ restart: } out: - unlock_kernel(); + unlock_flocks(); if (!IS_ERR(new_fl)) locks_free_lock(new_fl); return error; @@ -1319,7 +1338,7 @@ int fcntl_getlease(struct file *filp) struct file_lock *fl; int type = F_UNLCK; - lock_kernel(); + lock_flocks(); time_out_leases(filp->f_path.dentry->d_inode); for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl); fl = fl->fl_next) { @@ -1328,7 +1347,7 @@ int fcntl_getlease(struct file *filp) break; } } - unlock_kernel(); + unlock_flocks(); return type; } @@ -1341,7 +1360,7 @@ int fcntl_getlease(struct file *filp) * The (input) flp->fl_lmops->fl_break function is required * by break_lease(). * - * Called with kernel lock held. + * Called with file_lock_lock held. */ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) { @@ -1436,7 +1455,15 @@ out: } EXPORT_SYMBOL(generic_setlease); - /** +static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease) +{ + if (filp->f_op && filp->f_op->setlease) + return filp->f_op->setlease(filp, arg, lease); + else + return generic_setlease(filp, arg, lease); +} + +/** * vfs_setlease - sets a lease on an open file * @filp: file pointer * @arg: type of lease to obtain @@ -1467,12 +1494,9 @@ int vfs_setlease(struct file *filp, long arg, struct file_lock **lease) { int error; - lock_kernel(); - if (filp->f_op && filp->f_op->setlease) - error = filp->f_op->setlease(filp, arg, lease); - else - error = generic_setlease(filp, arg, lease); - unlock_kernel(); + lock_flocks(); + error = __vfs_setlease(filp, arg, lease); + unlock_flocks(); return error; } @@ -1499,9 +1523,9 @@ int fcntl_setlease(unsigned int fd, struct file *filp, long arg) if (error) return error; - lock_kernel(); + lock_flocks(); - error = vfs_setlease(filp, arg, &flp); + error = __vfs_setlease(filp, arg, &flp); if (error || arg == F_UNLCK) goto out_unlock; @@ -1516,7 +1540,7 @@ int fcntl_setlease(unsigned int fd, struct file *filp, long arg) error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); out_unlock: - unlock_kernel(); + unlock_flocks(); return error; } @@ -2020,7 +2044,7 @@ void locks_remove_flock(struct file *filp) fl.fl_ops->fl_release_private(&fl); } - lock_kernel(); + lock_flocks(); before = &inode->i_flock; while ((fl = *before) != NULL) { @@ -2038,7 +2062,7 @@ void locks_remove_flock(struct file *filp) } before = &fl->fl_next; } - unlock_kernel(); + unlock_flocks(); } /** @@ -2053,12 +2077,12 @@ posix_unblock_lock(struct file *filp, struct file_lock *waiter) { int status = 0; - lock_kernel(); + lock_flocks(); if (waiter->fl_next) __locks_delete_block(waiter); else status = -ENOENT; - unlock_kernel(); + unlock_flocks(); return status; } @@ -2172,7 +2196,7 @@ static int locks_show(struct seq_file *f, void *v) static void *locks_start(struct seq_file *f, loff_t *pos) { - lock_kernel(); + lock_flocks(); f->private = (void *)1; return seq_list_start(&file_lock_list, *pos); } @@ -2184,7 +2208,7 @@ static void *locks_next(struct seq_file *f, void *v, loff_t *pos) static void locks_stop(struct seq_file *f, void *v) { - unlock_kernel(); + unlock_flocks(); } static const struct seq_operations locks_seq_operations = { @@ -2231,7 +2255,7 @@ int lock_may_read(struct inode *inode, loff_t start, unsigned long len) { struct file_lock *fl; int result = 1; - lock_kernel(); + lock_flocks(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (IS_POSIX(fl)) { if (fl->fl_type == F_RDLCK) @@ -2248,7 +2272,7 @@ int lock_may_read(struct inode *inode, loff_t start, unsigned long len) result = 0; break; } - unlock_kernel(); + unlock_flocks(); return result; } @@ -2271,7 +2295,7 @@ int lock_may_write(struct inode *inode, loff_t start, unsigned long len) { struct file_lock *fl; int result = 1; - lock_kernel(); + lock_flocks(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (IS_POSIX(fl)) { if ((fl->fl_end < start) || (fl->fl_start > (start + len))) @@ -2286,7 +2310,7 @@ int lock_may_write(struct inode *inode, loff_t start, unsigned long len) result = 0; break; } - unlock_kernel(); + unlock_flocks(); return result; } diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index b9c3c43cea1..232a7eead33 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -71,20 +71,20 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ if (inode->i_flock == NULL) goto out; - /* Protect inode->i_flock using the BKL */ - lock_kernel(); + /* Protect inode->i_flock using the file locks lock */ + lock_flocks(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) continue; if (nfs_file_open_context(fl->fl_file) != ctx) continue; - unlock_kernel(); + unlock_flocks(); status = nfs4_lock_delegation_recall(state, fl); if (status < 0) goto out; - lock_kernel(); + lock_flocks(); } - unlock_kernel(); + unlock_flocks(); out: return status; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 3e2f19b04c0..96524c5dca6 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -40,7 +40,7 @@ #include #include -#include +#include #include #include #include @@ -970,13 +970,13 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ /* Guard against delegation returns and new lock/unlock calls */ down_write(&nfsi->rwsem); /* Protect inode->i_flock using the BKL */ - lock_kernel(); + lock_flocks(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) continue; if (nfs_file_open_context(fl->fl_file)->state != state) continue; - unlock_kernel(); + unlock_flocks(); status = ops->recover_lock(state, fl); switch (status) { case 0: @@ -1003,9 +1003,9 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ /* kill_proc(fl->fl_pid, SIGLOST, 1); */ status = 0; } - lock_kernel(); + lock_flocks(); } - unlock_kernel(); + unlock_flocks(); out: up_write(&nfsi->rwsem); return status; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cf0d2ffb3c8..a7292fcf771 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -33,7 +33,7 @@ */ #include -#include +#include #include #include #include @@ -3895,7 +3895,7 @@ check_for_locks(struct nfs4_file *filp, struct nfs4_stateowner *lowner) struct inode *inode = filp->fi_inode; int status = 0; - lock_kernel(); + lock_flocks(); for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) { if ((*flpp)->fl_owner == (fl_owner_t)lowner) { status = 1; @@ -3903,7 +3903,7 @@ check_for_locks(struct nfs4_file *filp, struct nfs4_stateowner *lowner) } } out: - unlock_kernel(); + unlock_flocks(); return status; } -- cgit v1.2.3-18-g5258 From 2a4df5d33202e99c015928bf2a2dfd8ad03e53bc Mon Sep 17 00:00:00 2001 From: Petr Vandrovec Date: Wed, 29 Sep 2010 14:39:11 +0200 Subject: ncpfs: Lock socket in ncpfs while setting its callbacks Otherwise partially updated pointers could be seen if pointer update is not atomic. Signed-off-by: Petr Vandrovec Signed-off-by: Arnd Bergmann --- fs/ncpfs/inode.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 5f4e58d93fd..985fabb26ac 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -303,10 +303,12 @@ ncp_evict_inode(struct inode *inode) static void ncp_stop_tasks(struct ncp_server *server) { struct sock* sk = server->ncp_sock->sk; - + + lock_sock(sk); sk->sk_error_report = server->error_report; sk->sk_data_ready = server->data_ready; sk->sk_write_space = server->write_space; + release_sock(sk); del_timer_sync(&server->timeout_tm); flush_scheduled_work(); } @@ -605,10 +607,6 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) mutex_init(&server->rcv.creq_mutex); server->tx.creq = NULL; server->rcv.creq = NULL; - server->data_ready = sock->sk->sk_data_ready; - server->write_space = sock->sk->sk_write_space; - server->error_report = sock->sk->sk_error_report; - sock->sk->sk_user_data = server; init_timer(&server->timeout_tm); #undef NCP_PACKET_SIZE @@ -625,6 +623,11 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) if (server->rxbuf == NULL) goto out_txbuf; + lock_sock(sock->sk); + server->data_ready = sock->sk->sk_data_ready; + server->write_space = sock->sk->sk_write_space; + server->error_report = sock->sk->sk_error_report; + sock->sk->sk_user_data = server; sock->sk->sk_data_ready = ncp_tcp_data_ready; sock->sk->sk_error_report = ncp_tcp_error_report; if (sock->type == SOCK_STREAM) { @@ -640,6 +643,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) server->timeout_tm.data = (unsigned long)server; server->timeout_tm.function = ncpdgram_timeout_call; } + release_sock(sock->sk); ncp_lock_server(server); error = ncp_connect(server); -- cgit v1.2.3-18-g5258 From 26daad8067f976a6e3622b42b0d80de9a53e8ad9 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Fri, 24 Sep 2010 15:22:22 +0200 Subject: autofs: Only declare function when CONFIG_COMPAT is defined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The patch solves the following warnings message when CONFIG_COMPAT is not defined: fs/autofs/root.c:30: warning: ‘autofs_root_compat_ioctl’ declared ‘static’ but never defined Signed-off-by: Márton Németh Cc: "H. Peter Anvin" Cc: Arnd Bergmann Signed-off-by: Frederic Weisbecker Signed-off-by: Arnd Bergmann --- fs/autofs/root.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 11b1ea786d0..0c4ca81aeae 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -27,7 +27,9 @@ static int autofs_root_unlink(struct inode *,struct dentry *); static int autofs_root_rmdir(struct inode *,struct dentry *); static int autofs_root_mkdir(struct inode *,struct dentry *,int); static long autofs_root_ioctl(struct file *,unsigned int,unsigned long); +#ifdef CONFIG_COMPAT static long autofs_root_compat_ioctl(struct file *,unsigned int,unsigned long); +#endif const struct file_operations autofs_root_operations = { .llseek = generic_file_llseek, -- cgit v1.2.3-18-g5258 From 5a44a73b9015e89cfb280ba0534dfcbf1ef29d05 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Fri, 24 Sep 2010 15:22:23 +0200 Subject: autofs4: Only declare function when CONFIG_COMPAT is defined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The patch solves the following warnings message when CONFIG_COMPAT is not defined: fs/autofs4/root.c:31: warning: ‘autofs4_root_compat_ioctl’ declared ‘static’ but never defined Signed-off-by: Felipe Contreras Cc: Ian Kent Cc: "H. Peter Anvin" Cc: Arnd Bergmann Signed-off-by: Frederic Weisbecker Signed-off-by: Arnd Bergmann --- fs/autofs4/root.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 9dd29c29fd1..d5c1401f003 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -28,7 +28,9 @@ static int autofs4_dir_unlink(struct inode *,struct dentry *); static int autofs4_dir_rmdir(struct inode *,struct dentry *); static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); static long autofs4_root_ioctl(struct file *,unsigned int,unsigned long); +#ifdef CONFIG_COMPAT static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); +#endif static int autofs4_dir_open(struct inode *inode, struct file *file); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); static void *autofs4_follow_link(struct dentry *, struct nameidata *); -- cgit v1.2.3-18-g5258 From 134669854e3a680d8aad9a4047891c653715f4c0 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Wed, 6 Oct 2010 09:58:44 +0100 Subject: GFS2: Fix type mapping for demote_rq interface Mostly the glock operations follow the type of the glock. The one exception is the transaction glock, so we need to check for that directly. Reported-by: Dr. David Alan Gilbert Signed-off-by: Steven Whitehouse --- fs/gfs2/glops.c | 1 - fs/gfs2/sys.c | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 621d80e8fb2..0d149dcc04e 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -452,7 +452,6 @@ const struct gfs2_glock_operations *gfs2_glops_list[] = { [LM_TYPE_META] = &gfs2_meta_glops, [LM_TYPE_INODE] = &gfs2_inode_glops, [LM_TYPE_RGRP] = &gfs2_rgrp_glops, - [LM_TYPE_NONDISK] = &gfs2_trans_glops, [LM_TYPE_IOPEN] = &gfs2_iopen_glops, [LM_TYPE_FLOCK] = &gfs2_flock_glops, [LM_TYPE_NONDISK] = &gfs2_nondisk_glops, diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 64082a5feae..748ccb557c1 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -230,7 +230,10 @@ static ssize_t demote_rq_store(struct gfs2_sbd *sdp, const char *buf, size_t len if (gltype > LM_TYPE_JOURNAL) return -EINVAL; - glops = gfs2_glops_list[gltype]; + if (gltype == LM_TYPE_NONDISK && glnum == GFS2_TRANS_LOCK) + glops = &gfs2_trans_glops; + else + glops = gfs2_glops_list[gltype]; if (glops == NULL) return -EINVAL; if (!test_and_set_bit(SDF_DEMOTE, &sdp->sd_flags)) -- cgit v1.2.3-18-g5258 From 7ffec372458d163492e56e663a1b3a2d7be0a0a2 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 29 Sep 2010 19:51:11 -0400 Subject: cifs: add refcounted and timestamped container for holding tcons Eventually, we'll need to track the use of tcons on a per-sb basis, so that we know when it's ok to tear them down. Begin this conversion by adding a new "tcon_link" struct and accessors that get it. For now, the core data structures are untouched -- cifs_sb still just points to a single tcon and the pointers are just cast to deal with the accessor functions. A later patch will flesh this out. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_dfs_ref.c | 21 ++++--- fs/cifs/cifsacl.c | 42 ++++++++++--- fs/cifs/cifsglob.h | 51 ++++++++++++---- fs/cifs/dir.c | 65 ++++++++++++++------ fs/cifs/file.c | 9 ++- fs/cifs/inode.c | 157 ++++++++++++++++++++++++++++++++++++++----------- fs/cifs/link.c | 87 ++++++++++++++++++--------- fs/cifs/readdir.c | 32 +++++----- fs/cifs/xattr.c | 60 ++++++++++++------- 9 files changed, 376 insertions(+), 148 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index f1e13ea45a1..f4aab6f0117 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -306,6 +306,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) int xid, i; int rc = 0; struct vfsmount *mnt = ERR_PTR(-ENOENT); + struct tcon_link *tlink; cFYI(1, "in %s", __func__); BUG_ON(IS_ROOT(dentry)); @@ -315,14 +316,6 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) dput(nd->path.dentry); nd->path.dentry = dget(dentry); - cifs_sb = CIFS_SB(dentry->d_inode->i_sb); - ses = cifs_sb_tcon(cifs_sb)->ses; - - if (!ses) { - rc = -EINVAL; - goto out_err; - } - /* * The MSDFS spec states that paths in DFS referral requests and * responses must be prefixed by a single '\' character instead of @@ -335,10 +328,20 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) goto out_err; } - rc = get_dfs_path(xid, ses , full_path + 1, cifs_sb->local_nls, + cifs_sb = CIFS_SB(dentry->d_inode->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto out_err; + } + ses = tlink_tcon(tlink)->ses; + + rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls, &num_referrals, &referrals, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); + for (i = 0; i < num_referrals; i++) { int len; dump_referral(referrals+i); diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 32f244909a0..2647ea410c4 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -557,11 +557,16 @@ static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, { struct cifs_ntsd *pntsd = NULL; int xid, rc; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + + if (IS_ERR(tlink)) + return NULL; xid = GetXid(); - rc = CIFSSMBGetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, &pntsd, pacllen); + rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), fid, &pntsd, pacllen); FreeXid(xid); + cifs_put_tlink(tlink); cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen); return pntsd; @@ -574,10 +579,16 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, int oplock = 0; int xid, rc; __u16 fid; + struct cifsTconInfo *tcon; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + + if (IS_ERR(tlink)) + return NULL; + tcon = tlink_tcon(tlink); xid = GetXid(); - rc = CIFSSMBOpen(xid, cifs_sb_tcon(cifs_sb), path, FILE_OPEN, READ_CONTROL, 0, + rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL, 0, &fid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { @@ -585,11 +596,12 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, goto out; } - rc = CIFSSMBGetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, &pntsd, pacllen); + rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen); cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen); - CIFSSMBClose(xid, cifs_sb_tcon(cifs_sb), fid); + CIFSSMBClose(xid, tcon, fid); out: + cifs_put_tlink(tlink); FreeXid(xid); return pntsd; } @@ -616,10 +628,15 @@ static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid, struct cifs_ntsd *pnntsd, u32 acllen) { int xid, rc; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + + if (IS_ERR(tlink)) + return PTR_ERR(tlink); xid = GetXid(); - rc = CIFSSMBSetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, pnntsd, acllen); + rc = CIFSSMBSetCIFSACL(xid, tlink_tcon(tlink), fid, pnntsd, acllen); FreeXid(xid); + cifs_put_tlink(tlink); cFYI(DBG2, "SetCIFSACL rc = %d", rc); return rc; @@ -631,10 +648,16 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path, int oplock = 0; int xid, rc; __u16 fid; + struct cifsTconInfo *tcon; + struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + tcon = tlink_tcon(tlink); xid = GetXid(); - rc = CIFSSMBOpen(xid, cifs_sb_tcon(cifs_sb), path, FILE_OPEN, WRITE_DAC, 0, + rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, 0, &fid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { @@ -642,12 +665,13 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path, goto out; } - rc = CIFSSMBSetCIFSACL(xid, cifs_sb_tcon(cifs_sb), fid, pnntsd, acllen); + rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen); cFYI(DBG2, "SetCIFSACL rc = %d", rc); - CIFSSMBClose(xid, cifs_sb_tcon(cifs_sb), fid); - out: + CIFSSMBClose(xid, tcon, fid); +out: FreeXid(xid); + cifs_put_tlink(tlink); return rc; } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c265ebdcd17..cdfd2db4e70 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -310,6 +310,44 @@ struct cifsTconInfo { /* BB add field for back pointer to sb struct(s)? */ }; +/* + * This is a refcounted and timestamped container for a tcon pointer. The + * container holds a tcon reference. It is considered safe to free one of + * these when the tl_count goes to 0. The tl_time is the time of the last + * "get" on the container. + */ +struct tcon_link { + spinlock_t tl_lock; + u32 tl_count; + u64 tl_time; + struct cifsTconInfo *tl_tcon; +}; + +static inline struct tcon_link * +cifs_sb_tlink(struct cifs_sb_info *cifs_sb) +{ + return (struct tcon_link *)cifs_sb->ptcon; +} + +static inline struct cifsTconInfo * +tlink_tcon(struct tcon_link *tlink) +{ + return (struct cifsTconInfo *)tlink; +} + +static inline void +cifs_put_tlink(struct tcon_link *tlink) +{ + return; +} + +/* This function is always expected to succeed */ +static inline struct cifsTconInfo * +cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) +{ + return cifs_sb->ptcon; +} + /* * This info hangs off the cifsFileInfo structure, pointed to by llist. * This is used to track byte stream locks on the file @@ -413,19 +451,6 @@ CIFS_SB(struct super_block *sb) return sb->s_fs_info; } -static inline struct cifsTconInfo * -cifs_sb_tcon(struct cifs_sb_info *cifs_sb) -{ - return cifs_sb->ptcon; -} - -/* This function is always expected to succeed */ -static inline struct cifsTconInfo * -cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) -{ - return cifs_sb->ptcon; -} - static inline char CIFS_DIR_SEP(const struct cifs_sb_info *cifs_sb) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 23ec28a4c11..bb3ea06ca6f 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -137,7 +137,6 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, { struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; - struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb); pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); if (pCifsFile == NULL) @@ -191,7 +190,8 @@ int cifs_posix_open(char *full_path, struct inode **pinode, __u32 posix_flags = 0; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_fattr fattr; - struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); + struct tcon_link *tlink; + struct cifsTconInfo *tcon; cFYI(1, "posix open %s", full_path); @@ -226,10 +226,20 @@ int cifs_posix_open(char *full_path, struct inode **pinode, posix_flags |= SMB_O_DIRECT; mode &= ~current_umask(); + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto posix_open_ret; + } + + tcon = tlink_tcon(tlink); rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data, poplock, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); + if (rc) goto posix_open_ret; @@ -290,6 +300,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, int desiredAccess = GENERIC_READ | GENERIC_WRITE; __u16 fileHandle; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *tcon; char *full_path = NULL; FILE_ALL_INFO *buf = NULL; @@ -299,13 +310,12 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - tcon = cifs_sb_tcon(cifs_sb); - - full_path = build_path_from_dentry(direntry); - if (full_path == NULL) { - rc = -ENOMEM; - goto cifs_create_out; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + FreeXid(xid); + return PTR_ERR(tlink); } + tcon = tlink_tcon(tlink); if (oplockEnabled) oplock = REQ_OPLOCK; @@ -315,6 +325,12 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, else oflags = FMODE_READ | SMB_O_CREAT; + full_path = build_path_from_dentry(direntry); + if (full_path == NULL) { + rc = -ENOMEM; + goto cifs_create_out; + } + if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { @@ -481,6 +497,7 @@ cifs_create_set_dentry: cifs_create_out: kfree(buf); kfree(full_path); + cifs_put_tlink(tlink); FreeXid(xid); return rc; } @@ -491,6 +508,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, int rc = -EPERM; int xid; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; char *full_path = NULL; struct inode *newinode = NULL; @@ -503,10 +521,14 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, if (!old_valid_dev(device_number)) return -EINVAL; - xid = GetXid(); - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + + pTcon = tlink_tcon(tlink); + + xid = GetXid(); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -606,6 +628,7 @@ mknod_out: kfree(full_path); kfree(buf); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } @@ -619,6 +642,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, __u16 fileHandle = 0; bool posix_open = false; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; struct cifsFileInfo *cfile; struct inode *newInode = NULL; @@ -633,7 +657,12 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, /* check whether path exists */ cifs_sb = CIFS_SB(parent_dir_inode->i_sb); - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + FreeXid(xid); + return (struct dentry *)tlink; + } + pTcon = tlink_tcon(tlink); /* * Don't allow the separator character in a path component. @@ -644,8 +673,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, for (i = 0; i < direntry->d_name.len; i++) if (direntry->d_name.name[i] == '\\') { cFYI(1, "Invalid file name"); - FreeXid(xid); - return ERR_PTR(-EINVAL); + rc = -EINVAL; + goto lookup_out; } } @@ -655,7 +684,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, */ if (nd && (nd->flags & LOOKUP_EXCL)) { d_instantiate(direntry, NULL); - return NULL; + rc = 0; + goto lookup_out; } /* can not grab the rename sem here since it would @@ -663,8 +693,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, in which we already have the sb rename sem */ full_path = build_path_from_dentry(direntry); if (full_path == NULL) { - FreeXid(xid); - return ERR_PTR(-ENOMEM); + rc = -ENOMEM; + goto lookup_out; } if (direntry->d_inode != NULL) { @@ -760,6 +790,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, lookup_out: kfree(full_path); + cifs_put_tlink(tlink); FreeXid(xid); return ERR_PTR(rc); } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index de046e183d1..1e375abc5eb 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -224,6 +224,7 @@ int cifs_open(struct inode *inode, struct file *file) __u32 oplock; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *tcon; + struct tcon_link *tlink; struct cifsFileInfo *pCifsFile = NULL; struct cifsInodeInfo *pCifsInode; char *full_path = NULL; @@ -235,7 +236,12 @@ int cifs_open(struct inode *inode, struct file *file) xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - tcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + FreeXid(xid); + return PTR_ERR(tlink); + } + tcon = tlink_tcon(tlink); pCifsInode = CIFS_I(file->f_path.dentry->d_inode); @@ -402,6 +408,7 @@ out: kfree(buf); kfree(full_path); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index da716d96dae..aa229692aef 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -313,16 +313,21 @@ int cifs_get_inode_info_unix(struct inode **pinode, FILE_UNIX_BASIC_INFO find_data; struct cifs_fattr fattr; struct cifsTconInfo *tcon; + struct tcon_link *tlink; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - tcon = cifs_sb_tcon(cifs_sb); - cFYI(1, "Getting info on %s", full_path); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + /* could have done a find first instead but this returns more info */ rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); if (!rc) { cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); @@ -361,7 +366,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, int rc; int oplock = 0; __u16 netfid; - struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb); + struct tcon_link *tlink; + struct cifsTconInfo *tcon; char buf[24]; unsigned int bytes_read; char *pbuf; @@ -380,7 +386,12 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, return -EINVAL; /* EOPNOTSUPP? */ } - rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, GENERIC_READ, CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & @@ -388,7 +399,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, if (rc == 0) { int buf_type = CIFS_NO_BUFFER; /* Read header */ - rc = CIFSSMBRead(xid, pTcon, netfid, + rc = CIFSSMBRead(xid, tcon, netfid, 24 /* length */, 0 /* offset */, &bytes_read, &pbuf, &buf_type); if ((rc == 0) && (bytes_read >= 8)) { @@ -430,8 +441,9 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, fattr->cf_dtype = DT_REG; rc = -EOPNOTSUPP; /* or some unknown SFU type */ } - CIFSSMBClose(xid, pTcon, netfid); + CIFSSMBClose(xid, tcon, netfid); } + cifs_put_tlink(tlink); return rc; } @@ -449,11 +461,19 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, ssize_t rc; char ea_value[4]; __u32 mode; + struct tcon_link *tlink; + struct cifsTconInfo *tcon; - rc = CIFSSMBQAllEAs(xid, cifs_sb_tcon(cifs_sb), path, "SETFILEBITS", + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + rc = CIFSSMBQAllEAs(xid, tcon, path, "SETFILEBITS", ea_value, 4 /* size of buf */, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); if (rc < 0) return (int)rc; else if (rc > 3) { @@ -564,26 +584,33 @@ int cifs_get_inode_info(struct inode **pinode, { int rc = 0, tmprc; struct cifsTconInfo *pTcon; + struct tcon_link *tlink; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); char *buf = NULL; bool adjustTZ = false; struct cifs_fattr fattr; - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + cFYI(1, "Getting info on %s", full_path); if ((pfindData == NULL) && (*pinode != NULL)) { if (CIFS_I(*pinode)->clientCanCacheRead) { cFYI(1, "No need to revalidate cached inode sizes"); - return rc; + goto cgii_exit; } } /* if file info not passed in then get it from server */ if (pfindData == NULL) { buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; + if (buf == NULL) { + rc = -ENOMEM; + goto cgii_exit; + } pfindData = (FILE_ALL_INFO *)buf; /* could do find first instead but this returns more info */ @@ -688,6 +715,7 @@ int cifs_get_inode_info(struct inode **pinode, cgii_exit: kfree(buf); + cifs_put_tlink(tlink); return rc; } @@ -895,6 +923,7 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, struct cifsFileInfo *open_file; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = NULL; struct cifsTconInfo *pTcon; FILE_BASIC_INFO info_buf; @@ -942,7 +971,13 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, goto set_via_filehandle; } - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + tlink = NULL; + goto out; + } + pTcon = tlink_tcon(tlink); /* * NT4 apparently returns success on this call, but it doesn't @@ -987,6 +1022,8 @@ set_via_filehandle: else cifsFileInfo_put(open_file); out: + if (tlink != NULL) + cifs_put_tlink(tlink); return rc; } @@ -1004,10 +1041,16 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid) struct inode *inode = dentry->d_inode; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); + struct tcon_link *tlink; + struct cifsTconInfo *tcon; __u32 dosattr, origattr; FILE_BASIC_INFO *info_buf = NULL; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR, &netfid, &oplock, NULL, cifs_sb->local_nls, @@ -1076,6 +1119,7 @@ out_close: CIFSSMBClose(xid, tcon, netfid); out: kfree(info_buf); + cifs_put_tlink(tlink); return rc; /* @@ -1115,12 +1159,18 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) struct cifsInodeInfo *cifs_inode; struct super_block *sb = dir->i_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); + struct tcon_link *tlink; + struct cifsTconInfo *tcon; struct iattr *attrs = NULL; __u32 dosattr = 0, origattr = 0; cFYI(1, "cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + xid = GetXid(); /* Unlink can be called from rename so we can not take the @@ -1128,8 +1178,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) full_path = build_path_from_dentry(dentry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto unlink_out; } if ((tcon->ses->capabilities & CAP_UNIX) && @@ -1195,10 +1244,11 @@ out_reval: dir->i_ctime = dir->i_mtime = current_fs_time(sb); cifs_inode = CIFS_I(dir); CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ - +unlink_out: kfree(full_path); kfree(attrs); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } @@ -1207,6 +1257,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) int rc = 0, tmprc; int xid; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; char *full_path = NULL; struct inode *newinode = NULL; @@ -1214,16 +1265,18 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) cFYI(1, "In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode); - xid = GetXid(); - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = GetXid(); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto mkdir_out; } if ((pTcon->ses->capabilities & CAP_UNIX) && @@ -1381,6 +1434,7 @@ mkdir_get_info: mkdir_out: kfree(full_path); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } @@ -1389,6 +1443,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) int rc = 0; int xid; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; char *full_path = NULL; struct cifsInodeInfo *cifsInode; @@ -1397,18 +1452,23 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) xid = GetXid(); - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb_tcon(cifs_sb); - full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto rmdir_exit; + } + + cifs_sb = CIFS_SB(inode->i_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto rmdir_exit; } + pTcon = tlink_tcon(tlink); rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); if (!rc) { drop_nlink(inode); @@ -1429,6 +1489,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb); +rmdir_exit: kfree(full_path); FreeXid(xid); return rc; @@ -1439,10 +1500,16 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, struct dentry *to_dentry, const char *toPath) { struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); - struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb); + struct tcon_link *tlink; + struct cifsTconInfo *pTcon; __u16 srcfid; int oplock, rc; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + /* try path-based rename first */ rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & @@ -1454,11 +1521,11 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, * rename by filehandle to various Windows servers. */ if (rc == 0 || rc != -ETXTBSY) - return rc; + goto do_rename_exit; /* open-file renames don't work across directories */ if (to_dentry->d_parent != from_dentry->d_parent) - return rc; + goto do_rename_exit; /* open the file to be renamed -- we need DELETE perms */ rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE, @@ -1474,7 +1541,8 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, CIFSSMBClose(xid, pTcon, srcfid); } - +do_rename_exit: + cifs_put_tlink(tlink); return rc; } @@ -1484,13 +1552,17 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, char *fromName = NULL; char *toName = NULL; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *tcon; FILE_UNIX_BASIC_INFO *info_buf_source = NULL; FILE_UNIX_BASIC_INFO *info_buf_target; int xid, rc, tmprc; cifs_sb = CIFS_SB(source_dir->i_sb); - tcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); xid = GetXid(); @@ -1566,6 +1638,7 @@ cifs_rename_exit: kfree(fromName); kfree(toName); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } @@ -1728,6 +1801,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, struct cifsFileInfo *open_file; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink = NULL; struct cifsTconInfo *pTcon = NULL; /* @@ -1758,8 +1832,12 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, rc = -EINVAL; if (rc != 0) { - if (pTcon == NULL) - pTcon = cifs_sb_tcon(cifs_sb); + if (pTcon == NULL) { + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + } /* Set file size by pathname rather than by handle either because no valid, writeable file handle for @@ -1790,6 +1868,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, CIFSSMBClose(xid, pTcon, netfid); } } + if (tlink) + cifs_put_tlink(tlink); } if (rc == 0) { @@ -1810,6 +1890,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) struct inode *inode = direntry->d_inode; struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; struct cifsTconInfo *pTcon; struct cifs_unix_set_info_args *args = NULL; struct cifsFileInfo *open_file; @@ -1905,11 +1986,17 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); cifsFileInfo_put(open_file); } else { - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto out; + } + pTcon = tlink_tcon(tlink); rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); } if (rc) diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 66db2d61fa4..b38fe6704ad 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -249,7 +249,8 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr, int rc; int oplock = 0; __u16 netfid = 0; - struct cifsTconInfo *pTcon = cifs_sb_tcon(cifs_sb); + struct tcon_link *tlink; + struct cifsTconInfo *pTcon; u8 *buf; char *pbuf; unsigned int bytes_read = 0; @@ -261,23 +262,30 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr, /* it's not a symlink */ return 0; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, CREATE_NOT_DIR, &netfid, &oplock, &file_info, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc != 0) - return rc; + goto out; if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { CIFSSMBClose(xid, pTcon, netfid); /* it's not a symlink */ - return 0; + goto out; } buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (!buf) { + rc = -ENOMEM; + goto out; + } pbuf = buf; rc = CIFSSMBRead(xid, pTcon, netfid, @@ -287,23 +295,28 @@ CIFSCheckMFSymlink(struct cifs_fattr *fattr, CIFSSMBClose(xid, pTcon, netfid); if (rc != 0) { kfree(buf); - return rc; + goto out; } rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); kfree(buf); - if (rc == -EINVAL) + if (rc == -EINVAL) { /* it's not a symlink */ - return 0; + rc = 0; + goto out; + } + if (rc != 0) - return rc; + goto out; /* it is a symlink */ fattr->cf_eof = link_len; fattr->cf_mode &= ~S_IFMT; fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; fattr->cf_dtype = DT_LNK; - return 0; +out: + cifs_put_tlink(tlink); + return rc; } int @@ -314,17 +327,17 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, int xid; char *fromName = NULL; char *toName = NULL; - struct cifs_sb_info *cifs_sb_target; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; struct cifsTconInfo *pTcon; struct cifsInodeInfo *cifsInode; - xid = GetXid(); - - cifs_sb_target = CIFS_SB(inode->i_sb); - pTcon = cifs_sb_tcon(cifs_sb_target); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); -/* No need to check for cross device links since server will do that - BB note DFS case in future though (when we may have to check) */ + xid = GetXid(); fromName = build_path_from_dentry(old_file); toName = build_path_from_dentry(direntry); @@ -336,13 +349,13 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, /* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/ if (pTcon->unix_ext) rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, - cifs_sb_target->local_nls, - cifs_sb_target->mnt_cifs_flags & + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else { rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, - cifs_sb_target->local_nls, - cifs_sb_target->mnt_cifs_flags & + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if ((rc == -EIO) || (rc == -EINVAL)) rc = -EOPNOTSUPP; @@ -378,6 +391,7 @@ cifs_hl_exit: kfree(fromName); kfree(toName); FreeXid(xid); + cifs_put_tlink(tlink); return rc; } @@ -390,10 +404,19 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) char *full_path = NULL; char *target_path = NULL; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct cifsTconInfo *tcon = cifs_sb_tcon(cifs_sb); + struct tcon_link *tlink = NULL; + struct cifsTconInfo *tcon; xid = GetXid(); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + tlink = NULL; + goto out; + } + tcon = tlink_tcon(tlink); + /* * For now, we just handle symlinks with unix extensions enabled. * Eventually we should handle NTFS reparse points, and MacOS @@ -442,6 +465,8 @@ out: } FreeXid(xid); + if (tlink) + cifs_put_tlink(tlink); nd_set_link(nd, target_path); return NULL; } @@ -451,22 +476,25 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) { int rc = -EOPNOTSUPP; int xid; - struct cifs_sb_info *cifs_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct tcon_link *tlink; struct cifsTconInfo *pTcon; char *full_path = NULL; struct inode *newinode = NULL; xid = GetXid(); - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto symlink_exit; + } + pTcon = tlink_tcon(tlink); full_path = build_path_from_dentry(direntry); - if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto symlink_exit; } cFYI(1, "Full path: %s", full_path); @@ -504,8 +532,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) d_instantiate(direntry, newinode); } } - +symlink_exit: kfree(full_path); + cifs_put_tlink(tlink); FreeXid(xid); return rc; } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 887a7e23037..170047cf452 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -223,33 +223,35 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, static int initiate_cifs_search(const int xid, struct file *file) { int rc = 0; - char *full_path; + char *full_path = NULL; struct cifsFileInfo *cifsFile; - struct cifs_sb_info *cifs_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); + struct tcon_link *tlink; struct cifsTconInfo *pTcon; - cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - if (cifs_sb == NULL) - return -EINVAL; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); if (file->private_data == NULL) file->private_data = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); + if (file->private_data == NULL) { + rc = -ENOMEM; + goto error_exit; + } - if (file->private_data == NULL) - return -ENOMEM; cifsFile = file->private_data; cifsFile->invalidHandle = true; cifsFile->srch_inf.endOfSearch = false; - cifsFile->tcon = cifs_sb_tcon(cifs_sb); - pTcon = cifsFile->tcon; - if (pTcon == NULL) - return -EINVAL; + cifsFile->tcon = pTcon; full_path = build_path_from_dentry(file->f_path.dentry); - - if (full_path == NULL) - return -ENOMEM; + if (full_path == NULL) { + rc = -ENOMEM; + goto error_exit; + } cFYI(1, "Full path: %s start at: %lld", full_path, file->f_pos); @@ -282,7 +284,9 @@ ffirst_retry: cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; goto ffirst_retry; } +error_exit: kfree(full_path); + cifs_put_tlink(tlink); return rc; } diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 41f95bf6797..a264b744bb4 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -47,9 +47,10 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name) #ifdef CONFIG_CIFS_XATTR int xid; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; struct super_block *sb; - char *full_path; + char *full_path = NULL; if (direntry == NULL) return -EIO; @@ -58,16 +59,19 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name) sb = direntry->d_inode->i_sb; if (sb == NULL) return -EIO; - xid = GetXid(); cifs_sb = CIFS_SB(sb); - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = GetXid(); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto remove_ea_exit; } if (ea_name == NULL) { cFYI(1, "Null xattr names not supported"); @@ -91,6 +95,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name) remove_ea_exit: kfree(full_path); FreeXid(xid); + cifs_put_tlink(tlink); #endif return rc; } @@ -102,6 +107,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, #ifdef CONFIG_CIFS_XATTR int xid; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; struct super_block *sb; char *full_path; @@ -113,16 +119,19 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, sb = direntry->d_inode->i_sb; if (sb == NULL) return -EIO; - xid = GetXid(); cifs_sb = CIFS_SB(sb); - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = GetXid(); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto set_ea_exit; } /* return dos attributes as pseudo xattr */ /* return alt name if available as pseudo attr */ @@ -132,9 +141,8 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, returns as xattrs */ if (value_size > MAX_EA_VALUE_SIZE) { cFYI(1, "size of EA value too large"); - kfree(full_path); - FreeXid(xid); - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; + goto set_ea_exit; } if (ea_name == NULL) { @@ -198,6 +206,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, set_ea_exit: kfree(full_path); FreeXid(xid); + cifs_put_tlink(tlink); #endif return rc; } @@ -209,6 +218,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, #ifdef CONFIG_CIFS_XATTR int xid; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; struct super_block *sb; char *full_path; @@ -221,16 +231,18 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, if (sb == NULL) return -EIO; - xid = GetXid(); - cifs_sb = CIFS_SB(sb); - pTcon = cifs_sb_tcon(cifs_sb); + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + + xid = GetXid(); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto get_ea_exit; } /* return dos attributes as pseudo xattr */ /* return alt name if available as pseudo attr */ @@ -323,6 +335,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, get_ea_exit: kfree(full_path); FreeXid(xid); + cifs_put_tlink(tlink); #endif return rc; } @@ -333,6 +346,7 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) #ifdef CONFIG_CIFS_XATTR int xid; struct cifs_sb_info *cifs_sb; + struct tcon_link *tlink; struct cifsTconInfo *pTcon; struct super_block *sb; char *full_path; @@ -346,18 +360,20 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) return -EIO; cifs_sb = CIFS_SB(sb); - pTcon = cifs_sb_tcon(cifs_sb); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) return -EOPNOTSUPP; + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + pTcon = tlink_tcon(tlink); + xid = GetXid(); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; - FreeXid(xid); - return rc; + goto list_ea_exit; } /* return dos attributes as pseudo xattr */ /* return alt name if available as pseudo attr */ @@ -370,8 +386,10 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +list_ea_exit: kfree(full_path); FreeXid(xid); + cifs_put_tlink(tlink); #endif return rc; } -- cgit v1.2.3-18-g5258 From 13cfb7334eb6fd0fc06da5589aea1e947791f1d6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 29 Sep 2010 19:51:11 -0400 Subject: cifs: have cifsFileInfo hold a reference to a tlink rather than tcon pointer cifsFileInfo needs a pointer to a tcon, but it doesn't currently hold a reference to it. Change it to keep a pointer to a tcon_link instead and hold a reference to it. That will keep the tcon from being freed until the file is closed. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 3 ++- fs/cifs/cifsglob.h | 9 ++++++++- fs/cifs/cifsproto.h | 2 +- fs/cifs/dir.c | 10 +++++----- fs/cifs/file.c | 31 ++++++++++++++++--------------- fs/cifs/inode.c | 12 ++++++------ fs/cifs/ioctl.c | 2 +- fs/cifs/link.c | 1 - fs/cifs/readdir.c | 4 ++-- 9 files changed, 41 insertions(+), 33 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 898d2a5cfad..b2fd075dc2e 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -595,7 +595,8 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) ((arg == F_WRLCK) && (CIFS_I(inode)->clientCanCacheAll))) return generic_setlease(file, arg, lease); - else if (cfile->tcon->local_lease && !CIFS_I(inode)->clientCanCacheRead) + else if (tlink_tcon(cfile->tlink)->local_lease && + !CIFS_I(inode)->clientCanCacheRead) /* If the server claims to support oplock on this file, then we still need to check oplock even if the local_lease mount option is set, but there diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index cdfd2db4e70..d5324853203 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -341,6 +341,12 @@ cifs_put_tlink(struct tcon_link *tlink) return; } +static inline struct tcon_link * +cifs_get_tlink(struct tcon_link *tlink) +{ + return tlink; +} + /* This function is always expected to succeed */ static inline struct cifsTconInfo * cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) @@ -389,7 +395,7 @@ struct cifsFileInfo { struct file *pfile; /* needed for writepage */ struct inode *pInode; /* needed for oplock break */ struct vfsmount *mnt; - struct cifsTconInfo *tcon; + struct tcon_link *tlink; struct mutex lock_mutex; struct list_head llist; /* list of byte range locks we have. */ bool closePend:1; /* file is marked to close */ @@ -411,6 +417,7 @@ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file) static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file) { if (atomic_dec_and_test(&cifs_file->count)) { + cifs_put_tlink(cifs_file->tlink); iput(cifs_file->pInode); kfree(cifs_file); } diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 610b2263c9f..7294723d062 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -107,7 +107,7 @@ extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, - struct vfsmount *mnt, struct cifsTconInfo *tcon, + struct vfsmount *mnt, struct tcon_link *tlink, unsigned int oflags, __u32 oplock); extern int cifs_posix_open(char *full_path, struct inode **pinode, struct super_block *sb, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index bb3ea06ca6f..5adf47f28fe 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -132,7 +132,7 @@ cifs_bp_rename_retry: struct cifsFileInfo * cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, - struct vfsmount *mnt, struct cifsTconInfo *tcon, + struct vfsmount *mnt, struct tcon_link *tlink, unsigned int oflags, __u32 oplock) { struct cifsFileInfo *pCifsFile; @@ -149,7 +149,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, pCifsFile->pfile = file; pCifsFile->invalidHandle = false; pCifsFile->closePend = false; - pCifsFile->tcon = tcon; + pCifsFile->tlink = cifs_get_tlink(tlink); mutex_init(&pCifsFile->fh_mutex); mutex_init(&pCifsFile->lock_mutex); INIT_LIST_HEAD(&pCifsFile->llist); @@ -157,7 +157,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); write_lock(&GlobalSMBSeslock); - list_add(&pCifsFile->tlist, &tcon->openFileList); + list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList)); pCifsInode = CIFS_I(newinode); if (pCifsInode) { /* if readable file instance put first in list*/ @@ -483,7 +483,7 @@ cifs_create_set_dentry: } pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp, - nd->path.mnt, tcon, oflags, + nd->path.mnt, tlink, oflags, oplock); if (pfile_info == NULL) { fput(filp); @@ -758,7 +758,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, } cfile = cifs_new_fileinfo(newInode, fileHandle, filp, - nd->path.mnt, pTcon, + nd->path.mnt, tlink, nd->intent.open.flags, oplock); if (cfile == NULL) { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1e375abc5eb..24332d43715 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -283,7 +283,7 @@ int cifs_open(struct inode *inode, struct file *file) pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, - tcon, oflags, oplock); + tlink, oflags, oplock); if (pCifsFile == NULL) { CIFSSMBClose(xid, tcon, netfid); rc = -ENOMEM; @@ -376,7 +376,7 @@ int cifs_open(struct inode *inode, struct file *file) goto out; pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, - tcon, file->f_flags, oplock); + tlink, file->f_flags, oplock); if (pCifsFile == NULL) { rc = -ENOMEM; goto out; @@ -468,7 +468,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) } cifs_sb = CIFS_SB(inode->i_sb); - tcon = pCifsFile->tcon; + tcon = tlink_tcon(pCifsFile->tlink); /* can not grab rename sem here because various ops, including those that already have the rename sem can end up causing writepage @@ -582,7 +582,7 @@ int cifs_close(struct inode *inode, struct file *file) xid = GetXid(); cifs_sb = CIFS_SB(inode->i_sb); - pTcon = pSMBFile->tcon; + pTcon = tlink_tcon(pSMBFile->tlink); if (pSMBFile) { struct cifsLockInfo *li, *tmp; write_lock(&GlobalSMBSeslock); @@ -660,7 +660,7 @@ int cifs_closedir(struct inode *inode, struct file *file) xid = GetXid(); if (pCFileStruct) { - struct cifsTconInfo *pTcon = pCFileStruct->tcon; + struct cifsTconInfo *pTcon = tlink_tcon(pCFileStruct->tlink); cFYI(1, "Freeing private data in close dir"); write_lock(&GlobalSMBSeslock); @@ -684,6 +684,7 @@ int cifs_closedir(struct inode *inode, struct file *file) else cifs_buf_release(ptmp); } + cifs_put_tlink(pCFileStruct->tlink); kfree(file->private_data); file->private_data = NULL; } @@ -770,7 +771,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) cFYI(1, "Unknown type of lock"); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - tcon = ((struct cifsFileInfo *)file->private_data)->tcon; + tcon = tlink_tcon(((struct cifsFileInfo *)file->private_data)->tlink); if (file->private_data == NULL) { rc = -EBADF; @@ -970,7 +971,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, return -EBADF; open_file = file->private_data; - pTcon = open_file->tcon; + pTcon = tlink_tcon(open_file->tlink); rc = generic_write_checks(file, poffset, &write_size, 0); if (rc) @@ -1071,7 +1072,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, if (file->private_data == NULL) return -EBADF; open_file = file->private_data; - pTcon = open_file->tcon; + pTcon = tlink_tcon(open_file->tlink); xid = GetXid(); @@ -1393,7 +1394,7 @@ static int cifs_writepages(struct address_space *mapping, return generic_writepages(mapping, wbc); } - tcon = open_file->tcon; + tcon = tlink_tcon(open_file->tlink); if (!experimEnabled && tcon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { cifsFileInfo_put(open_file); @@ -1672,7 +1673,7 @@ int cifs_fsync(struct file *file, int datasync) if (rc == 0) { rc = CIFS_I(inode)->write_behind_rc; CIFS_I(inode)->write_behind_rc = 0; - tcon = smbfile->tcon; + tcon = tlink_tcon(smbfile->tlink); if (!rc && tcon && smbfile && !(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); @@ -1764,7 +1765,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, return rc; } open_file = file->private_data; - pTcon = open_file->tcon; + pTcon = tlink_tcon(open_file->tlink); if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, "attempting read on write only file instance"); @@ -1845,7 +1846,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, return rc; } open_file = file->private_data; - pTcon = open_file->tcon; + pTcon = tlink_tcon(open_file->tlink); if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, "attempting read on write only file instance"); @@ -1981,7 +1982,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, } open_file = file->private_data; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - pTcon = open_file->tcon; + pTcon = tlink_tcon(open_file->tlink); /* * Reads as many pages as possible from fscache. Returns -ENOBUFS @@ -2345,8 +2346,8 @@ void cifs_oplock_break(struct work_struct *work) * disconnected since oplock already released by the server */ if (!cfile->closePend && !cfile->oplock_break_cancelled) { - rc = CIFSSMBLock(0, cfile->tcon, cfile->netfid, 0, 0, 0, 0, - LOCKING_ANDX_OPLOCK_RELEASE, false); + rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid, 0, + 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false); cFYI(1, "Oplock release rc = %d", rc); } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index aa229692aef..a39a1c45173 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -289,7 +289,7 @@ int cifs_get_file_info_unix(struct file *filp) struct inode *inode = filp->f_path.dentry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsFileInfo *cfile = filp->private_data; - struct cifsTconInfo *tcon = cfile->tcon; + struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink); xid = GetXid(); rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data); @@ -546,7 +546,7 @@ int cifs_get_file_info(struct file *filp) struct inode *inode = filp->f_path.dentry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsFileInfo *cfile = filp->private_data; - struct cifsTconInfo *tcon = cfile->tcon; + struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink); xid = GetXid(); rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data); @@ -967,7 +967,7 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, if (open_file) { netfid = open_file->netfid; netpid = open_file->pid; - pTcon = open_file->tcon; + pTcon = tlink_tcon(open_file->tlink); goto set_via_filehandle; } @@ -1696,7 +1696,7 @@ int cifs_revalidate_file(struct file *filp) if (!cifs_inode_needs_reval(inode)) goto check_inval; - if (cfile->tcon->unix_ext) + if (tlink_tcon(cfile->tlink)->unix_ext) rc = cifs_get_file_info_unix(filp); else rc = cifs_get_file_info(filp); @@ -1817,7 +1817,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, if (open_file) { __u16 nfid = open_file->netfid; __u32 npid = open_file->pid; - pTcon = open_file->tcon; + pTcon = tlink_tcon(open_file->tlink); rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, npid, false); cifsFileInfo_put(open_file); @@ -1982,7 +1982,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) if (open_file) { u16 nfid = open_file->netfid; u32 npid = open_file->pid; - pTcon = open_file->tcon; + pTcon = tlink_tcon(open_file->tlink); rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); cifsFileInfo_put(open_file); } else { diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index cc70a61a47d..077bf756f34 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -38,7 +38,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) struct cifs_sb_info *cifs_sb; #ifdef CONFIG_CIFS_POSIX struct cifsFileInfo *pSMBFile = filep->private_data; - struct cifsTconInfo *tcon = pSMBFile->tcon; + struct cifsTconInfo *tcon = tlink_tcon(pSMBFile->tlink); __u64 ExtAttrBits = 0; __u64 ExtAttrMask = 0; __u64 caps = le64_to_cpu(tcon->fsUnixInfo.Capability); diff --git a/fs/cifs/link.c b/fs/cifs/link.c index b38fe6704ad..85cdbf831e7 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -346,7 +346,6 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, goto cifs_hl_exit; } -/* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/ if (pTcon->unix_ext) rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, cifs_sb->local_nls, diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 170047cf452..1f0bd0f972d 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -245,7 +245,7 @@ static int initiate_cifs_search(const int xid, struct file *file) cifsFile = file->private_data; cifsFile->invalidHandle = true; cifsFile->srch_inf.endOfSearch = false; - cifsFile->tcon = pTcon; + cifsFile->tlink = cifs_get_tlink(tlink); full_path = build_path_from_dentry(file->f_path.dentry); if (full_path == NULL) { @@ -838,7 +838,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) CIFSFindClose(xid, pTcon, cifsFile->netfid); } */ - pTcon = cifsFile->tcon; + pTcon = tlink_tcon(cifsFile->tlink); rc = find_cifs_entry(xid, pTcon, file, ¤t_entry, &num_to_fill); if (rc) { -- cgit v1.2.3-18-g5258 From 6508d904e6fb66ce4c34617f72b38d6714c4b9f6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 29 Sep 2010 19:51:11 -0400 Subject: cifs: have find_readable/writable_file filter by fsuid When we implement multiuser mounts, we'll need to filter filehandles by fsuid. Add a flag for multiuser mounts and code to filter by fsuid when it's set. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 3 ++- fs/cifs/cifsacl.c | 4 ++-- fs/cifs/cifsproto.h | 4 ++-- fs/cifs/dir.c | 1 + fs/cifs/file.c | 33 +++++++++++++++++++++++++-------- fs/cifs/inode.c | 6 +++--- 6 files changed, 35 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index ba0afd3acff..e04e6923d35 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -37,6 +37,7 @@ #define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/ #define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ #define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */ +#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ struct cifs_sb_info { struct cifsTconInfo *ptcon; /* primary mount */ @@ -48,7 +49,7 @@ struct cifs_sb_info { gid_t mnt_gid; mode_t mnt_file_mode; mode_t mnt_dir_mode; - int mnt_cifs_flags; + unsigned int mnt_cifs_flags; int prepathlen; char *prepath; /* relative path under the share to mount to */ #ifdef CONFIG_CIFS_DFS_UPCALL diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 2647ea410c4..c9b4792ae82 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -615,7 +615,7 @@ static struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb, struct cifsFileInfo *open_file = NULL; if (inode) - open_file = find_readable_file(CIFS_I(inode)); + open_file = find_readable_file(CIFS_I(inode), true); if (!open_file) return get_cifs_acl_by_path(cifs_sb, path, pacllen); @@ -685,7 +685,7 @@ static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, cFYI(DBG2, "set ACL for %s from mode 0x%x", path, inode->i_mode); - open_file = find_readable_file(CIFS_I(inode)); + open_file = find_readable_file(CIFS_I(inode), true); if (!open_file) return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen); diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7294723d062..29a2ee8ae51 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -78,9 +78,9 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); extern bool is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); -extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); +extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool); #ifdef CONFIG_CIFS_EXPERIMENTAL -extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *); +extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); #endif extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 5adf47f28fe..e249b561ce8 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -144,6 +144,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, pCifsFile->netfid = fileHandle; pCifsFile->pid = current->tgid; + pCifsFile->uid = current_fsuid(); pCifsFile->pInode = igrab(newinode); pCifsFile->mnt = mnt; pCifsFile->pfile = file; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 24332d43715..80856f18071 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1168,9 +1168,15 @@ static ssize_t cifs_write(struct file *file, const char *write_data, } #ifdef CONFIG_CIFS_EXPERIMENTAL -struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) +struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, + bool fsuid_only) { struct cifsFileInfo *open_file = NULL; + struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); + + /* only filter by fsuid on multiuser mounts */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) + fsuid_only = false; read_lock(&GlobalSMBSeslock); /* we could simply get the first_list_entry since write-only entries @@ -1179,6 +1185,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (open_file->closePend) continue; + if (fsuid_only && open_file->uid != current_fsuid()) + continue; if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_RDONLY))) { if (!open_file->invalidHandle) { @@ -1198,9 +1206,11 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) } #endif -struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) +struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, + bool fsuid_only) { struct cifsFileInfo *open_file; + struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); bool any_available = false; int rc; @@ -1214,13 +1224,19 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) return NULL; } + /* only filter by fsuid on multiuser mounts */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) + fsuid_only = false; + read_lock(&GlobalSMBSeslock); refind_writable: list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (open_file->closePend || - (!any_available && open_file->pid != current->tgid)) + if (open_file->closePend) + continue; + if (!any_available && open_file->pid != current->tgid) + continue; + if (fsuid_only && open_file->uid != current_fsuid()) continue; - if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || (open_file->pfile->f_flags & O_WRONLY))) { @@ -1315,7 +1331,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) if (mapping->host->i_size - offset < (loff_t)to) to = (unsigned)(mapping->host->i_size - offset); - open_file = find_writable_file(CIFS_I(mapping->host)); + open_file = find_writable_file(CIFS_I(mapping->host), false); if (open_file) { bytes_written = cifs_write(open_file->pfile, write_data, to-from, &offset); @@ -1388,7 +1404,7 @@ static int cifs_writepages(struct address_space *mapping, * but it'll at least handle the return. Maybe it should be * a BUG() instead? */ - open_file = find_writable_file(CIFS_I(mapping->host)); + open_file = find_writable_file(CIFS_I(mapping->host), false); if (!open_file) { kfree(iov); return generic_writepages(mapping, wbc); @@ -1505,7 +1521,8 @@ retry: break; } if (n_iov) { - open_file = find_writable_file(CIFS_I(mapping->host)); + open_file = find_writable_file(CIFS_I(mapping->host), + false); if (!open_file) { cERROR(1, "No writable handles for inode"); rc = -EBADF; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index a39a1c45173..df29a3a3d80 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -963,7 +963,7 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, /* * If the file is already open for write, just use that fileid */ - open_file = find_writable_file(cifsInode); + open_file = find_writable_file(cifsInode, true); if (open_file) { netfid = open_file->netfid; netpid = open_file->pid; @@ -1813,7 +1813,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, * writebehind data than the SMB timeout for the SetPathInfo * request would allow */ - open_file = find_writable_file(cifsInode); + open_file = find_writable_file(cifsInode, true); if (open_file) { __u16 nfid = open_file->netfid; __u32 npid = open_file->pid; @@ -1978,7 +1978,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) args->ctime = NO_CHANGE_64; args->device = 0; - open_file = find_writable_file(cifsInode); + open_file = find_writable_file(cifsInode, true); if (open_file) { u16 nfid = open_file->netfid; u32 npid = open_file->pid; -- cgit v1.2.3-18-g5258 From 29e07c82a9e8acebbb38ecc22b0b5005a0a5d839 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 29 Sep 2010 19:51:12 -0400 Subject: cifs: fix cifs_show_options to show "username=" or "multiuser" ...based on CIFS_MOUNT_MULTIUSER flag. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index b2fd075dc2e..c96345c3314 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -369,8 +369,12 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; seq_printf(s, ",unc=%s", tcon->treeName); - if (tcon->ses->userName) + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) + seq_printf(s, ",multiuser"); + else if (tcon->ses->userName) seq_printf(s, ",username=%s", tcon->ses->userName); + if (tcon->ses->domainName) seq_printf(s, ",domain=%s", tcon->ses->domainName); -- cgit v1.2.3-18-g5258 From c9928f7040a6e5f39e028bea500e0fde910d4a96 Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Mon, 4 Oct 2010 19:56:13 -0500 Subject: ntlm authentication and signing - Correct response length for ntlmv2 authentication without extended security Fix incorrect calculation of case sensitive response length in the ntlmv2 (without extended security) response. Signed-off-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/sess.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index af18a500f7e..c926e6c7c0c 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -739,9 +739,6 @@ ssetup_ntlmssp_authenticate: pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; /* cpu_to_le16(LM2_SESS_KEY_SIZE); */ - pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(sizeof(struct ntlmv2_resp)); - /* calculate session key */ rc = setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); if (rc) { @@ -753,6 +750,11 @@ ssetup_ntlmssp_authenticate: sizeof(struct ntlmv2_resp)); bcc_ptr += sizeof(struct ntlmv2_resp); kfree(v2_sess_key); + /* set case sensitive password length after tilen may get + * assigned, tilen is 0 otherwise. + */ + pSMB->req_no_secext.CaseSensitivePasswordLength = + cpu_to_le16(sizeof(struct ntlmv2_resp) + ses->tilen); if (ses->tilen > 0) { memcpy(bcc_ptr, ses->tiblob, ses->tilen); bcc_ptr += ses->tilen; @@ -761,6 +763,7 @@ ssetup_ntlmssp_authenticate: ses->tiblob = NULL; ses->tilen = 0; } + if (ses->capabilities & CAP_UNICODE) { if (iov[0].iov_len % 2) { *bcc_ptr = 0; -- cgit v1.2.3-18-g5258 From 54b5187b5a1ad6573ade8b18e065dda92501fc52 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Thu, 7 Oct 2010 15:26:08 -0700 Subject: ocfs2/cluster: Add heartbeat mode configfs parameter Add heartbeat mode parameter to the configfs tree. This will be used to set/show the heartbeat mode. The user is free to toggle the mode between local and global as long as there is no active heartbeat region. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 41d5f1f92d5..4d36459a834 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -77,7 +77,19 @@ static struct o2hb_callback *hbcall_from_type(enum o2hb_callback_type type); #define O2HB_DEFAULT_BLOCK_BITS 9 +enum o2hb_heartbeat_modes { + O2HB_HEARTBEAT_LOCAL = 0, + O2HB_HEARTBEAT_GLOBAL, + O2HB_HEARTBEAT_NUM_MODES, +}; + +char *o2hb_heartbeat_mode_desc[O2HB_HEARTBEAT_NUM_MODES] = { + "local", /* O2HB_HEARTBEAT_LOCAL */ + "global", /* O2HB_HEARTBEAT_GLOBAL */ +}; + unsigned int o2hb_dead_threshold = O2HB_DEFAULT_DEAD_THRESHOLD; +unsigned int o2hb_heartbeat_mode = O2HB_HEARTBEAT_LOCAL; /* Only sets a new threshold if there are no active regions. * @@ -94,6 +106,22 @@ static void o2hb_dead_threshold_set(unsigned int threshold) } } +static int o2hb_global_hearbeat_mode_set(unsigned int hb_mode) +{ + int ret = -1; + + if (hb_mode < O2HB_HEARTBEAT_NUM_MODES) { + spin_lock(&o2hb_live_lock); + if (list_empty(&o2hb_all_regions)) { + o2hb_heartbeat_mode = hb_mode; + ret = 0; + } + spin_unlock(&o2hb_live_lock); + } + + return ret; +} + struct o2hb_node_event { struct list_head hn_item; enum o2hb_callback_type hn_event_type; @@ -1688,6 +1716,41 @@ static ssize_t o2hb_heartbeat_group_threshold_store(struct o2hb_heartbeat_group return count; } +static +ssize_t o2hb_heartbeat_group_mode_show(struct o2hb_heartbeat_group *group, + char *page) +{ + return sprintf(page, "%s\n", + o2hb_heartbeat_mode_desc[o2hb_heartbeat_mode]); +} + +static +ssize_t o2hb_heartbeat_group_mode_store(struct o2hb_heartbeat_group *group, + const char *page, size_t count) +{ + unsigned int i; + int ret; + size_t len; + + len = (page[count - 1] == '\n') ? count - 1 : count; + if (!len) + return -EINVAL; + + for (i = 0; i < O2HB_HEARTBEAT_NUM_MODES; ++i) { + if (strnicmp(page, o2hb_heartbeat_mode_desc[i], len)) + continue; + + ret = o2hb_global_hearbeat_mode_set(i); + if (!ret) + printk(KERN_NOTICE "ocfs2: Heartbeat mode set to %s\n", + o2hb_heartbeat_mode_desc[i]); + return count; + } + + return -EINVAL; + +} + static struct o2hb_heartbeat_group_attribute o2hb_heartbeat_group_attr_threshold = { .attr = { .ca_owner = THIS_MODULE, .ca_name = "dead_threshold", @@ -1696,8 +1759,17 @@ static struct o2hb_heartbeat_group_attribute o2hb_heartbeat_group_attr_threshold .store = o2hb_heartbeat_group_threshold_store, }; +static struct o2hb_heartbeat_group_attribute o2hb_heartbeat_group_attr_mode = { + .attr = { .ca_owner = THIS_MODULE, + .ca_name = "mode", + .ca_mode = S_IRUGO | S_IWUSR }, + .show = o2hb_heartbeat_group_mode_show, + .store = o2hb_heartbeat_group_mode_store, +}; + static struct configfs_attribute *o2hb_heartbeat_group_attrs[] = { &o2hb_heartbeat_group_attr_threshold.attr, + &o2hb_heartbeat_group_attr_mode.attr, NULL, }; -- cgit v1.2.3-18-g5258 From 98f486f23bc5b6a6fa90e1a0707b7e9fe0e7f3e4 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Sat, 9 Oct 2010 10:24:46 -0700 Subject: ocfs2: Add an incompat feature flag OCFS2_FEATURE_INCOMPAT_CLUSTERINFO OCFS2_FEATURE_INCOMPAT_CLUSTERINFO allows us to use sb->s_cluster_info for both userspace and o2cb cluster stacks. It also allows us to extend cluster info to include stack flags. This patch also adds stackflags to sb->s_clusterinfo. It also introduces a clusterinfo flag OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT to denote the enabled global heartbeat mode. This incompat flag can be set/cleared using tunefs.ocfs2 --fs-features. The clusterinfo flag is set/cleared using tunefs.ocfs2 --update-cluster-stack. Signed-off-by: Sunil Mushran --- fs/ocfs2/ocfs2.h | 31 +++++++++++++++++++++++++++++-- fs/ocfs2/ocfs2_fs.h | 40 ++++++++++++++++++++++++++++++++++------ fs/ocfs2/super.c | 4 +++- 3 files changed, 66 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index c67003b6b5a..d5496a792bd 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -368,6 +368,8 @@ struct ocfs2_super struct ocfs2_alloc_stats alloc_stats; char dev_str[20]; /* "major,minor" of the device */ + u8 osb_stackflags; + char osb_cluster_stack[OCFS2_STACK_LABEL_LEN + 1]; struct ocfs2_cluster_connection *cconn; struct ocfs2_lock_res osb_super_lockres; @@ -601,10 +603,35 @@ static inline int ocfs2_is_soft_readonly(struct ocfs2_super *osb) return ret; } -static inline int ocfs2_userspace_stack(struct ocfs2_super *osb) +static inline int ocfs2_clusterinfo_valid(struct ocfs2_super *osb) { return (osb->s_feature_incompat & - OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK); + (OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK | + OCFS2_FEATURE_INCOMPAT_CLUSTERINFO)); +} + +static inline int ocfs2_userspace_stack(struct ocfs2_super *osb) +{ + if (ocfs2_clusterinfo_valid(osb) && + memcmp(osb->osb_cluster_stack, OCFS2_CLASSIC_CLUSTER_STACK, + OCFS2_STACK_LABEL_LEN)) + return 1; + return 0; +} + +static inline int ocfs2_o2cb_stack(struct ocfs2_super *osb) +{ + if (ocfs2_clusterinfo_valid(osb) && + !memcmp(osb->osb_cluster_stack, OCFS2_CLASSIC_CLUSTER_STACK, + OCFS2_STACK_LABEL_LEN)) + return 1; + return 0; +} + +static inline int ocfs2_cluster_o2cb_global_heartbeat(struct ocfs2_super *osb) +{ + return ocfs2_o2cb_stack(osb) && + (osb->osb_stackflags & OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT); } static inline int ocfs2_mount_local(struct ocfs2_super *osb) diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index fa31d05e41b..d5b1d99abc3 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -101,7 +101,8 @@ | OCFS2_FEATURE_INCOMPAT_META_ECC \ | OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS \ | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE \ - | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG) + | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG \ + | OCFS2_FEATURE_INCOMPAT_CLUSTERINFO) #define OCFS2_FEATURE_RO_COMPAT_SUPP (OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \ | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \ | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA) @@ -169,6 +170,13 @@ /* Discontigous block groups */ #define OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG 0x2000 +/* + * Incompat bit to indicate useable clusterinfo with stackflags for all + * cluster stacks (userspace adnd o2cb). If this bit is set, + * INCOMPAT_USERSPACE_STACK becomes superfluous and thus should not be set. + */ +#define OCFS2_FEATURE_INCOMPAT_CLUSTERINFO 0x4000 + /* * backup superblock flag is used to indicate that this volume * has backup superblocks. @@ -292,10 +300,13 @@ #define OCFS2_VOL_UUID_LEN 16 #define OCFS2_MAX_VOL_LABEL_LEN 64 -/* The alternate, userspace stack fields */ +/* The cluster stack fields */ #define OCFS2_STACK_LABEL_LEN 4 #define OCFS2_CLUSTER_NAME_LEN 16 +/* Classic (historically speaking) cluster stack */ +#define OCFS2_CLASSIC_CLUSTER_STACK "o2cb" + /* Journal limits (in bytes) */ #define OCFS2_MIN_JOURNAL_SIZE (4 * 1024 * 1024) @@ -305,6 +316,11 @@ */ #define OCFS2_MIN_XATTR_INLINE_SIZE 256 +/* + * Cluster info flags (ocfs2_cluster_info.ci_stackflags) + */ +#define OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT (0x01) + struct ocfs2_system_inode_info { char *si_name; int si_iflags; @@ -566,9 +582,21 @@ struct ocfs2_slot_map_extended { */ }; +/* + * ci_stackflags is only valid if the incompat bit + * OCFS2_FEATURE_INCOMPAT_CLUSTERINFO is set. + */ struct ocfs2_cluster_info { /*00*/ __u8 ci_stack[OCFS2_STACK_LABEL_LEN]; - __le32 ci_reserved; + union { + __le32 ci_reserved; + struct { + __u8 ci_stackflags; + __u8 ci_reserved1; + __u8 ci_reserved2; + __u8 ci_reserved3; + }; + }; /*08*/ __u8 ci_cluster[OCFS2_CLUSTER_NAME_LEN]; /*18*/ }; @@ -605,9 +633,9 @@ struct ocfs2_super_block { * group header */ /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ /*90*/ __u8 s_uuid[OCFS2_VOL_UUID_LEN]; /* 128-bit uuid */ -/*A0*/ struct ocfs2_cluster_info s_cluster_info; /* Selected userspace - stack. Only valid - with INCOMPAT flag. */ +/*A0*/ struct ocfs2_cluster_info s_cluster_info; /* Only valid if either + userspace or clusterinfo + INCOMPAT flag set. */ /*B8*/ __le16 s_xattr_inline_size; /* extended attribute inline size for this fs*/ __le16 s_reserved0; diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index fa1be1b304d..75543173939 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -2149,7 +2149,9 @@ static int ocfs2_initialize_super(struct super_block *sb, goto bail; } - if (ocfs2_userspace_stack(osb)) { + if (ocfs2_clusterinfo_valid(osb)) { + osb->osb_stackflags = + OCFS2_RAW_SB(di)->s_cluster_info.ci_stackflags; memcpy(osb->osb_cluster_stack, OCFS2_RAW_SB(di)->s_cluster_info.ci_stack, OCFS2_STACK_LABEL_LEN); -- cgit v1.2.3-18-g5258 From 2c442719e90a44a6982c033d69df4aae4b167cfa Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Thu, 7 Oct 2010 15:23:50 -0700 Subject: ocfs2: Add support for heartbeat=global mount option Adds support for heartbeat=global mount option. It ensures that the heartbeat mode passed matches the one enabled on disk. Signed-off-by: Sunil Mushran --- fs/ocfs2/ocfs2.h | 4 +++- fs/ocfs2/ocfs2_fs.h | 1 + fs/ocfs2/super.c | 55 +++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index d5496a792bd..481387b90b2 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -243,7 +243,7 @@ enum ocfs2_local_alloc_state enum ocfs2_mount_options { - OCFS2_MOUNT_HB_LOCAL = 1 << 0, /* Heartbeat started in local mode */ + OCFS2_MOUNT_HB_LOCAL = 1 << 0, /* Local heartbeat */ OCFS2_MOUNT_BARRIER = 1 << 1, /* Use block barriers */ OCFS2_MOUNT_NOINTR = 1 << 2, /* Don't catch signals */ OCFS2_MOUNT_ERRORS_PANIC = 1 << 3, /* Panic on errors */ @@ -256,6 +256,8 @@ enum ocfs2_mount_options control lists */ OCFS2_MOUNT_USRQUOTA = 1 << 10, /* We support user quotas */ OCFS2_MOUNT_GRPQUOTA = 1 << 11, /* We support group quotas */ + OCFS2_MOUNT_HB_NONE = 1 << 12, /* No heartbeat */ + OCFS2_MOUNT_HB_GLOBAL = 1 << 13, /* Global heartbeat */ }; #define OCFS2_OSB_SOFT_RO 0x0001 diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index d5b1d99abc3..28ff536b4f8 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -376,6 +376,7 @@ static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = { /* Parameter passed from mount.ocfs2 to module */ #define OCFS2_HB_NONE "heartbeat=none" #define OCFS2_HB_LOCAL "heartbeat=local" +#define OCFS2_HB_GLOBAL "heartbeat=global" /* * OCFS2 directory file types. Only the low 3 bits are used. The diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 75543173939..4e009ad303a 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -162,6 +162,7 @@ enum { Opt_nointr, Opt_hb_none, Opt_hb_local, + Opt_hb_global, Opt_data_ordered, Opt_data_writeback, Opt_atime_quantum, @@ -190,6 +191,7 @@ static const match_table_t tokens = { {Opt_nointr, "nointr"}, {Opt_hb_none, OCFS2_HB_NONE}, {Opt_hb_local, OCFS2_HB_LOCAL}, + {Opt_hb_global, OCFS2_HB_GLOBAL}, {Opt_data_ordered, "data=ordered"}, {Opt_data_writeback, "data=writeback"}, {Opt_atime_quantum, "atime_quantum=%u"}, @@ -608,6 +610,7 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) int ret = 0; struct mount_options parsed_options; struct ocfs2_super *osb = OCFS2_SB(sb); + u32 tmp; lock_kernel(); @@ -617,8 +620,9 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) goto out; } - if ((osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) != - (parsed_options.mount_opt & OCFS2_MOUNT_HB_LOCAL)) { + tmp = OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL | + OCFS2_MOUNT_HB_NONE; + if ((osb->s_mount_opt & tmp) != (parsed_options.mount_opt & tmp)) { ret = -EINVAL; mlog(ML_ERROR, "Cannot change heartbeat mode on remount\n"); goto out; @@ -809,23 +813,29 @@ bail: static int ocfs2_verify_heartbeat(struct ocfs2_super *osb) { - if (ocfs2_mount_local(osb)) { - if (osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) { + u32 hb_enabled = OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL; + + if (osb->s_mount_opt & hb_enabled) { + if (ocfs2_mount_local(osb)) { mlog(ML_ERROR, "Cannot heartbeat on a locally " "mounted device.\n"); return -EINVAL; } - } - - if (ocfs2_userspace_stack(osb)) { - if (osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) { + if (ocfs2_userspace_stack(osb)) { mlog(ML_ERROR, "Userspace stack expected, but " "o2cb heartbeat arguments passed to mount\n"); return -EINVAL; } + if (((osb->s_mount_opt & OCFS2_MOUNT_HB_GLOBAL) && + !ocfs2_cluster_o2cb_global_heartbeat(osb)) || + ((osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) && + ocfs2_cluster_o2cb_global_heartbeat(osb))) { + mlog(ML_ERROR, "Mismatching o2cb heartbeat modes\n"); + return -EINVAL; + } } - if (!(osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL)) { + if (!(osb->s_mount_opt & hb_enabled)) { if (!ocfs2_mount_local(osb) && !ocfs2_is_hard_readonly(osb) && !ocfs2_userspace_stack(osb)) { mlog(ML_ERROR, "Heartbeat has to be started to mount " @@ -1291,6 +1301,7 @@ static int ocfs2_parse_options(struct super_block *sb, { int status; char *p; + u32 tmp; mlog_entry("remount: %d, options: \"%s\"\n", is_remount, options ? options : "(none)"); @@ -1322,7 +1333,10 @@ static int ocfs2_parse_options(struct super_block *sb, mopt->mount_opt |= OCFS2_MOUNT_HB_LOCAL; break; case Opt_hb_none: - mopt->mount_opt &= ~OCFS2_MOUNT_HB_LOCAL; + mopt->mount_opt |= OCFS2_MOUNT_HB_NONE; + break; + case Opt_hb_global: + mopt->mount_opt |= OCFS2_MOUNT_HB_GLOBAL; break; case Opt_barrier: if (match_int(&args[0], &option)) { @@ -1477,6 +1491,15 @@ static int ocfs2_parse_options(struct super_block *sb, } } + /* Ensure only one heartbeat mode */ + tmp = mopt->mount_opt & (OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL | + OCFS2_MOUNT_HB_NONE); + if (hweight32(tmp) != 1) { + mlog(ML_ERROR, "Invalid heartbeat mount options\n"); + status = 0; + goto bail; + } + status = 1; bail: @@ -1490,10 +1513,14 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) unsigned long opts = osb->s_mount_opt; unsigned int local_alloc_megs; - if (opts & OCFS2_MOUNT_HB_LOCAL) - seq_printf(s, ",_netdev,heartbeat=local"); - else - seq_printf(s, ",heartbeat=none"); + if (opts & (OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL)) { + seq_printf(s, ",_netdev"); + if (opts & OCFS2_MOUNT_HB_LOCAL) + seq_printf(s, ",%s", OCFS2_HB_LOCAL); + else + seq_printf(s, ",%s", OCFS2_HB_GLOBAL); + } else + seq_printf(s, ",%s", OCFS2_HB_NONE); if (opts & OCFS2_MOUNT_NOINTR) seq_printf(s, ",nointr"); -- cgit v1.2.3-18-g5258 From b1365d0bd14b912cceb424cbeed9fe939a9038e3 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 17:55:34 -0700 Subject: ocfs2/dlm: Expose dlm_protocol in dlm_state Add dlm_protocol to the list of info shown by the debugfs file, dlm_state. Signed-off-by: Sunil Mushran --- fs/ocfs2/dlm/dlmdebug.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index 901ca52bf86..f693ab812f3 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -782,7 +782,9 @@ static int debug_state_print(struct dlm_ctxt *dlm, struct debug_buffer *db) /* Domain: xxxxxxxxxx Key: 0xdfbac769 */ out += snprintf(db->buf + out, db->len - out, - "Domain: %s Key: 0x%08x\n", dlm->name, dlm->key); + "Domain: %s Key: 0x%08x Protocol: %d.%d\n", + dlm->name, dlm->key, dlm->dlm_locking_proto.pv_major, + dlm->dlm_locking_proto.pv_minor); /* Thread Pid: xxx Node: xxx State: xxxxx */ out += snprintf(db->buf + out, db->len - out, -- cgit v1.2.3-18-g5258 From b3c85c4cdf77154acc940dd0f14d1fb99cbbaf75 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Thu, 7 Oct 2010 14:31:06 -0700 Subject: ocfs2/cluster: Get all heartbeat regions Export function in o2hb to get a list of heartbeat regions. It also adds an upper limit to the length of the heartbeat region name. o2hb_global_heartbeat_active() currently disables global heartbeat. It will be enabled in a later patch after all the code is added. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 34 ++++++++++++++++++++++++++++++++++ fs/ocfs2/cluster/heartbeat.h | 4 ++++ 2 files changed, 38 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 4d36459a834..3415e58ff77 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1623,6 +1623,9 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g if (reg == NULL) return ERR_PTR(-ENOMEM); + if (strlen(name) > O2HB_MAX_REGION_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + config_item_init_type_name(®->hr_item, name, &o2hb_region_type); spin_lock(&o2hb_live_lock); @@ -2035,3 +2038,34 @@ void o2hb_stop_all_regions(void) spin_unlock(&o2hb_live_lock); } EXPORT_SYMBOL_GPL(o2hb_stop_all_regions); + +int o2hb_get_all_regions(char *region_uuids, u8 max_regions) +{ + struct o2hb_region *reg; + int numregs = 0; + char *p; + + spin_lock(&o2hb_live_lock); + + p = region_uuids; + list_for_each_entry(reg, &o2hb_all_regions, hr_all_item) { + mlog(0, "Region: %s\n", config_item_name(®->hr_item)); + if (numregs < max_regions) { + memcpy(p, config_item_name(®->hr_item), + O2HB_MAX_REGION_NAME_LEN); + p += O2HB_MAX_REGION_NAME_LEN; + } + numregs++; + } + + spin_unlock(&o2hb_live_lock); + + return numregs; +} +EXPORT_SYMBOL_GPL(o2hb_get_all_regions); + +int o2hb_global_heartbeat_active(void) +{ + return 0; +} +EXPORT_SYMBOL(o2hb_global_heartbeat_active); diff --git a/fs/ocfs2/cluster/heartbeat.h b/fs/ocfs2/cluster/heartbeat.h index 2f1649253b4..00ad8e8fea5 100644 --- a/fs/ocfs2/cluster/heartbeat.h +++ b/fs/ocfs2/cluster/heartbeat.h @@ -31,6 +31,8 @@ #define O2HB_REGION_TIMEOUT_MS 2000 +#define O2HB_MAX_REGION_NAME_LEN 32 + /* number of changes to be seen as live */ #define O2HB_LIVE_THRESHOLD 2 /* number of equal samples to be seen as dead */ @@ -81,5 +83,7 @@ int o2hb_check_node_heartbeating(u8 node_num); int o2hb_check_node_heartbeating_from_callback(u8 node_num); int o2hb_check_local_node_heartbeating(void); void o2hb_stop_all_regions(void); +int o2hb_get_all_regions(char *region_uuids, u8 numregions); +int o2hb_global_heartbeat_active(void); #endif /* O2CLUSTER_HEARTBEAT_H */ -- cgit v1.2.3-18-g5258 From ea2034416b54700e30371f2ad6517cbb94674083 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Sat, 9 Oct 2010 10:26:23 -0700 Subject: ocfs2/dlm: Add message DLM_QUERY_REGION Adds new dlm message DLM_QUERY_REGION that sends the names of all active heartbeat regions. This message is only sent in the global heartbeat mode. If the regions in the joining node do not fully match the ones in the active nodes, the join domain request is rejected. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/ocfs2_nodemanager.h | 6 + fs/ocfs2/dlm/dlmcommon.h | 12 +- fs/ocfs2/dlm/dlmdomain.c | 218 +++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/ocfs2_nodemanager.h b/fs/ocfs2/cluster/ocfs2_nodemanager.h index 5b9854bad57..49b594325be 100644 --- a/fs/ocfs2/cluster/ocfs2_nodemanager.h +++ b/fs/ocfs2/cluster/ocfs2_nodemanager.h @@ -36,4 +36,10 @@ /* host name, group name, cluster name all 64 bytes */ #define O2NM_MAX_NAME_LEN 64 // __NEW_UTS_LEN +/* + * Maximum number of global heartbeat regions allowed. + * **CAUTION** Changing this number will break dlm compatibility. + */ +#define O2NM_MAX_REGIONS 32 + #endif /* _OCFS2_NODEMANAGER_H */ diff --git a/fs/ocfs2/dlm/dlmcommon.h b/fs/ocfs2/dlm/dlmcommon.h index 765298908f1..aa506d3e2ae 100644 --- a/fs/ocfs2/dlm/dlmcommon.h +++ b/fs/ocfs2/dlm/dlmcommon.h @@ -445,7 +445,8 @@ enum { DLM_LOCK_REQUEST_MSG, /* 515 */ DLM_RECO_DATA_DONE_MSG, /* 516 */ DLM_BEGIN_RECO_MSG, /* 517 */ - DLM_FINALIZE_RECO_MSG /* 518 */ + DLM_FINALIZE_RECO_MSG, /* 518 */ + DLM_QUERY_REGION, /* 519 */ }; struct dlm_reco_node_data @@ -727,6 +728,15 @@ struct dlm_cancel_join u8 domain[O2NM_MAX_NAME_LEN]; }; +struct dlm_query_region { + u8 qr_node; + u8 qr_numregions; + u8 qr_namelen; + u8 pad1; + u8 qr_domain[O2NM_MAX_NAME_LEN]; + u8 qr_regions[O2HB_MAX_REGION_NAME_LEN * O2NM_MAX_REGIONS]; +}; + struct dlm_exit_domain { u8 node_idx; diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 11a5c87fd7f..49650756dfe 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -128,6 +128,9 @@ static DECLARE_WAIT_QUEUE_HEAD(dlm_domain_events); * will have a negotiated version with the same major number and a minor * number equal or smaller. The dlm_ctxt->dlm_locking_proto field should * be used to determine what a running domain is actually using. + * + * New in version 1.1: + * - Message DLM_QUERY_REGION added to support global heartbeat */ static const struct dlm_protocol_version dlm_protocol = { .pv_major = 1, @@ -142,6 +145,8 @@ static int dlm_assert_joined_handler(struct o2net_msg *msg, u32 len, void *data, void **ret_data); static int dlm_cancel_join_handler(struct o2net_msg *msg, u32 len, void *data, void **ret_data); +static int dlm_query_region_handler(struct o2net_msg *msg, u32 len, + void *data, void **ret_data); static int dlm_exit_domain_handler(struct o2net_msg *msg, u32 len, void *data, void **ret_data); static int dlm_protocol_compare(struct dlm_protocol_version *existing, @@ -921,6 +926,203 @@ static int dlm_assert_joined_handler(struct o2net_msg *msg, u32 len, void *data, return 0; } +static int dlm_match_regions(struct dlm_ctxt *dlm, + struct dlm_query_region *qr) +{ + char *local = NULL, *remote = qr->qr_regions; + char *l, *r; + int localnr, i, j, foundit; + int status = 0; + + if (!o2hb_global_heartbeat_active()) { + if (qr->qr_numregions) { + mlog(ML_ERROR, "Domain %s: Joining node %d has global " + "heartbeat enabled but local node %d does not\n", + qr->qr_domain, qr->qr_node, dlm->node_num); + status = -EINVAL; + } + goto bail; + } + + if (o2hb_global_heartbeat_active() && !qr->qr_numregions) { + mlog(ML_ERROR, "Domain %s: Local node %d has global " + "heartbeat enabled but joining node %d does not\n", + qr->qr_domain, dlm->node_num, qr->qr_node); + status = -EINVAL; + goto bail; + } + + r = remote; + for (i = 0; i < qr->qr_numregions; ++i) { + mlog(0, "Region %.*s\n", O2HB_MAX_REGION_NAME_LEN, r); + r += O2HB_MAX_REGION_NAME_LEN; + } + + local = kmalloc(sizeof(qr->qr_regions), GFP_KERNEL); + if (!local) { + status = -ENOMEM; + goto bail; + } + + localnr = o2hb_get_all_regions(local, O2NM_MAX_REGIONS); + + /* compare local regions with remote */ + l = local; + for (i = 0; i < localnr; ++i) { + foundit = 0; + r = remote; + for (j = 0; j <= qr->qr_numregions; ++j) { + if (!memcmp(l, r, O2HB_MAX_REGION_NAME_LEN)) { + foundit = 1; + break; + } + r += O2HB_MAX_REGION_NAME_LEN; + } + if (!foundit) { + status = -EINVAL; + mlog(ML_ERROR, "Domain %s: Region '%.*s' registered " + "in local node %d but not in joining node %d\n", + qr->qr_domain, O2HB_MAX_REGION_NAME_LEN, l, + dlm->node_num, qr->qr_node); + goto bail; + } + l += O2HB_MAX_REGION_NAME_LEN; + } + + /* compare remote with local regions */ + r = remote; + for (i = 0; i < qr->qr_numregions; ++i) { + foundit = 0; + l = local; + for (j = 0; j < localnr; ++j) { + if (!memcmp(r, l, O2HB_MAX_REGION_NAME_LEN)) { + foundit = 1; + break; + } + l += O2HB_MAX_REGION_NAME_LEN; + } + if (!foundit) { + status = -EINVAL; + mlog(ML_ERROR, "Domain %s: Region '%.*s' registered " + "in joining node %d but not in local node %d\n", + qr->qr_domain, O2HB_MAX_REGION_NAME_LEN, r, + qr->qr_node, dlm->node_num); + goto bail; + } + r += O2HB_MAX_REGION_NAME_LEN; + } + +bail: + kfree(local); + + return status; +} + +static int dlm_send_regions(struct dlm_ctxt *dlm, unsigned long *node_map) +{ + struct dlm_query_region *qr = NULL; + int status, ret = 0, i; + char *p; + + if (find_next_bit(node_map, O2NM_MAX_NODES, 0) >= O2NM_MAX_NODES) + goto bail; + + qr = kzalloc(sizeof(struct dlm_query_region), GFP_KERNEL); + if (!qr) { + ret = -ENOMEM; + mlog_errno(ret); + goto bail; + } + + qr->qr_node = dlm->node_num; + qr->qr_namelen = strlen(dlm->name); + memcpy(qr->qr_domain, dlm->name, qr->qr_namelen); + /* if local hb, the numregions will be zero */ + if (o2hb_global_heartbeat_active()) + qr->qr_numregions = o2hb_get_all_regions(qr->qr_regions, + O2NM_MAX_REGIONS); + + p = qr->qr_regions; + for (i = 0; i < qr->qr_numregions; ++i, p += O2HB_MAX_REGION_NAME_LEN) + mlog(0, "Region %.*s\n", O2HB_MAX_REGION_NAME_LEN, p); + + i = -1; + while ((i = find_next_bit(node_map, O2NM_MAX_NODES, + i + 1)) < O2NM_MAX_NODES) { + if (i == dlm->node_num) + continue; + + mlog(0, "Sending regions to node %d\n", i); + + ret = o2net_send_message(DLM_QUERY_REGION, DLM_MOD_KEY, qr, + sizeof(struct dlm_query_region), + i, &status); + if (ret >= 0) + ret = status; + if (ret) { + mlog(ML_ERROR, "Region mismatch %d, node %d\n", + ret, i); + break; + } + } + +bail: + kfree(qr); + return ret; +} + +static int dlm_query_region_handler(struct o2net_msg *msg, u32 len, + void *data, void **ret_data) +{ + struct dlm_query_region *qr; + struct dlm_ctxt *dlm = NULL; + int status = 0; + int locked = 0; + + qr = (struct dlm_query_region *) msg->buf; + + mlog(0, "Node %u queries hb regions on domain %s\n", qr->qr_node, + qr->qr_domain); + + status = -EINVAL; + + spin_lock(&dlm_domain_lock); + dlm = __dlm_lookup_domain_full(qr->qr_domain, qr->qr_namelen); + if (!dlm) { + mlog(ML_ERROR, "Node %d queried hb regions on domain %s " + "before join domain\n", qr->qr_node, qr->qr_domain); + goto bail; + } + + spin_lock(&dlm->spinlock); + locked = 1; + if (dlm->joining_node != qr->qr_node) { + mlog(ML_ERROR, "Node %d queried hb regions on domain %s " + "but joining node is %d\n", qr->qr_node, qr->qr_domain, + dlm->joining_node); + goto bail; + } + + /* Support for global heartbeat was added in 1.1 */ + if (dlm->dlm_locking_proto.pv_major == 1 && + dlm->dlm_locking_proto.pv_minor == 0) { + mlog(ML_ERROR, "Node %d queried hb regions on domain %s " + "but active dlm protocol is %d.%d\n", qr->qr_node, + qr->qr_domain, dlm->dlm_locking_proto.pv_major, + dlm->dlm_locking_proto.pv_minor); + goto bail; + } + + status = dlm_match_regions(dlm, qr); + +bail: + if (locked) + spin_unlock(&dlm->spinlock); + spin_unlock(&dlm_domain_lock); + + return status; +} + static int dlm_cancel_join_handler(struct o2net_msg *msg, u32 len, void *data, void **ret_data) { @@ -1241,6 +1443,15 @@ static int dlm_try_to_join_domain(struct dlm_ctxt *dlm) set_bit(dlm->node_num, dlm->domain_map); spin_unlock(&dlm->spinlock); + /* Support for global heartbeat was added in 1.1 */ + if (dlm_protocol.pv_major > 1 || dlm_protocol.pv_minor > 0) { + status = dlm_send_regions(dlm, ctxt->yes_resp_map); + if (status) { + mlog_errno(status); + goto bail; + } + } + dlm_send_join_asserts(dlm, ctxt->yes_resp_map); /* Joined state *must* be set before the joining node @@ -1807,6 +2018,13 @@ static int dlm_register_net_handlers(void) sizeof(struct dlm_cancel_join), dlm_cancel_join_handler, NULL, NULL, &dlm_join_handlers); + if (status) + goto bail; + + status = o2net_register_handler(DLM_QUERY_REGION, DLM_MOD_KEY, + sizeof(struct dlm_query_region), + dlm_query_region_handler, + NULL, NULL, &dlm_join_handlers); bail: if (status < 0) -- cgit v1.2.3-18-g5258 From 5f3c6d9c615770708df464c170316f83cf437242 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 17:55:29 -0700 Subject: ocfs2: Print message if user mounts without starting global heartbeat In global heartbeat mode, the heartbeat is started by the user. This patch prints an error if the user attempts to mount a volume without starting the heartbeat. Signed-off-by: Sunil Mushran --- fs/ocfs2/stack_o2cb.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/stack_o2cb.c b/fs/ocfs2/stack_o2cb.c index 0d3049f696c..19965b00c43 100644 --- a/fs/ocfs2/stack_o2cb.c +++ b/fs/ocfs2/stack_o2cb.c @@ -283,6 +283,8 @@ static int o2cb_cluster_connect(struct ocfs2_cluster_connection *conn) /* for now we only have one cluster/node, make sure we see it * in the heartbeat universe */ if (!o2hb_check_local_node_heartbeating()) { + if (o2hb_global_heartbeat_active()) + mlog(ML_ERROR, "Global heartbeat not started\n"); rc = -EINVAL; goto out; } -- cgit v1.2.3-18-g5258 From 18cfdf1b1a8e83b09e4185c02396257ce7e7bee3 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Thu, 7 Oct 2010 16:47:03 -0700 Subject: ocfs2/dlm: Add message DLM_QUERY_NODEINFO Adds new dlm message DLM_QUERY_NODEINFO that sends the attributes of all registered nodes. This message is sent if the negotiated dlm protocol is 1.1 or higher. If the information of the joining node does not match that of any existing nodes, the join domain request is rejected. Signed-off-by: Sunil Mushran --- fs/ocfs2/dlm/dlmcommon.h | 17 +++++ fs/ocfs2/dlm/dlmdomain.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 198 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmcommon.h b/fs/ocfs2/dlm/dlmcommon.h index aa506d3e2ae..b36d0bf77a5 100644 --- a/fs/ocfs2/dlm/dlmcommon.h +++ b/fs/ocfs2/dlm/dlmcommon.h @@ -447,6 +447,7 @@ enum { DLM_BEGIN_RECO_MSG, /* 517 */ DLM_FINALIZE_RECO_MSG, /* 518 */ DLM_QUERY_REGION, /* 519 */ + DLM_QUERY_NODEINFO, /* 520 */ }; struct dlm_reco_node_data @@ -737,6 +738,22 @@ struct dlm_query_region { u8 qr_regions[O2HB_MAX_REGION_NAME_LEN * O2NM_MAX_REGIONS]; }; +struct dlm_node_info { + u8 ni_nodenum; + u8 pad1; + u16 ni_ipv4_port; + u32 ni_ipv4_address; +}; + +struct dlm_query_nodeinfo { + u8 qn_nodenum; + u8 qn_numnodes; + u8 qn_namelen; + u8 pad1; + u8 qn_domain[O2NM_MAX_NAME_LEN]; + struct dlm_node_info qn_nodes[O2NM_MAX_NODES]; +}; + struct dlm_exit_domain { u8 node_idx; diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 49650756dfe..78d428f5e10 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -131,6 +131,7 @@ static DECLARE_WAIT_QUEUE_HEAD(dlm_domain_events); * * New in version 1.1: * - Message DLM_QUERY_REGION added to support global heartbeat + * - Message DLM_QUERY_NODEINFO added to allow online node removes */ static const struct dlm_protocol_version dlm_protocol = { .pv_major = 1, @@ -1123,6 +1124,173 @@ bail: return status; } +static int dlm_match_nodes(struct dlm_ctxt *dlm, struct dlm_query_nodeinfo *qn) +{ + struct o2nm_node *local; + struct dlm_node_info *remote; + int i, j; + int status = 0; + + for (j = 0; j < qn->qn_numnodes; ++j) + mlog(0, "Node %3d, %pI4:%u\n", qn->qn_nodes[j].ni_nodenum, + &(qn->qn_nodes[j].ni_ipv4_address), + ntohs(qn->qn_nodes[j].ni_ipv4_port)); + + for (i = 0; i < O2NM_MAX_NODES && !status; ++i) { + local = o2nm_get_node_by_num(i); + remote = NULL; + for (j = 0; j < qn->qn_numnodes; ++j) { + if (qn->qn_nodes[j].ni_nodenum == i) { + remote = &(qn->qn_nodes[j]); + break; + } + } + + if (!local && !remote) + continue; + + if ((local && !remote) || (!local && remote)) + status = -EINVAL; + + if (!status && + ((remote->ni_nodenum != local->nd_num) || + (remote->ni_ipv4_port != local->nd_ipv4_port) || + (remote->ni_ipv4_address != local->nd_ipv4_address))) + status = -EINVAL; + + if (status) { + if (remote && !local) + mlog(ML_ERROR, "Domain %s: Node %d (%pI4:%u) " + "registered in joining node %d but not in " + "local node %d\n", qn->qn_domain, + remote->ni_nodenum, + &(remote->ni_ipv4_address), + ntohs(remote->ni_ipv4_port), + qn->qn_nodenum, dlm->node_num); + if (local && !remote) + mlog(ML_ERROR, "Domain %s: Node %d (%pI4:%u) " + "registered in local node %d but not in " + "joining node %d\n", qn->qn_domain, + local->nd_num, &(local->nd_ipv4_address), + ntohs(local->nd_ipv4_port), + dlm->node_num, qn->qn_nodenum); + BUG_ON((!local && !remote)); + } + + if (local) + o2nm_node_put(local); + } + + return status; +} + +static int dlm_send_nodeinfo(struct dlm_ctxt *dlm, unsigned long *node_map) +{ + struct dlm_query_nodeinfo *qn = NULL; + struct o2nm_node *node; + int ret = 0, status, count, i; + + if (find_next_bit(node_map, O2NM_MAX_NODES, 0) >= O2NM_MAX_NODES) + goto bail; + + qn = kzalloc(sizeof(struct dlm_query_nodeinfo), GFP_KERNEL); + if (!qn) { + ret = -ENOMEM; + mlog_errno(ret); + goto bail; + } + + for (i = 0, count = 0; i < O2NM_MAX_NODES; ++i) { + node = o2nm_get_node_by_num(i); + if (!node) + continue; + qn->qn_nodes[count].ni_nodenum = node->nd_num; + qn->qn_nodes[count].ni_ipv4_port = node->nd_ipv4_port; + qn->qn_nodes[count].ni_ipv4_address = node->nd_ipv4_address; + mlog(0, "Node %3d, %pI4:%u\n", node->nd_num, + &(node->nd_ipv4_address), ntohs(node->nd_ipv4_port)); + ++count; + o2nm_node_put(node); + } + + qn->qn_nodenum = dlm->node_num; + qn->qn_numnodes = count; + qn->qn_namelen = strlen(dlm->name); + memcpy(qn->qn_domain, dlm->name, qn->qn_namelen); + + i = -1; + while ((i = find_next_bit(node_map, O2NM_MAX_NODES, + i + 1)) < O2NM_MAX_NODES) { + if (i == dlm->node_num) + continue; + + mlog(0, "Sending nodeinfo to node %d\n", i); + + ret = o2net_send_message(DLM_QUERY_NODEINFO, DLM_MOD_KEY, + qn, sizeof(struct dlm_query_nodeinfo), + i, &status); + if (ret >= 0) + ret = status; + if (ret) { + mlog(ML_ERROR, "node mismatch %d, node %d\n", ret, i); + break; + } + } + +bail: + kfree(qn); + return ret; +} + +static int dlm_query_nodeinfo_handler(struct o2net_msg *msg, u32 len, + void *data, void **ret_data) +{ + struct dlm_query_nodeinfo *qn; + struct dlm_ctxt *dlm = NULL; + int locked = 0, status = -EINVAL; + + qn = (struct dlm_query_nodeinfo *) msg->buf; + + mlog(0, "Node %u queries nodes on domain %s\n", qn->qn_nodenum, + qn->qn_domain); + + spin_lock(&dlm_domain_lock); + dlm = __dlm_lookup_domain_full(qn->qn_domain, qn->qn_namelen); + if (!dlm) { + mlog(ML_ERROR, "Node %d queried nodes on domain %s before " + "join domain\n", qn->qn_nodenum, qn->qn_domain); + goto bail; + } + + spin_lock(&dlm->spinlock); + locked = 1; + if (dlm->joining_node != qn->qn_nodenum) { + mlog(ML_ERROR, "Node %d queried nodes on domain %s but " + "joining node is %d\n", qn->qn_nodenum, qn->qn_domain, + dlm->joining_node); + goto bail; + } + + /* Support for node query was added in 1.1 */ + if (dlm->dlm_locking_proto.pv_major == 1 && + dlm->dlm_locking_proto.pv_minor == 0) { + mlog(ML_ERROR, "Node %d queried nodes on domain %s " + "but active dlm protocol is %d.%d\n", qn->qn_nodenum, + qn->qn_domain, dlm->dlm_locking_proto.pv_major, + dlm->dlm_locking_proto.pv_minor); + goto bail; + } + + status = dlm_match_nodes(dlm, qn); + +bail: + if (locked) + spin_unlock(&dlm->spinlock); + spin_unlock(&dlm_domain_lock); + + return status; +} + static int dlm_cancel_join_handler(struct o2net_msg *msg, u32 len, void *data, void **ret_data) { @@ -1443,8 +1611,13 @@ static int dlm_try_to_join_domain(struct dlm_ctxt *dlm) set_bit(dlm->node_num, dlm->domain_map); spin_unlock(&dlm->spinlock); - /* Support for global heartbeat was added in 1.1 */ + /* Support for global heartbeat and node info was added in 1.1 */ if (dlm_protocol.pv_major > 1 || dlm_protocol.pv_minor > 0) { + status = dlm_send_nodeinfo(dlm, ctxt->yes_resp_map); + if (status) { + mlog_errno(status); + goto bail; + } status = dlm_send_regions(dlm, ctxt->yes_resp_map); if (status) { mlog_errno(status); @@ -2026,6 +2199,13 @@ static int dlm_register_net_handlers(void) dlm_query_region_handler, NULL, NULL, &dlm_join_handlers); + if (status) + goto bail; + + status = o2net_register_handler(DLM_QUERY_NODEINFO, DLM_MOD_KEY, + sizeof(struct dlm_query_nodeinfo), + dlm_query_nodeinfo_handler, + NULL, NULL, &dlm_join_handlers); bail: if (status < 0) dlm_unregister_net_handlers(); -- cgit v1.2.3-18-g5258 From 18c50cb0d3c293eabd6c2ef89c43f2a968e709ed Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 18:26:59 -0700 Subject: ocfs2/cluster: Print messages when adding/removing heartbeat regions Prints messages when the user adds or removes heartbeat regions in global heartbeat mode. These messages are useful when debugging cluster related issues. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 3415e58ff77..12bb12ba864 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1476,6 +1476,10 @@ static ssize_t o2hb_region_dev_write(struct o2hb_region *reg, else ret = -EIO; + if (hb_task && o2hb_global_heartbeat_active()) + printk(KERN_NOTICE "o2hb: Heartbeat started on region %s\n", + config_item_name(®->hr_item)); + out: if (filp) fput(filp); @@ -1659,6 +1663,9 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group, wake_up(&o2hb_steady_queue); } + if (o2hb_global_heartbeat_active()) + printk(KERN_NOTICE "o2hb: Heartbeat stopped on region %s\n", + config_item_name(®->hr_item)); config_item_put(item); } @@ -1745,7 +1752,7 @@ ssize_t o2hb_heartbeat_group_mode_store(struct o2hb_heartbeat_group *group, ret = o2hb_global_hearbeat_mode_set(i); if (!ret) - printk(KERN_NOTICE "ocfs2: Heartbeat mode set to %s\n", + printk(KERN_NOTICE "o2hb: Heartbeat mode set to %s\n", o2hb_heartbeat_mode_desc[i]); return count; } -- cgit v1.2.3-18-g5258 From 39a298563e0619b1b6e2e0974e58801de780621c Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Thu, 7 Oct 2010 17:30:17 -0700 Subject: ocfs2/cluster: Print messages when adding/removing nodes Prints messages when the user adds or removes nodes. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/masklog.h | 3 ++- fs/ocfs2/cluster/nodemanager.c | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h index fd96e2a2fa5..ea2ed9f56c9 100644 --- a/fs/ocfs2/cluster/masklog.h +++ b/fs/ocfs2/cluster/masklog.h @@ -119,7 +119,8 @@ #define ML_ERROR 0x0000000100000000ULL /* sent to KERN_ERR */ #define ML_NOTICE 0x0000000200000000ULL /* setn to KERN_NOTICE */ #define ML_KTHREAD 0x0000000400000000ULL /* kernel thread activity */ -#define ML_RESERVATIONS 0x0000000800000000ULL /* ocfs2 alloc reservations */ +#define ML_RESERVATIONS 0x0000000800000000ULL /* ocfs2 alloc reservations */ +#define ML_CLUSTER 0x0000001000000000ULL /* cluster stack */ #define MLOG_INITIAL_AND_MASK (ML_ERROR|ML_NOTICE) #define MLOG_INITIAL_NOT_MASK (ML_ENTRY|ML_EXIT) diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index ed0c9f367fe..bb240647ca5 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -711,6 +711,8 @@ static struct config_item *o2nm_node_group_make_item(struct config_group *group, config_item_init_type_name(&node->nd_item, name, &o2nm_node_type); spin_lock_init(&node->nd_lock); + mlog(ML_CLUSTER, "o2nm: Registering node %s\n", name); + return &node->nd_item; } @@ -744,6 +746,9 @@ static void o2nm_node_group_drop_item(struct config_group *group, } write_unlock(&cluster->cl_nodes_lock); + mlog(ML_CLUSTER, "o2nm: Unregistered node %s\n", + config_item_name(&node->nd_item)); + config_item_put(item); } -- cgit v1.2.3-18-g5258 From 0e105d37c2adb19cb777aa6701a866f211764a30 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Thu, 7 Oct 2010 17:00:16 -0700 Subject: ocfs2/cluster: Check slots for unconfigured live nodes o2hb currently checks slots for configured nodes only. This patch makes it check the slots for the live nodes too to take care of a race in which a node is removed from the configuration but not from the live map. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 38 +++++++++++++++++++++++++++++++------- fs/ocfs2/cluster/tcp.c | 5 +++++ 2 files changed, 36 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 12bb12ba864..a8f10649674 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -541,6 +541,8 @@ static void o2hb_queue_node_event(struct o2hb_node_event *event, { assert_spin_locked(&o2hb_live_lock); + BUG_ON((!node) && (type != O2HB_NODE_DOWN_CB)); + event->hn_event_type = type; event->hn_node = node; event->hn_node_num = node_num; @@ -593,14 +595,22 @@ static int o2hb_check_slot(struct o2hb_region *reg, u64 cputime; unsigned int dead_ms = o2hb_dead_threshold * O2HB_REGION_TIMEOUT_MS; unsigned int slot_dead_ms; + int tmp; memcpy(hb_block, slot->ds_raw_block, reg->hr_block_bytes); - /* Is this correct? Do we assume that the node doesn't exist - * if we're not configured for him? */ + /* + * If a node is no longer configured but is still in the livemap, we + * may need to clear that bit from the livemap. + */ node = o2nm_get_node_by_num(slot->ds_node_num); - if (!node) - return 0; + if (!node) { + spin_lock(&o2hb_live_lock); + tmp = test_bit(slot->ds_node_num, o2hb_live_node_bitmap); + spin_unlock(&o2hb_live_lock); + if (!tmp) + return 0; + } if (!o2hb_verify_crc(reg, hb_block)) { /* all paths from here will drop o2hb_live_lock for @@ -717,8 +727,9 @@ fire_callbacks: if (list_empty(&o2hb_live_slots[slot->ds_node_num])) { clear_bit(slot->ds_node_num, o2hb_live_node_bitmap); - o2hb_queue_node_event(&event, O2HB_NODE_DOWN_CB, node, - slot->ds_node_num); + /* node can be null */ + o2hb_queue_node_event(&event, O2HB_NODE_DOWN_CB, + node, slot->ds_node_num); changed = 1; } @@ -738,7 +749,8 @@ out: o2hb_run_event_list(&event); - o2nm_node_put(node); + if (node) + o2nm_node_put(node); return changed; } @@ -765,6 +777,7 @@ static int o2hb_do_disk_heartbeat(struct o2hb_region *reg) { int i, ret, highest_node, change = 0; unsigned long configured_nodes[BITS_TO_LONGS(O2NM_MAX_NODES)]; + unsigned long live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; struct o2hb_bio_wait_ctxt write_wc; ret = o2nm_configured_node_map(configured_nodes, @@ -774,6 +787,17 @@ static int o2hb_do_disk_heartbeat(struct o2hb_region *reg) return ret; } + /* + * If a node is not configured but is in the livemap, we still need + * to read the slot so as to be able to remove it from the livemap. + */ + o2hb_fill_node_map(live_node_bitmap, sizeof(live_node_bitmap)); + i = -1; + while ((i = find_next_bit(live_node_bitmap, + O2NM_MAX_NODES, i + 1)) < O2NM_MAX_NODES) { + set_bit(i, configured_nodes); + } + highest_node = o2hb_highest_node(configured_nodes, O2NM_MAX_NODES); if (highest_node >= O2NM_MAX_NODES) { mlog(ML_NOTICE, "ocfs2_heartbeat: no configured nodes found!\n"); diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index cbe2f057cc2..9aa426e4212 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -1696,6 +1696,9 @@ static void o2net_hb_node_down_cb(struct o2nm_node *node, int node_num, { o2quo_hb_down(node_num); + if (!node) + return; + if (node_num != o2nm_this_node()) o2net_disconnect_node(node); @@ -1709,6 +1712,8 @@ static void o2net_hb_node_up_cb(struct o2nm_node *node, int node_num, o2quo_hb_up(node_num); + BUG_ON(!node); + /* ensure an immediate connect attempt */ nn->nn_last_connect_attempt = jiffies - (msecs_to_jiffies(o2net_reconnect_delay()) + 1); -- cgit v1.2.3-18-g5258 From 8ca8b0bbd841b6bcd8ac05e51b0143aa61cfeff3 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Thu, 7 Oct 2010 17:01:27 -0700 Subject: ocfs2/cluster: Reorganize o2hb debugfs init o2hb debugfs handling is reorganized to allow for easy expansion. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 101 +++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index a8f10649674..16e49765c85 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -62,8 +62,19 @@ static unsigned long o2hb_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; static LIST_HEAD(o2hb_node_events); static DECLARE_WAIT_QUEUE_HEAD(o2hb_steady_queue); +#define O2HB_DB_TYPE_LIVENODES 0 +struct o2hb_debug_buf { + int db_type; + int db_size; + int db_len; + void *db_data; +}; + +static struct o2hb_debug_buf *o2hb_db_livenodes; + #define O2HB_DEBUG_DIR "o2hb" #define O2HB_DEBUG_LIVENODES "livenodes" + static struct dentry *o2hb_debug_dir; static struct dentry *o2hb_debug_livenodes; @@ -969,21 +980,35 @@ static int o2hb_thread(void *data) #ifdef CONFIG_DEBUG_FS static int o2hb_debug_open(struct inode *inode, struct file *file) { + struct o2hb_debug_buf *db = inode->i_private; unsigned long map[BITS_TO_LONGS(O2NM_MAX_NODES)]; char *buf = NULL; int i = -1; int out = 0; + /* max_nodes should be the largest bitmap we pass here */ + BUG_ON(sizeof(map) < db->db_size); + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) goto bail; - o2hb_fill_node_map(map, sizeof(map)); + switch (db->db_type) { + case O2HB_DB_TYPE_LIVENODES: + spin_lock(&o2hb_live_lock); + memcpy(map, db->db_data, db->db_size); + spin_unlock(&o2hb_live_lock); + break; + + default: + goto done; + } - while ((i = find_next_bit(map, O2NM_MAX_NODES, i + 1)) < O2NM_MAX_NODES) + while ((i = find_next_bit(map, db->db_len, i + 1)) < db->db_len) out += snprintf(buf + out, PAGE_SIZE - out, "%d ", i); out += snprintf(buf + out, PAGE_SIZE - out, "\n"); +done: i_size_write(inode, out); file->private_data = buf; @@ -1030,10 +1055,56 @@ static const struct file_operations o2hb_debug_fops = { void o2hb_exit(void) { - if (o2hb_debug_livenodes) - debugfs_remove(o2hb_debug_livenodes); - if (o2hb_debug_dir) - debugfs_remove(o2hb_debug_dir); + kfree(o2hb_db_livenodes); + debugfs_remove(o2hb_debug_livenodes); + debugfs_remove(o2hb_debug_dir); +} + +static struct dentry *o2hb_debug_create(const char *name, struct dentry *dir, + struct o2hb_debug_buf **db, int db_len, + int type, int size, int len, void *data) +{ + *db = kmalloc(db_len, GFP_KERNEL); + if (!*db) + return NULL; + + (*db)->db_type = type; + (*db)->db_size = size; + (*db)->db_len = len; + (*db)->db_data = data; + + return debugfs_create_file(name, S_IFREG|S_IRUSR, dir, *db, + &o2hb_debug_fops); +} + +static int o2hb_debug_init(void) +{ + int ret = -ENOMEM; + + o2hb_debug_dir = debugfs_create_dir(O2HB_DEBUG_DIR, NULL); + if (!o2hb_debug_dir) { + mlog_errno(ret); + goto bail; + } + + o2hb_debug_livenodes = o2hb_debug_create(O2HB_DEBUG_LIVENODES, + o2hb_debug_dir, + &o2hb_db_livenodes, + sizeof(*o2hb_db_livenodes), + O2HB_DB_TYPE_LIVENODES, + sizeof(o2hb_live_node_bitmap), + O2NM_MAX_NODES, + o2hb_live_node_bitmap); + if (!o2hb_debug_livenodes) { + mlog_errno(ret); + goto bail; + } + ret = 0; +bail: + if (ret) + o2hb_exit(); + + return ret; } int o2hb_init(void) @@ -1050,23 +1121,7 @@ int o2hb_init(void) memset(o2hb_live_node_bitmap, 0, sizeof(o2hb_live_node_bitmap)); - o2hb_debug_dir = debugfs_create_dir(O2HB_DEBUG_DIR, NULL); - if (!o2hb_debug_dir) { - mlog_errno(-ENOMEM); - return -ENOMEM; - } - - o2hb_debug_livenodes = debugfs_create_file(O2HB_DEBUG_LIVENODES, - S_IFREG|S_IRUSR, - o2hb_debug_dir, NULL, - &o2hb_debug_fops); - if (!o2hb_debug_livenodes) { - mlog_errno(-ENOMEM); - debugfs_remove(o2hb_debug_dir); - return -ENOMEM; - } - - return 0; + return o2hb_debug_init(); } /* if we're already in a callback then we're already serialized by the sem */ -- cgit v1.2.3-18-g5258 From 823a637ae933fde8fdb280612dd3ff9912e301e3 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 17:55:21 -0700 Subject: ocfs2/cluster: Maintain live node bitmap per heartbeat region Currently we track a global livenode bitmap that keeps track of all nodes that are heartbeating in all regions. This patch adds the ability to track the livenode bitmap on a per region basis. We will use this facility in a later patch to allow us to withstand the loss of a minority number of regions. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 16e49765c85..188f50269b8 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -174,6 +174,9 @@ struct o2hb_region { struct block_device *hr_bdev; struct o2hb_disk_slot *hr_slots; + /* live node map of this region */ + unsigned long hr_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; + /* let the person setting up hb wait for it to return until it * has reached a 'steady' state. This will be fixed when we have * a more complete api that doesn't lead to this sort of fragility. */ @@ -688,6 +691,8 @@ fire_callbacks: mlog(ML_HEARTBEAT, "Node %d (id 0x%llx) joined my region\n", slot->ds_node_num, (long long)slot->ds_last_generation); + set_bit(slot->ds_node_num, reg->hr_live_node_bitmap); + /* first on the list generates a callback */ if (list_empty(&o2hb_live_slots[slot->ds_node_num])) { set_bit(slot->ds_node_num, o2hb_live_node_bitmap); @@ -733,6 +738,8 @@ fire_callbacks: mlog(ML_HEARTBEAT, "Node %d left my region\n", slot->ds_node_num); + clear_bit(slot->ds_node_num, reg->hr_live_node_bitmap); + /* last off the live_slot generates a callback */ list_del_init(&slot->ds_live_item); if (list_empty(&o2hb_live_slots[slot->ds_node_num])) { -- cgit v1.2.3-18-g5258 From 536f0741f324f116d8b059295999945a2dac56bc Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Thu, 7 Oct 2010 17:03:07 -0700 Subject: ocfs2/cluster: Track number of global heartbeat regions In global heartbeat mode, we have a upper limit for the number of active regions. This patch adds the facility to track the number of active global heartbeat regions and fails to start heartbeat if the number exceeds the maximum. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 188f50269b8..d66b17c000d 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -62,6 +62,12 @@ static unsigned long o2hb_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; static LIST_HEAD(o2hb_node_events); static DECLARE_WAIT_QUEUE_HEAD(o2hb_steady_queue); +/* + * In global heartbeat, we maintain a series of region bitmaps. + * - o2hb_region_bitmap allows us to limit the region number to max region. + */ +static unsigned long o2hb_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; + #define O2HB_DB_TYPE_LIVENODES 0 struct o2hb_debug_buf { int db_type; @@ -176,6 +182,7 @@ struct o2hb_region { /* live node map of this region */ unsigned long hr_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; + unsigned int hr_region_num; /* let the person setting up hb wait for it to return until it * has reached a 'steady' state. This will be fixed when we have @@ -1127,6 +1134,7 @@ int o2hb_init(void) INIT_LIST_HEAD(&o2hb_node_events); memset(o2hb_live_node_bitmap, 0, sizeof(o2hb_live_node_bitmap)); + memset(o2hb_region_bitmap, 0, sizeof(o2hb_region_bitmap)); return o2hb_debug_init(); } @@ -1716,12 +1724,22 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g if (strlen(name) > O2HB_MAX_REGION_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); - config_item_init_type_name(®->hr_item, name, &o2hb_region_type); - spin_lock(&o2hb_live_lock); + reg->hr_region_num = 0; + if (o2hb_global_heartbeat_active()) { + reg->hr_region_num = find_first_zero_bit(o2hb_region_bitmap, + O2NM_MAX_REGIONS); + if (reg->hr_region_num >= O2NM_MAX_REGIONS) { + spin_unlock(&o2hb_live_lock); + return ERR_PTR(-EFBIG); + } + set_bit(reg->hr_region_num, o2hb_region_bitmap); + } list_add_tail(®->hr_all_item, &o2hb_all_regions); spin_unlock(&o2hb_live_lock); + config_item_init_type_name(®->hr_item, name, &o2hb_region_type); + return ®->hr_item; } @@ -1733,6 +1751,8 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group, /* stop the thread when the user removes the region dir */ spin_lock(&o2hb_live_lock); + if (o2hb_global_heartbeat_active()) + clear_bit(reg->hr_region_num, o2hb_region_bitmap); hb_task = reg->hr_task; reg->hr_task = NULL; spin_unlock(&o2hb_live_lock); -- cgit v1.2.3-18-g5258 From e7d656baf6607a0775f4ca85464a4ead306741e5 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 17:55:18 -0700 Subject: ocfs2/cluster: Track bitmap of live heartbeat regions A heartbeat region becomes live (or active) after a fixed number of (steady) iterations. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index d66b17c000d..2a7cd17e96f 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -65,8 +65,10 @@ static DECLARE_WAIT_QUEUE_HEAD(o2hb_steady_queue); /* * In global heartbeat, we maintain a series of region bitmaps. * - o2hb_region_bitmap allows us to limit the region number to max region. + * - o2hb_live_region_bitmap tracks live regions (seen steady iterations). */ static unsigned long o2hb_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; +static unsigned long o2hb_live_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; #define O2HB_DB_TYPE_LIVENODES 0 struct o2hb_debug_buf { @@ -1135,6 +1137,7 @@ int o2hb_init(void) memset(o2hb_live_node_bitmap, 0, sizeof(o2hb_live_node_bitmap)); memset(o2hb_region_bitmap, 0, sizeof(o2hb_region_bitmap)); + memset(o2hb_live_region_bitmap, 0, sizeof(o2hb_live_region_bitmap)); return o2hb_debug_init(); } @@ -1563,6 +1566,8 @@ static ssize_t o2hb_region_dev_write(struct o2hb_region *reg, /* Ok, we were woken. Make sure it wasn't by drop_item() */ spin_lock(&o2hb_live_lock); hb_task = reg->hr_task; + if (o2hb_global_heartbeat_active()) + set_bit(reg->hr_region_num, o2hb_live_region_bitmap); spin_unlock(&o2hb_live_lock); if (hb_task) @@ -1751,8 +1756,10 @@ static void o2hb_heartbeat_group_drop_item(struct config_group *group, /* stop the thread when the user removes the region dir */ spin_lock(&o2hb_live_lock); - if (o2hb_global_heartbeat_active()) + if (o2hb_global_heartbeat_active()) { clear_bit(reg->hr_region_num, o2hb_region_bitmap); + clear_bit(reg->hr_region_num, o2hb_live_region_bitmap); + } hb_task = reg->hr_task; reg->hr_task = NULL; spin_unlock(&o2hb_live_lock); -- cgit v1.2.3-18-g5258 From 43182d2a799865872041b6e4d8387131e9462f56 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 17:55:16 -0700 Subject: ocfs2/cluster: Maintain bitmap of quorum regions o2hb allows online adding of regions. However, a newly added region is not used in quorum calculations unless it has been added on all nodes. This patch tracks a bitmap of such quorum regions. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 2a7cd17e96f..62a8af27134 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -66,9 +66,12 @@ static DECLARE_WAIT_QUEUE_HEAD(o2hb_steady_queue); * In global heartbeat, we maintain a series of region bitmaps. * - o2hb_region_bitmap allows us to limit the region number to max region. * - o2hb_live_region_bitmap tracks live regions (seen steady iterations). + * - o2hb_quorum_region_bitmap tracks live regions that have seen all nodes + * heartbeat on it. */ static unsigned long o2hb_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; static unsigned long o2hb_live_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; +static unsigned long o2hb_quorum_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; #define O2HB_DB_TYPE_LIVENODES 0 struct o2hb_debug_buf { @@ -607,6 +610,35 @@ static void o2hb_shutdown_slot(struct o2hb_disk_slot *slot) o2nm_node_put(node); } +static void o2hb_set_quorum_device(struct o2hb_region *reg, + struct o2hb_disk_slot *slot) +{ + assert_spin_locked(&o2hb_live_lock); + + if (!o2hb_global_heartbeat_active()) + return; + + if (test_bit(reg->hr_region_num, o2hb_quorum_region_bitmap)) + return; + + /* + * A region can be added to the quorum only when it sees all + * live nodes heartbeat on it. In other words, the region has been + * added to all nodes. + */ + if (memcmp(reg->hr_live_node_bitmap, o2hb_live_node_bitmap, + sizeof(o2hb_live_node_bitmap))) + return; + + if (slot->ds_changed_samples < O2HB_LIVE_THRESHOLD) + return; + + printk(KERN_NOTICE "o2hb: Region %s is now a quorum device\n", + config_item_name(®->hr_item)); + + set_bit(reg->hr_region_num, o2hb_quorum_region_bitmap); +} + static int o2hb_check_slot(struct o2hb_region *reg, struct o2hb_disk_slot *slot) { @@ -772,6 +804,8 @@ fire_callbacks: slot->ds_equal_samples = 0; } out: + o2hb_set_quorum_device(reg, slot); + spin_unlock(&o2hb_live_lock); o2hb_run_event_list(&event); @@ -1138,6 +1172,7 @@ int o2hb_init(void) memset(o2hb_live_node_bitmap, 0, sizeof(o2hb_live_node_bitmap)); memset(o2hb_region_bitmap, 0, sizeof(o2hb_region_bitmap)); memset(o2hb_live_region_bitmap, 0, sizeof(o2hb_live_region_bitmap)); + memset(o2hb_quorum_region_bitmap, 0, sizeof(o2hb_quorum_region_bitmap)); return o2hb_debug_init(); } -- cgit v1.2.3-18-g5258 From b1c5ebfbe398b3360614a4788c02061cd153e60a Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Thu, 7 Oct 2010 17:05:52 -0700 Subject: ocfs2/cluster: Maintain bitmap of failed regions In global heartbeat mode, we track the bitmap of regions that have seen heartbeat timeouts. We fence if the number of such regions is greater than or equal to half the number of quorum regions. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 62a8af27134..f890656127f 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -68,10 +68,12 @@ static DECLARE_WAIT_QUEUE_HEAD(o2hb_steady_queue); * - o2hb_live_region_bitmap tracks live regions (seen steady iterations). * - o2hb_quorum_region_bitmap tracks live regions that have seen all nodes * heartbeat on it. + * - o2hb_failed_region_bitmap tracks the regions that have seen io timeouts. */ static unsigned long o2hb_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; static unsigned long o2hb_live_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; static unsigned long o2hb_quorum_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; +static unsigned long o2hb_failed_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; #define O2HB_DB_TYPE_LIVENODES 0 struct o2hb_debug_buf { @@ -217,8 +219,19 @@ struct o2hb_bio_wait_ctxt { int wc_error; }; +static int o2hb_pop_count(void *map, int count) +{ + int i = -1, pop = 0; + + while ((i = find_next_bit(map, count, i + 1)) < count) + pop++; + return pop; +} + static void o2hb_write_timeout(struct work_struct *work) { + int failed, quorum; + unsigned long flags; struct o2hb_region *reg = container_of(work, struct o2hb_region, hr_write_timeout_work.work); @@ -226,6 +239,28 @@ static void o2hb_write_timeout(struct work_struct *work) mlog(ML_ERROR, "Heartbeat write timeout to device %s after %u " "milliseconds\n", reg->hr_dev_name, jiffies_to_msecs(jiffies - reg->hr_last_timeout_start)); + + if (o2hb_global_heartbeat_active()) { + spin_lock_irqsave(&o2hb_live_lock, flags); + if (test_bit(reg->hr_region_num, o2hb_quorum_region_bitmap)) + set_bit(reg->hr_region_num, o2hb_failed_region_bitmap); + failed = o2hb_pop_count(&o2hb_failed_region_bitmap, + O2NM_MAX_REGIONS); + quorum = o2hb_pop_count(&o2hb_quorum_region_bitmap, + O2NM_MAX_REGIONS); + spin_unlock_irqrestore(&o2hb_live_lock, flags); + + mlog(ML_HEARTBEAT, "Number of regions %d, failed regions %d\n", + quorum, failed); + + /* + * Fence if the number of failed regions >= half the number + * of quorum regions + */ + if ((failed << 1) < quorum) + return; + } + o2quo_disk_timeout(); } @@ -234,6 +269,11 @@ static void o2hb_arm_write_timeout(struct o2hb_region *reg) mlog(ML_HEARTBEAT, "Queue write timeout for %u ms\n", O2HB_MAX_WRITE_TIMEOUT_MS); + if (o2hb_global_heartbeat_active()) { + spin_lock(&o2hb_live_lock); + clear_bit(reg->hr_region_num, o2hb_failed_region_bitmap); + spin_unlock(&o2hb_live_lock); + } cancel_delayed_work(®->hr_write_timeout_work); reg->hr_last_timeout_start = jiffies; schedule_delayed_work(®->hr_write_timeout_work, @@ -1173,6 +1213,7 @@ int o2hb_init(void) memset(o2hb_region_bitmap, 0, sizeof(o2hb_region_bitmap)); memset(o2hb_live_region_bitmap, 0, sizeof(o2hb_live_region_bitmap)); memset(o2hb_quorum_region_bitmap, 0, sizeof(o2hb_quorum_region_bitmap)); + memset(o2hb_failed_region_bitmap, 0, sizeof(o2hb_failed_region_bitmap)); return o2hb_debug_init(); } -- cgit v1.2.3-18-g5258 From a6de013654b4839c8609e26241ebd9eb1ecc52e6 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 17:55:13 -0700 Subject: ocfs2/cluster: Create debugfs files for live, quorum and failed region bitmaps This patch prints the bitmaps of live, quorum and failed regions. This information will be useful in debugging cluster issues. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index f890656127f..b06b9e52fba 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -76,6 +76,9 @@ static unsigned long o2hb_quorum_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; static unsigned long o2hb_failed_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; #define O2HB_DB_TYPE_LIVENODES 0 +#define O2HB_DB_TYPE_LIVEREGIONS 1 +#define O2HB_DB_TYPE_QUORUMREGIONS 2 +#define O2HB_DB_TYPE_FAILEDREGIONS 3 struct o2hb_debug_buf { int db_type; int db_size; @@ -84,12 +87,21 @@ struct o2hb_debug_buf { }; static struct o2hb_debug_buf *o2hb_db_livenodes; +static struct o2hb_debug_buf *o2hb_db_liveregions; +static struct o2hb_debug_buf *o2hb_db_quorumregions; +static struct o2hb_debug_buf *o2hb_db_failedregions; #define O2HB_DEBUG_DIR "o2hb" #define O2HB_DEBUG_LIVENODES "livenodes" +#define O2HB_DEBUG_LIVEREGIONS "live_regions" +#define O2HB_DEBUG_QUORUMREGIONS "quorum_regions" +#define O2HB_DEBUG_FAILEDREGIONS "failed_regions" static struct dentry *o2hb_debug_dir; static struct dentry *o2hb_debug_livenodes; +static struct dentry *o2hb_debug_liveregions; +static struct dentry *o2hb_debug_quorumregions; +static struct dentry *o2hb_debug_failedregions; static LIST_HEAD(o2hb_all_regions); @@ -1085,6 +1097,9 @@ static int o2hb_debug_open(struct inode *inode, struct file *file) switch (db->db_type) { case O2HB_DB_TYPE_LIVENODES: + case O2HB_DB_TYPE_LIVEREGIONS: + case O2HB_DB_TYPE_QUORUMREGIONS: + case O2HB_DB_TYPE_FAILEDREGIONS: spin_lock(&o2hb_live_lock); memcpy(map, db->db_data, db->db_size); spin_unlock(&o2hb_live_lock); @@ -1146,6 +1161,12 @@ static const struct file_operations o2hb_debug_fops = { void o2hb_exit(void) { kfree(o2hb_db_livenodes); + kfree(o2hb_db_liveregions); + kfree(o2hb_db_quorumregions); + kfree(o2hb_db_failedregions); + debugfs_remove(o2hb_debug_failedregions); + debugfs_remove(o2hb_debug_quorumregions); + debugfs_remove(o2hb_debug_liveregions); debugfs_remove(o2hb_debug_livenodes); debugfs_remove(o2hb_debug_dir); } @@ -1189,6 +1210,48 @@ static int o2hb_debug_init(void) mlog_errno(ret); goto bail; } + + o2hb_debug_liveregions = o2hb_debug_create(O2HB_DEBUG_LIVEREGIONS, + o2hb_debug_dir, + &o2hb_db_liveregions, + sizeof(*o2hb_db_liveregions), + O2HB_DB_TYPE_LIVEREGIONS, + sizeof(o2hb_live_region_bitmap), + O2NM_MAX_REGIONS, + o2hb_live_region_bitmap); + if (!o2hb_debug_liveregions) { + mlog_errno(ret); + goto bail; + } + + o2hb_debug_quorumregions = + o2hb_debug_create(O2HB_DEBUG_QUORUMREGIONS, + o2hb_debug_dir, + &o2hb_db_quorumregions, + sizeof(*o2hb_db_quorumregions), + O2HB_DB_TYPE_QUORUMREGIONS, + sizeof(o2hb_quorum_region_bitmap), + O2NM_MAX_REGIONS, + o2hb_quorum_region_bitmap); + if (!o2hb_debug_quorumregions) { + mlog_errno(ret); + goto bail; + } + + o2hb_debug_failedregions = + o2hb_debug_create(O2HB_DEBUG_FAILEDREGIONS, + o2hb_debug_dir, + &o2hb_db_failedregions, + sizeof(*o2hb_db_failedregions), + O2HB_DB_TYPE_FAILEDREGIONS, + sizeof(o2hb_failed_region_bitmap), + O2NM_MAX_REGIONS, + o2hb_failed_region_bitmap); + if (!o2hb_debug_failedregions) { + mlog_errno(ret); + goto bail; + } + ret = 0; bail: if (ret) -- cgit v1.2.3-18-g5258 From 1f28530537f106f83e5cf7ef0193075667b6d520 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 17:55:12 -0700 Subject: ocfs2/cluster: Create debugfs dir/files for each region This patch creates debugfs directory for each o2hb region and creates files to expose the region number and the per region live node bitmap. This information will be useful in debugging cluster issues. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index b06b9e52fba..f28de4b09c6 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -79,6 +79,8 @@ static unsigned long o2hb_failed_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; #define O2HB_DB_TYPE_LIVEREGIONS 1 #define O2HB_DB_TYPE_QUORUMREGIONS 2 #define O2HB_DB_TYPE_FAILEDREGIONS 3 +#define O2HB_DB_TYPE_REGION_LIVENODES 4 +#define O2HB_DB_TYPE_REGION_NUMBER 5 struct o2hb_debug_buf { int db_type; int db_size; @@ -96,6 +98,7 @@ static struct o2hb_debug_buf *o2hb_db_failedregions; #define O2HB_DEBUG_LIVEREGIONS "live_regions" #define O2HB_DEBUG_QUORUMREGIONS "quorum_regions" #define O2HB_DEBUG_FAILEDREGIONS "failed_regions" +#define O2HB_DEBUG_REGION_NUMBER "num" static struct dentry *o2hb_debug_dir; static struct dentry *o2hb_debug_livenodes; @@ -203,6 +206,12 @@ struct o2hb_region { unsigned long hr_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; unsigned int hr_region_num; + struct dentry *hr_debug_dir; + struct dentry *hr_debug_livenodes; + struct dentry *hr_debug_regnum; + struct o2hb_debug_buf *hr_db_livenodes; + struct o2hb_debug_buf *hr_db_regnum; + /* let the person setting up hb wait for it to return until it * has reached a 'steady' state. This will be fixed when we have * a more complete api that doesn't lead to this sort of fragility. */ @@ -1083,6 +1092,7 @@ static int o2hb_thread(void *data) static int o2hb_debug_open(struct inode *inode, struct file *file) { struct o2hb_debug_buf *db = inode->i_private; + struct o2hb_region *reg; unsigned long map[BITS_TO_LONGS(O2NM_MAX_NODES)]; char *buf = NULL; int i = -1; @@ -1105,6 +1115,19 @@ static int o2hb_debug_open(struct inode *inode, struct file *file) spin_unlock(&o2hb_live_lock); break; + case O2HB_DB_TYPE_REGION_LIVENODES: + spin_lock(&o2hb_live_lock); + reg = (struct o2hb_region *)db->db_data; + memcpy(map, reg->hr_live_node_bitmap, db->db_size); + spin_unlock(&o2hb_live_lock); + break; + + case O2HB_DB_TYPE_REGION_NUMBER: + reg = (struct o2hb_region *)db->db_data; + out += snprintf(buf + out, PAGE_SIZE - out, "%d\n", + reg->hr_region_num); + goto done; + default: goto done; } @@ -1342,6 +1365,12 @@ static void o2hb_region_release(struct config_item *item) if (reg->hr_slots) kfree(reg->hr_slots); + kfree(reg->hr_db_regnum); + kfree(reg->hr_db_livenodes); + debugfs_remove(reg->hr_debug_livenodes); + debugfs_remove(reg->hr_debug_regnum); + debugfs_remove(reg->hr_debug_dir); + spin_lock(&o2hb_live_lock); list_del(®->hr_all_item); spin_unlock(&o2hb_live_lock); @@ -1856,10 +1885,52 @@ static struct o2hb_heartbeat_group *to_o2hb_heartbeat_group(struct config_group : NULL; } +static int o2hb_debug_region_init(struct o2hb_region *reg, struct dentry *dir) +{ + int ret = -ENOMEM; + + reg->hr_debug_dir = + debugfs_create_dir(config_item_name(®->hr_item), dir); + if (!reg->hr_debug_dir) { + mlog_errno(ret); + goto bail; + } + + reg->hr_debug_livenodes = + o2hb_debug_create(O2HB_DEBUG_LIVENODES, + reg->hr_debug_dir, + &(reg->hr_db_livenodes), + sizeof(*(reg->hr_db_livenodes)), + O2HB_DB_TYPE_REGION_LIVENODES, + sizeof(reg->hr_live_node_bitmap), + O2NM_MAX_NODES, reg); + if (!reg->hr_debug_livenodes) { + mlog_errno(ret); + goto bail; + } + + reg->hr_debug_regnum = + o2hb_debug_create(O2HB_DEBUG_REGION_NUMBER, + reg->hr_debug_dir, + &(reg->hr_db_regnum), + sizeof(*(reg->hr_db_regnum)), + O2HB_DB_TYPE_REGION_NUMBER, + 0, O2NM_MAX_NODES, reg); + if (!reg->hr_debug_regnum) { + mlog_errno(ret); + goto bail; + } + + ret = 0; +bail: + return ret; +} + static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *group, const char *name) { struct o2hb_region *reg = NULL; + int ret; reg = kzalloc(sizeof(struct o2hb_region), GFP_KERNEL); if (reg == NULL) @@ -1884,6 +1955,12 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g config_item_init_type_name(®->hr_item, name, &o2hb_region_type); + ret = o2hb_debug_region_init(reg, o2hb_debug_dir); + if (ret) { + config_item_put(®->hr_item); + return ERR_PTR(ret); + } + return ®->hr_item; } -- cgit v1.2.3-18-g5258 From d6aa1c7c9e4b48081c2302e14b0f857017461efd Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 18:50:50 -0700 Subject: ocfs2/cluster: Add mlogs for heartbeat up/down events This patch adds mlogs for o2hb up and down events. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index f28de4b09c6..e8676accf90 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -797,6 +797,8 @@ fire_callbacks: /* first on the list generates a callback */ if (list_empty(&o2hb_live_slots[slot->ds_node_num])) { + mlog(ML_HEARTBEAT, "o2hb: Add node %d to live nodes " + "bitmap\n", slot->ds_node_num); set_bit(slot->ds_node_num, o2hb_live_node_bitmap); o2hb_queue_node_event(&event, O2HB_NODE_UP_CB, node, @@ -845,6 +847,8 @@ fire_callbacks: /* last off the live_slot generates a callback */ list_del_init(&slot->ds_live_item); if (list_empty(&o2hb_live_slots[slot->ds_node_num])) { + mlog(ML_HEARTBEAT, "o2hb: Remove node %d from live " + "nodes bitmap\n", slot->ds_node_num); clear_bit(slot->ds_node_num, o2hb_live_node_bitmap); /* node can be null */ -- cgit v1.2.3-18-g5258 From 43695d095dfaf266a8a940d9b07eed7f46076b49 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Wed, 6 Oct 2010 17:55:09 -0700 Subject: ocfs2/cluster: Show per region heartbeat elapsed time This patch adds a per region debugfs file that shows the elapsed time since the time the o2hb timer was last armed. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index e8676accf90..29aee2128ed 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -81,6 +81,7 @@ static unsigned long o2hb_failed_region_bitmap[BITS_TO_LONGS(O2NM_MAX_REGIONS)]; #define O2HB_DB_TYPE_FAILEDREGIONS 3 #define O2HB_DB_TYPE_REGION_LIVENODES 4 #define O2HB_DB_TYPE_REGION_NUMBER 5 +#define O2HB_DB_TYPE_REGION_ELAPSED_TIME 6 struct o2hb_debug_buf { int db_type; int db_size; @@ -99,6 +100,7 @@ static struct o2hb_debug_buf *o2hb_db_failedregions; #define O2HB_DEBUG_QUORUMREGIONS "quorum_regions" #define O2HB_DEBUG_FAILEDREGIONS "failed_regions" #define O2HB_DEBUG_REGION_NUMBER "num" +#define O2HB_DEBUG_REGION_ELAPSED_TIME "elapsed_time_in_ms" static struct dentry *o2hb_debug_dir; static struct dentry *o2hb_debug_livenodes; @@ -209,8 +211,10 @@ struct o2hb_region { struct dentry *hr_debug_dir; struct dentry *hr_debug_livenodes; struct dentry *hr_debug_regnum; + struct dentry *hr_debug_elapsed_time; struct o2hb_debug_buf *hr_db_livenodes; struct o2hb_debug_buf *hr_db_regnum; + struct o2hb_debug_buf *hr_db_elapsed_time; /* let the person setting up hb wait for it to return until it * has reached a 'steady' state. This will be fixed when we have @@ -1132,6 +1136,13 @@ static int o2hb_debug_open(struct inode *inode, struct file *file) reg->hr_region_num); goto done; + case O2HB_DB_TYPE_REGION_ELAPSED_TIME: + reg = (struct o2hb_region *)db->db_data; + out += snprintf(buf + out, PAGE_SIZE - out, "%u\n", + jiffies_to_msecs(jiffies - + reg->hr_last_timeout_start)); + goto done; + default: goto done; } @@ -1925,6 +1936,18 @@ static int o2hb_debug_region_init(struct o2hb_region *reg, struct dentry *dir) goto bail; } + reg->hr_debug_elapsed_time = + o2hb_debug_create(O2HB_DEBUG_REGION_ELAPSED_TIME, + reg->hr_debug_dir, + &(reg->hr_db_elapsed_time), + sizeof(*(reg->hr_db_elapsed_time)), + O2HB_DB_TYPE_REGION_ELAPSED_TIME, + 0, 0, reg); + if (!reg->hr_debug_elapsed_time) { + mlog_errno(ret); + goto bail; + } + ret = 0; bail: return ret; -- cgit v1.2.3-18-g5258 From 081003fff467ea0e727f66d5d435b4f473a789b3 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Fri, 1 Oct 2010 07:43:54 +0000 Subject: xfs: properly account for reclaimed inodes When marking an inode reclaimable, a per-AG counter is increased, the inode is tagged reclaimable in its per-AG tree, and, when this is the first reclaimable inode in the AG, the AG entry in the per-mount tree is also tagged. When an inode is finally reclaimed, however, it is only deleted from the per-AG tree. Neither the counter is decreased, nor is the parent tree's AG entry untagged properly. Since the tags in the per-mount tree are not cleared, the inode shrinker iterates over all AGs that have had reclaimable inodes at one point in time. The counters on the other hand signal an increasing amount of slab objects to reclaim. Since "70e60ce xfs: convert inode shrinker to per-filesystem context" this is not a real issue anymore because the shrinker bails out after one iteration. But the problem was observable on a machine running v2.6.34, where the reclaimable work increased and each process going into direct reclaim eventually got stuck on the xfs inode shrinking path, trying to scan several million objects. Fix this by properly unwinding the reclaimable-state tracking of an inode when it is reclaimed. Signed-off-by: Johannes Weiner Cc: stable@kernel.org Reviewed-by: Dave Chinner Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_sync.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index d59c4a65d49..81976ffed7d 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -668,14 +668,11 @@ xfs_inode_set_reclaim_tag( xfs_perag_put(pag); } -void -__xfs_inode_clear_reclaim_tag( - xfs_mount_t *mp, +STATIC void +__xfs_inode_clear_reclaim( xfs_perag_t *pag, xfs_inode_t *ip) { - radix_tree_tag_clear(&pag->pag_ici_root, - XFS_INO_TO_AGINO(mp, ip->i_ino), XFS_ICI_RECLAIM_TAG); pag->pag_ici_reclaimable--; if (!pag->pag_ici_reclaimable) { /* clear the reclaim tag from the perag radix tree */ @@ -689,6 +686,17 @@ __xfs_inode_clear_reclaim_tag( } } +void +__xfs_inode_clear_reclaim_tag( + xfs_mount_t *mp, + xfs_perag_t *pag, + xfs_inode_t *ip) +{ + radix_tree_tag_clear(&pag->pag_ici_root, + XFS_INO_TO_AGINO(mp, ip->i_ino), XFS_ICI_RECLAIM_TAG); + __xfs_inode_clear_reclaim(pag, ip); +} + /* * Inodes in different states need to be treated differently, and the return * value of xfs_iflush is not sufficient to get this right. The following table @@ -838,6 +846,7 @@ reclaim: if (!radix_tree_delete(&pag->pag_ici_root, XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino))) ASSERT(0); + __xfs_inode_clear_reclaim(pag, ip); write_unlock(&pag->pag_ici_lock); /* -- cgit v1.2.3-18-g5258 From 936aeb5c4a9fa799abd7d630a94223acedcaad50 Mon Sep 17 00:00:00 2001 From: Henry C Chang Date: Wed, 22 Sep 2010 20:21:17 -0700 Subject: ceph: fix list_add usage on unsafe_writes list Fix argument order. Signed-off-by: Henry C Chang Signed-off-by: Sage Weil --- fs/ceph/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 8c044a4f045..66e4da6dba2 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -697,7 +697,7 @@ more: * start_request so that a tid has been assigned. */ spin_lock(&ci->i_unsafe_lock); - list_add(&ci->i_unsafe_writes, &req->r_unsafe_item); + list_add(&req->r_unsafe_item, &ci->i_unsafe_writes); spin_unlock(&ci->i_unsafe_lock); ceph_get_cap_refs(ci, CEPH_CAP_FILE_WR); } -- cgit v1.2.3-18-g5258 From 6bc18876ba01fd4a077db6e1ed27201e4bda8864 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 27 Sep 2010 10:18:52 -0700 Subject: ceph: avoid null deref in osd request error path If we interrupt an osd request, we call __cancel_request, but it wasn't verifying that req->r_osd was non-NULL before dereferencing it. This could cause a crash if osds were flapping and we aborted a request on said osd. Reported-by: Henry C Chang Signed-off-by: Sage Weil --- fs/ceph/osd_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index dfced1dacbc..3b5571b8ce2 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -549,7 +549,7 @@ static void __unregister_request(struct ceph_osd_client *osdc, */ static void __cancel_request(struct ceph_osd_request *req) { - if (req->r_sent) { + if (req->r_sent && req->r_osd) { ceph_con_revoke(&req->r_osd->o_con, req->r_request); req->r_sent = 0; } -- cgit v1.2.3-18-g5258 From 92923dcbfcad107b0e0469f579a2455729ccf10e Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 5 Oct 2010 16:03:41 +0530 Subject: ceph: Fix return value of encode_fh function encode_fh function should return 255 on error as done by other file system to indicate EOVERFLOW. Also max_len is in sizeof(u32) units and not in bytes. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Sage Weil --- fs/ceph/export.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ceph/export.c b/fs/ceph/export.c index 4480cb1c63e..387c5823944 100644 --- a/fs/ceph/export.c +++ b/fs/ceph/export.c @@ -42,32 +42,34 @@ struct ceph_nfs_confh { static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len, int connectable) { + int type; struct ceph_nfs_fh *fh = (void *)rawfh; struct ceph_nfs_confh *cfh = (void *)rawfh; struct dentry *parent = dentry->d_parent; struct inode *inode = dentry->d_inode; - int type; + int connected_handle_length = sizeof(*cfh)/4; + int handle_length = sizeof(*fh)/4; /* don't re-export snaps */ if (ceph_snap(inode) != CEPH_NOSNAP) return -EINVAL; - if (*max_len >= sizeof(*cfh)) { + if (*max_len >= connected_handle_length) { dout("encode_fh %p connectable\n", dentry); cfh->ino = ceph_ino(dentry->d_inode); cfh->parent_ino = ceph_ino(parent->d_inode); cfh->parent_name_hash = parent->d_name.hash; - *max_len = sizeof(*cfh); + *max_len = connected_handle_length; type = 2; - } else if (*max_len > sizeof(*fh)) { + } else if (*max_len >= handle_length) { if (connectable) - return -ENOSPC; + return 255; dout("encode_fh %p\n", dentry); fh->ino = ceph_ino(dentry->d_inode); - *max_len = sizeof(*fh); + *max_len = handle_length; type = 1; } else { - return -ENOSPC; + return 255; } return type; } -- cgit v1.2.3-18-g5258 From bba0cd0e3d97472855840af817b766e3f632a501 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 5 Oct 2010 16:03:42 +0530 Subject: ceph: Update max_len with minimum required size encode_fh on error should update max_len with minimum required size, so that caller can redo the call with the reallocated buffer. This is required with open by handle patch series Signed-off-by: Aneesh Kumar K.V Signed-off-by: Sage Weil --- fs/ceph/export.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/export.c b/fs/ceph/export.c index 387c5823944..e38423e82f2 100644 --- a/fs/ceph/export.c +++ b/fs/ceph/export.c @@ -62,13 +62,16 @@ static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len, *max_len = connected_handle_length; type = 2; } else if (*max_len >= handle_length) { - if (connectable) + if (connectable) { + *max_len = connected_handle_length; return 255; + } dout("encode_fh %p\n", dentry); fh->ino = ceph_ino(dentry->d_inode); *max_len = handle_length; type = 1; } else { + *max_len = handle_length; return 255; } return type; -- cgit v1.2.3-18-g5258 From 21b559de56695d36b3f0819b7e2454737db254f8 Mon Sep 17 00:00:00 2001 From: Greg Farnum Date: Wed, 6 Oct 2010 15:46:30 -0700 Subject: ceph: send cap release message early on failed revoke. If an MDS tries to revoke caps that we don't have, we want to send releases early since they probably contain the caps message the MDS is looking for. Previously, we only sent the messages if we didn't have the inode either. But in a multi-mds system we can retain the inode after dropping all caps for a single MDS. Signed-off-by: Greg Farnum Signed-off-by: Sage Weil --- fs/ceph/caps.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 73c153092f7..97de325a49f 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2774,15 +2774,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, if (op == CEPH_CAP_OP_IMPORT) __queue_cap_release(session, vino.ino, cap_id, mseq, seq); - - /* - * send any full release message to try to move things - * along for the mds (who clearly thinks we still have this - * cap). - */ - ceph_add_cap_releases(mdsc, session); - ceph_send_cap_releases(mdsc, session); - goto done; + goto flush_cap_releases; } /* these will work even if we don't have a cap yet */ @@ -2810,7 +2802,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, dout(" no cap on %p ino %llx.%llx from mds%d\n", inode, ceph_ino(inode), ceph_snap(inode), mds); spin_unlock(&inode->i_lock); - goto done; + goto flush_cap_releases; } /* note that each of these drops i_lock for us */ @@ -2834,6 +2826,17 @@ void ceph_handle_caps(struct ceph_mds_session *session, ceph_cap_op_name(op)); } + goto done; + +flush_cap_releases: + /* + * send any full release message to try to move things + * along for the mds (who clearly thinks we still have this + * cap). + */ + ceph_add_cap_releases(mdsc, session); + ceph_send_cap_releases(mdsc, session); + done: mutex_unlock(&session->s_mutex); done_unlocked: -- cgit v1.2.3-18-g5258 From d91f2438d881514e4a923fd786dbd94b764a9440 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 22 Sep 2010 11:16:00 -0700 Subject: ceph: update issue_seq on cap grant We need to update the issue_seq on any grant operation, be it via an MDS reply or a separate grant message. The update in the grant path was missing. This broke cap release for inodes in which the MDS sent an explicit grant message that was not soon after followed by a successful MDS reply on the same inode. Also fix the signedness on seq locals. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 97de325a49f..5e9da996a15 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2283,7 +2283,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, { struct ceph_inode_info *ci = ceph_inode(inode); int mds = session->s_mds; - int seq = le32_to_cpu(grant->seq); + unsigned seq = le32_to_cpu(grant->seq); + unsigned issue_seq = le32_to_cpu(grant->issue_seq); int newcaps = le32_to_cpu(grant->caps); int issued, implemented, used, wanted, dirty; u64 size = le64_to_cpu(grant->size); @@ -2295,8 +2296,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, int revoked_rdcache = 0; int queue_invalidate = 0; - dout("handle_cap_grant inode %p cap %p mds%d seq %d %s\n", - inode, cap, mds, seq, ceph_cap_string(newcaps)); + dout("handle_cap_grant inode %p cap %p mds%d seq %u/%u %s\n", + inode, cap, mds, seq, issue_seq, ceph_cap_string(newcaps)); dout(" size %llu max_size %llu, i_size %llu\n", size, max_size, inode->i_size); @@ -2392,6 +2393,7 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, } cap->seq = seq; + cap->issue_seq = issue_seq; /* file layout may have changed */ ci->i_layout = grant->layout; -- cgit v1.2.3-18-g5258 From 9d002df492b14c690425d9785530371b6c1ccbca Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 6 Oct 2010 19:51:11 -0400 Subject: cifs: add routines to build sessions and tcons on the fly This patch is rather large, but it's a bit difficult to do piecemeal... For non-multiuser mounts, everything will basically work as it does today. A call to cifs_sb_tlink will return the "master" tcon link. Turn the tcon pointer in the cifs_sb into a radix tree that uses the fsuid of the process as a key. The value is a new "tcon_link" struct that contains info about a tcon that's under construction. When a new process needs a tcon, it'll call cifs_sb_tcon. That will then look up the tcon_link in the radix tree. If it exists and is valid, it's returned. If it doesn't exist, then we stuff a new tcon_link into the tree and mark it as pending and then go and try to build the session/tcon. If that works, the tcon pointer in the tcon_link is updated and the pending flag is cleared. If the construction fails, then we set the tcon pointer to an ERR_PTR and clear the pending flag. If the radix tree is searched and the tcon_link is marked pending then we go to sleep and wait for the pending flag to be cleared. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 7 +- fs/cifs/cifsglob.h | 32 +++--- fs/cifs/connect.c | 268 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 279 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index e04e6923d35..5ce57bdf186 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -15,6 +15,8 @@ * the GNU Lesser General Public License for more details. * */ +#include + #ifndef _CIFS_FS_SB_H #define _CIFS_FS_SB_H @@ -40,8 +42,9 @@ #define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ struct cifs_sb_info { - struct cifsTconInfo *ptcon; /* primary mount */ - struct list_head nested_tcon_q; + struct radix_tree_root tlink_tree; +#define CIFS_TLINK_MASTER_TAG 0 /* is "master" (mount) tcon */ + spinlock_t tlink_tree_lock; struct nls_table *local_nls; unsigned int rsize; unsigned int wsize; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d5324853203..9a7c472a153 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -317,42 +317,36 @@ struct cifsTconInfo { * "get" on the container. */ struct tcon_link { - spinlock_t tl_lock; - u32 tl_count; - u64 tl_time; + unsigned long tl_index; + unsigned long tl_flags; +#define TCON_LINK_MASTER 0 +#define TCON_LINK_PENDING 1 +#define TCON_LINK_IN_TREE 2 + unsigned long tl_time; + atomic_t tl_count; struct cifsTconInfo *tl_tcon; }; -static inline struct tcon_link * -cifs_sb_tlink(struct cifs_sb_info *cifs_sb) -{ - return (struct tcon_link *)cifs_sb->ptcon; -} +extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb); static inline struct cifsTconInfo * tlink_tcon(struct tcon_link *tlink) { - return (struct cifsTconInfo *)tlink; + return tlink->tl_tcon; } -static inline void -cifs_put_tlink(struct tcon_link *tlink) -{ - return; -} +extern void cifs_put_tlink(struct tcon_link *tlink); static inline struct tcon_link * cifs_get_tlink(struct tcon_link *tlink) { + if (tlink && !IS_ERR(tlink)) + atomic_inc(&tlink->tl_count); return tlink; } /* This function is always expected to succeed */ -static inline struct cifsTconInfo * -cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) -{ - return cifs_sb->ptcon; -} +extern struct cifsTconInfo *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); /* * This info hangs off the cifsFileInfo structure, pointed to by llist. diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f6a3091c287..3156a9de947 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -109,6 +109,9 @@ struct smb_vol { struct nls_table *local_nls; }; +#define TLINK_ERROR_EXPIRE (1 * HZ) + + static int ipv4_connect(struct TCP_Server_Info *server); static int ipv6_connect(struct TCP_Server_Info *server); @@ -1959,6 +1962,23 @@ out_fail: return ERR_PTR(rc); } +void +cifs_put_tlink(struct tcon_link *tlink) +{ + if (!tlink || IS_ERR(tlink)) + return; + + if (!atomic_dec_and_test(&tlink->tl_count) || + test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) { + tlink->tl_time = jiffies; + return; + } + + if (!IS_ERR(tlink_tcon(tlink))) + cifs_put_tcon(tlink_tcon(tlink)); + kfree(tlink); + return; +} int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, @@ -2641,6 +2661,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, struct TCP_Server_Info *srvTcp; char *full_path; char *mount_data = mount_data_global; + struct tcon_link *tlink; #ifdef CONFIG_CIFS_DFS_UPCALL struct dfs_info3_param *referrals = NULL; unsigned int num_referrals = 0; @@ -2652,6 +2673,7 @@ try_mount_again: pSesInfo = NULL; srvTcp = NULL; full_path = NULL; + tlink = NULL; xid = GetXid(); @@ -2727,8 +2749,6 @@ try_mount_again: goto remote_path_check; } - cifs_sb->ptcon = tcon; - /* do not care if following two calls succeed - informational */ if (!tcon->ipc) { CIFSSMBQFSDeviceInfo(xid, tcon); @@ -2837,6 +2857,35 @@ remote_path_check: #endif } + if (rc) + goto mount_fail_check; + + /* now, hang the tcon off of the superblock */ + tlink = kzalloc(sizeof *tlink, GFP_KERNEL); + if (tlink == NULL) { + rc = -ENOMEM; + goto mount_fail_check; + } + + tlink->tl_index = pSesInfo->linux_uid; + tlink->tl_tcon = tcon; + tlink->tl_time = jiffies; + set_bit(TCON_LINK_MASTER, &tlink->tl_flags); + set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags); + + rc = radix_tree_preload(GFP_KERNEL); + if (rc == -ENOMEM) { + kfree(tlink); + goto mount_fail_check; + } + + spin_lock(&cifs_sb->tlink_tree_lock); + radix_tree_insert(&cifs_sb->tlink_tree, pSesInfo->linux_uid, tlink); + radix_tree_tag_set(&cifs_sb->tlink_tree, pSesInfo->linux_uid, + CIFS_TLINK_MASTER_TAG); + spin_unlock(&cifs_sb->tlink_tree_lock); + radix_tree_preload_end(); + mount_fail_check: /* on error free sesinfo and tcon struct if needed */ if (rc) { @@ -3023,19 +3072,37 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, int cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) { - int rc = 0; + int i, ret; char *tmp; - struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); + struct tcon_link *tlink[8]; + unsigned long index = 0; + + do { + spin_lock(&cifs_sb->tlink_tree_lock); + ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, + (void **)tlink, index, + ARRAY_SIZE(tlink)); + /* increment index for next pass */ + if (ret > 0) + index = tlink[ret - 1]->tl_index + 1; + for (i = 0; i < ret; i++) { + cifs_get_tlink(tlink[i]); + clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); + radix_tree_delete(&cifs_sb->tlink_tree, + tlink[i]->tl_index); + } + spin_unlock(&cifs_sb->tlink_tree_lock); - cifs_put_tcon(tcon); + for (i = 0; i < ret; i++) + cifs_put_tlink(tlink[i]); + } while (ret != 0); - cifs_sb->ptcon = NULL; tmp = cifs_sb->prepath; cifs_sb->prepathlen = 0; cifs_sb->prepath = NULL; kfree(tmp); - return rc; + return 0; } int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) @@ -3096,3 +3163,190 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, return rc; } +struct cifsTconInfo * +cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) +{ + struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb); + struct cifsSesInfo *ses; + struct cifsTconInfo *tcon = NULL; + struct smb_vol *vol_info; + char username[MAX_USERNAME_SIZE + 1]; + + vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); + if (vol_info == NULL) { + tcon = ERR_PTR(-ENOMEM); + goto out; + } + + snprintf(username, MAX_USERNAME_SIZE, "krb50x%x", fsuid); + vol_info->username = username; + vol_info->local_nls = cifs_sb->local_nls; + vol_info->linux_uid = fsuid; + vol_info->cred_uid = fsuid; + vol_info->UNC = master_tcon->treeName; + vol_info->retry = master_tcon->retry; + vol_info->nocase = master_tcon->nocase; + vol_info->local_lease = master_tcon->local_lease; + vol_info->no_linux_ext = !master_tcon->unix_ext; + + /* FIXME: allow for other secFlg settings */ + vol_info->secFlg = CIFSSEC_MUST_KRB5; + + /* get a reference for the same TCP session */ + write_lock(&cifs_tcp_ses_lock); + ++master_tcon->ses->server->srv_count; + write_unlock(&cifs_tcp_ses_lock); + + ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); + if (IS_ERR(ses)) { + tcon = (struct cifsTconInfo *)ses; + cifs_put_tcp_session(master_tcon->ses->server); + goto out; + } + + tcon = cifs_get_tcon(ses, vol_info); + if (IS_ERR(tcon)) { + cifs_put_smb_ses(ses); + goto out; + } + + if (ses->capabilities & CAP_UNIX) + reset_cifs_unix_caps(0, tcon, NULL, vol_info); +out: + kfree(vol_info); + + return tcon; +} + +static struct tcon_link * +cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb) +{ + struct tcon_link *tlink; + unsigned int ret; + + spin_lock(&cifs_sb->tlink_tree_lock); + ret = radix_tree_gang_lookup_tag(&cifs_sb->tlink_tree, (void **)&tlink, + 0, 1, CIFS_TLINK_MASTER_TAG); + spin_unlock(&cifs_sb->tlink_tree_lock); + + /* the master tcon should always be present */ + if (ret == 0) + BUG(); + + return tlink; +} + +struct cifsTconInfo * +cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) +{ + return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); +} + +static int +cifs_sb_tcon_pending_wait(void *unused) +{ + schedule(); + return signal_pending(current) ? -ERESTARTSYS : 0; +} + +/* + * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the + * current task. + * + * If the superblock doesn't refer to a multiuser mount, then just return + * the master tcon for the mount. + * + * First, search the radix tree for an existing tcon for this fsuid. If one + * exists, then check to see if it's pending construction. If it is then wait + * for construction to complete. Once it's no longer pending, check to see if + * it failed and either return an error or retry construction, depending on + * the timeout. + * + * If one doesn't exist then insert a new tcon_link struct into the tree and + * try to construct a new one. + */ +struct tcon_link * +cifs_sb_tlink(struct cifs_sb_info *cifs_sb) +{ + int ret; + unsigned long fsuid = (unsigned long) current_fsuid(); + struct tcon_link *tlink, *newtlink; + + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) + return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb)); + + spin_lock(&cifs_sb->tlink_tree_lock); + tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); + if (tlink) + cifs_get_tlink(tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + + if (tlink == NULL) { + newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL); + if (newtlink == NULL) + return ERR_PTR(-ENOMEM); + newtlink->tl_index = fsuid; + newtlink->tl_tcon = ERR_PTR(-EACCES); + set_bit(TCON_LINK_PENDING, &newtlink->tl_flags); + set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags); + cifs_get_tlink(newtlink); + + ret = radix_tree_preload(GFP_KERNEL); + if (ret != 0) { + kfree(newtlink); + return ERR_PTR(ret); + } + + spin_lock(&cifs_sb->tlink_tree_lock); + /* was one inserted after previous search? */ + tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid); + if (tlink) { + cifs_get_tlink(tlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + radix_tree_preload_end(); + kfree(newtlink); + goto wait_for_construction; + } + ret = radix_tree_insert(&cifs_sb->tlink_tree, fsuid, newtlink); + spin_unlock(&cifs_sb->tlink_tree_lock); + radix_tree_preload_end(); + if (ret) { + kfree(newtlink); + return ERR_PTR(ret); + } + tlink = newtlink; + } else { +wait_for_construction: + ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, + cifs_sb_tcon_pending_wait, + TASK_INTERRUPTIBLE); + if (ret) { + cifs_put_tlink(tlink); + return ERR_PTR(ret); + } + + /* if it's good, return it */ + if (!IS_ERR(tlink->tl_tcon)) + return tlink; + + /* return error if we tried this already recently */ + if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) { + cifs_put_tlink(tlink); + return ERR_PTR(-EACCES); + } + + if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags)) + goto wait_for_construction; + } + + tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid); + clear_bit(TCON_LINK_PENDING, &tlink->tl_flags); + wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING); + + if (IS_ERR(tlink->tl_tcon)) { + cifs_put_tlink(tlink); + return ERR_PTR(-EACCES); + } + + return tlink; +} -- cgit v1.2.3-18-g5258 From 0eb8a132c449c755b7a3f18f33365b2040c47347 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 6 Oct 2010 19:51:12 -0400 Subject: cifs: add "multiuser" mount option This allows someone to declare a mount as a multiuser mount. Multiuser mounts also imply "noperm" since we want to allow the server to handle permission checking. It also (for now) requires Kerberos authentication. Eventually, we could expand this to other authtypes, but that requires a scheme to allow per-user credential stashing in some form. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'fs') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 3156a9de947..e65f72d1f23 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -100,6 +100,7 @@ struct smb_vol { bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ bool fsc:1; /* enable fscache */ bool mfsymlinks:1; /* use Minshall+French Symlinks */ + bool multiuser:1; unsigned int rsize; unsigned int wsize; bool sockopt_tcp_nodelay:1; @@ -1347,6 +1348,8 @@ cifs_parse_mount_options(char *options, const char *devname, vol->fsc = true; } else if (strnicmp(data, "mfsymlinks", 10) == 0) { vol->mfsymlinks = true; + } else if (strnicmp(data, "multiuser", 8) == 0) { + vol->multiuser = true; } else printk(KERN_WARNING "CIFS: Unknown mount option %s\n", data); @@ -1378,6 +1381,13 @@ cifs_parse_mount_options(char *options, const char *devname, return 1; } } + + if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) { + cERROR(1, "Multiuser mounts currently require krb5 " + "authentication!"); + return 1; + } + if (vol->UNCip == NULL) vol->UNCip = &vol->UNC[2]; @@ -2563,6 +2573,9 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; if (pvolume_info->fsc) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; + if (pvolume_info->multiuser) + cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER | + CIFS_MOUNT_NO_PERM); if (pvolume_info->direct_io) { cFYI(1, "mounting share using direct i/o"); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; -- cgit v1.2.3-18-g5258 From 13cd4b7f7472eea7cbc1ab34e042842fbb902160 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 7 Oct 2010 18:46:32 +0000 Subject: [CIFS] Various small checkpatch cleanups Signed-off-by: Steve French --- fs/cifs/cifs_debug.h | 2 +- fs/cifs/cifsfs.h | 2 +- fs/cifs/cifsglob.h | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index aa316891ac0..8942b28cf80 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -34,7 +34,7 @@ void cifs_dump_mids(struct TCP_Server_Info *); extern int traceSMB; /* flag which enables the function below */ void dump_smb(struct smb_hdr *, int); #define CIFS_INFO 0x01 -#define CIFS_RC 0x02 +#define CIFS_RC 0x02 #define CIFS_TIMER 0x04 /* diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index d82f5fb4761..786bdf36aeb 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -104,7 +104,7 @@ extern int cifs_readlink(struct dentry *direntry, char __user *buffer, extern int cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname); extern int cifs_removexattr(struct dentry *, const char *); -extern int cifs_setxattr(struct dentry *, const char *, const void *, +extern int cifs_setxattr(struct dentry *, const char *, const void *, size_t, int); extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9a7c472a153..4f85dfdf197 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -517,16 +517,16 @@ struct oplock_q_entry { /* for pending dnotify requests */ struct dir_notify_req { - struct list_head lhead; - __le16 Pid; - __le16 PidHigh; - __u16 Mid; - __u16 Tid; - __u16 Uid; - __u16 netfid; - __u32 filter; /* CompletionFilter (for multishot) */ - int multishot; - struct file *pfile; + struct list_head lhead; + __le16 Pid; + __le16 PidHigh; + __u16 Mid; + __u16 Tid; + __u16 Uid; + __u16 netfid; + __u32 filter; /* CompletionFilter (for multishot) */ + int multishot; + struct file *pfile; }; struct dfs_info3_param { -- cgit v1.2.3-18-g5258 From 3aa1c8c2900065a51268430ab48a1b42fdfe5b45 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 7 Oct 2010 14:46:28 -0400 Subject: cifs: on multiuser mount, set ownership to current_fsuid/current_fsgid (try #5) ...when unix extensions aren't enabled. This makes everything on the mount appear to be owned by the current user. This version of the patch differs from previous versions however in that the admin can still force the ownership of all files to appear as a single user via the uid=/gid= options. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/inode.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'fs') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index df29a3a3d80..7e5b8c39a07 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1755,11 +1755,21 @@ check_inval: int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { + struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); + struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); int err = cifs_revalidate_dentry(dentry); + if (!err) { generic_fillattr(dentry->d_inode, stat); stat->blksize = CIFS_MAX_MSGSIZE; stat->ino = CIFS_I(dentry->d_inode)->uniqueid; + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && + !tcon->unix_ext) { + if (!cifs_sb->mnt_uid) + stat->uid = current_fsuid(); + if (!cifs_sb->mnt_uid) + stat->gid = current_fsgid(); + } } return err; } -- cgit v1.2.3-18-g5258 From 2de970ff69bbcc5a4b7440df669a595b2b1acd73 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 6 Oct 2010 19:51:12 -0400 Subject: cifs: implement recurring workqueue job to prune old tcons Create a workqueue job that cleans out unused tlinks. For now, it uses a hardcoded expire time of 10 minutes. When it's done, the work rearms itself. On umount, the work is cancelled before tearing down the tlink tree. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/connect.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 5ce57bdf186..586ee3d527d 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -59,5 +59,6 @@ struct cifs_sb_info { char *mountdata; /* mount options received at mount time */ #endif struct backing_dev_info bdi; + struct delayed_work prune_tlinks; }; #endif /* _CIFS_FS_SB_H */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e65f72d1f23..1092e9e839c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -110,11 +110,13 @@ struct smb_vol { struct nls_table *local_nls; }; +/* FIXME: should these be tunable? */ #define TLINK_ERROR_EXPIRE (1 * HZ) - +#define TLINK_IDLE_EXPIRE (600 * HZ) static int ipv4_connect(struct TCP_Server_Info *server); static int ipv6_connect(struct TCP_Server_Info *server); +static void cifs_prune_tlinks(struct work_struct *work); /* * cifs tcp session reconnection @@ -2494,6 +2496,8 @@ convert_delimiter(char *path, char delim) static void setup_cifs_sb(struct smb_vol *pvolume_info, struct cifs_sb_info *cifs_sb) { + INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks); + if (pvolume_info->rsize > CIFSMaxBufSize) { cERROR(1, "rsize %d too large, using MaxBufSize", pvolume_info->rsize); @@ -2899,6 +2903,9 @@ remote_path_check: spin_unlock(&cifs_sb->tlink_tree_lock); radix_tree_preload_end(); + queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, + TLINK_IDLE_EXPIRE); + mount_fail_check: /* on error free sesinfo and tcon struct if needed */ if (rc) { @@ -3090,6 +3097,8 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) struct tcon_link *tlink[8]; unsigned long index = 0; + cancel_delayed_work_sync(&cifs_sb->prune_tlinks); + do { spin_lock(&cifs_sb->tlink_tree_lock); ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, @@ -3363,3 +3372,50 @@ wait_for_construction: return tlink; } + +/* + * periodic workqueue job that scans tcon_tree for a superblock and closes + * out tcons. + */ +static void +cifs_prune_tlinks(struct work_struct *work) +{ + struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info, + prune_tlinks.work); + struct tcon_link *tlink[8]; + unsigned long now = jiffies; + unsigned long index = 0; + int i, ret; + + do { + spin_lock(&cifs_sb->tlink_tree_lock); + ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree, + (void **)tlink, index, + ARRAY_SIZE(tlink)); + /* increment index for next pass */ + if (ret > 0) + index = tlink[ret - 1]->tl_index + 1; + for (i = 0; i < ret; i++) { + if (test_bit(TCON_LINK_MASTER, &tlink[i]->tl_flags) || + atomic_read(&tlink[i]->tl_count) != 0 || + time_after(tlink[i]->tl_time + TLINK_IDLE_EXPIRE, + now)) { + tlink[i] = NULL; + continue; + } + cifs_get_tlink(tlink[i]); + clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags); + radix_tree_delete(&cifs_sb->tlink_tree, + tlink[i]->tl_index); + } + spin_unlock(&cifs_sb->tlink_tree_lock); + + for (i = 0; i < ret; i++) { + if (tlink[i] != NULL) + cifs_put_tlink(tlink[i]); + } + } while (ret != 0); + + queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks, + TLINK_IDLE_EXPIRE); +} -- cgit v1.2.3-18-g5258 From ccc46a7402200a3b28a8fa1605ea5405a9ef66f7 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 6 Oct 2010 19:51:12 -0400 Subject: cifs: fix module refcount leak in find_domain_name find_domain_name() uses load_nls_default which takes a module reference on the appropriate NLS module, but doesn't put it. Signed-off-by: Jeff Layton Cc: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 730038a1298..89fb94fac4b 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -328,13 +328,16 @@ find_domain_name(struct cifsSesInfo *ses) if (!attrsize) break; if (!ses->domainName) { + struct nls_table *default_nls; ses->domainName = kmalloc(attrsize + 1, GFP_KERNEL); if (!ses->domainName) return -ENOMEM; + default_nls = load_nls_default(); cifs_from_ucs2(ses->domainName, (__le16 *)blobptr, attrsize, attrsize, - load_nls_default(), false); + default_nls, false); + unload_nls(default_nls); break; } } -- cgit v1.2.3-18-g5258 From d2445556137c38ae15d3191174bfd235630ed7cd Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 8 Oct 2010 03:38:46 +0000 Subject: [CIFS] Remove build warning Signed-off-by: Steve French --- fs/cifs/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 1092e9e839c..4944fc84d5e 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3185,7 +3185,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, return rc; } -struct cifsTconInfo * +static struct cifsTconInfo * cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) { struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb); -- cgit v1.2.3-18-g5258 From e4eab08d6050ad04d960738f589724204fd1064c Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 20 Aug 2010 21:14:46 +0100 Subject: ARM: 6342/1: fix ASLR of PIE executables Since commits 990cb8acf2 and cc92c28b2d, it is possible to have full address space layout randomization (ASLR) on ARM. Except that one small change was missing for ASLR of PIE executables. Signed-off-by: Nicolas Pitre Signed-off-by: Russell King --- fs/binfmt_elf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 535e763ab1a..6884e198e0c 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -800,7 +800,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) * default mmap base, as well as whatever program they * might try to exec. This is because the brk will * follow the loader, and is not movable. */ -#ifdef CONFIG_X86 +#if defined(CONFIG_X86) || defined(CONFIG_ARM) load_bias = 0; #else load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr); -- cgit v1.2.3-18-g5258 From f17b1f9f1a5882e486aad469b9ac4cb18581707f Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Thu, 7 Oct 2010 13:37:51 -0400 Subject: exofs: Fix double page_unlock BUG in write_begin/end This BUG is there since the first submit of the code, but only triggered in last Kernel. It's timing related do to the asynchronous object-creation behaviour of exofs. (Which should be investigated farther) The bug is obvious hence the fixed. Signed-off-by: Boaz Harrosh --- fs/exofs/inode.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index eb7368ebd8c..3eadd97324b 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -54,6 +54,9 @@ struct page_collect { unsigned nr_pages; unsigned long length; loff_t pg_first; /* keep 64bit also in 32-arches */ + bool read_4_write; /* This means two things: that the read is sync + * And the pages should not be unlocked. + */ }; static void _pcol_init(struct page_collect *pcol, unsigned expected_pages, @@ -71,6 +74,7 @@ static void _pcol_init(struct page_collect *pcol, unsigned expected_pages, pcol->nr_pages = 0; pcol->length = 0; pcol->pg_first = -1; + pcol->read_4_write = false; } static void _pcol_reset(struct page_collect *pcol) @@ -347,7 +351,8 @@ static int readpage_strip(void *data, struct page *page) if (PageError(page)) ClearPageError(page); - unlock_page(page); + if (!pcol->read_4_write) + unlock_page(page); EXOFS_DBGMSG("readpage_strip(0x%lx, 0x%lx) empty page," " splitting\n", inode->i_ino, page->index); @@ -428,6 +433,7 @@ static int _readpage(struct page *page, bool is_sync) /* readpage_strip might call read_exec(,is_sync==false) at several * places but not if we have a single page. */ + pcol.read_4_write = is_sync; ret = readpage_strip(&pcol, page); if (ret) { EXOFS_ERR("_readpage => %d\n", ret); -- cgit v1.2.3-18-g5258 From 0dd12c21951e085357046b68c3a108273062d2aa Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 8 Oct 2010 12:20:12 -0400 Subject: cifs: initialize tlink_tree_lock and tlink_tree Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index c96345c3314..3258c822328 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -98,6 +98,9 @@ cifs_read_super(struct super_block *sb, void *data, if (cifs_sb == NULL) return -ENOMEM; + spin_lock_init(&cifs_sb->tlink_tree_lock); + INIT_RADIX_TREE(&cifs_sb->tlink_tree, GFP_KERNEL); + rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY); if (rc) { kfree(cifs_sb); -- cgit v1.2.3-18-g5258 From 4d94aa1b1d437f9513ddc89974d8bd214b8304f6 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Sat, 9 Oct 2010 10:27:04 -0700 Subject: ocfs2/cluster: Bump up dlm protocol to version 1.1 dlm protocol 1.1. activates messages DLM_QUERY_REGION and DLM_QUERY_NODEINFO that are a must for global heartbeat. It also activates o2hb_global_heartbeat_active(). Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 2 +- fs/ocfs2/dlm/dlmdomain.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 29aee2128ed..6a1280a013e 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -2429,6 +2429,6 @@ EXPORT_SYMBOL_GPL(o2hb_get_all_regions); int o2hb_global_heartbeat_active(void) { - return 0; + return (o2hb_heartbeat_mode == O2HB_HEARTBEAT_GLOBAL); } EXPORT_SYMBOL(o2hb_global_heartbeat_active); diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 78d428f5e10..58a93b95373 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -135,7 +135,7 @@ static DECLARE_WAIT_QUEUE_HEAD(dlm_domain_events); */ static const struct dlm_protocol_version dlm_protocol = { .pv_major = 1, - .pv_minor = 0, + .pv_minor = 1, }; #define DLM_DOMAIN_BACKOFF_MS 200 -- cgit v1.2.3-18-g5258 From 6370a6ad3b53df90b4700977f7718118a2cd524a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 11 Oct 2010 15:12:27 +0200 Subject: workqueue: add and use WQ_MEM_RECLAIM flag Add WQ_MEM_RECLAIM flag which currently maps to WQ_RESCUER, mark WQ_RESCUER as internal and replace all external WQ_RESCUER usages to WQ_MEM_RECLAIM. This makes the API users express the intent of the workqueue instead of indicating the internal mechanism used to guarantee forward progress. This is also to make it cleaner to add more semantics to WQ_MEM_RECLAIM. For example, if deemed necessary, memory reclaim workqueues can be made highpri. This patch doesn't introduce any functional change. Signed-off-by: Tejun Heo Cc: Jeff Garzik Cc: Dave Chinner Cc: Steven Whitehouse --- fs/gfs2/main.c | 2 +- fs/xfs/linux-2.6/xfs_buf.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index b1e9630eb46..1c5f46075d5 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -140,7 +140,7 @@ static int __init init_gfs2_fs(void) error = -ENOMEM; gfs_recovery_wq = alloc_workqueue("gfs_recovery", - WQ_NON_REENTRANT | WQ_RESCUER, 0); + WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0); if (!gfs_recovery_wq) goto fail_wq; diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 286e36e21da..6838aefca71 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -1933,7 +1933,7 @@ xfs_buf_init(void) goto out; xfslogd_workqueue = alloc_workqueue("xfslogd", - WQ_RESCUER | WQ_HIGHPRI, 1); + WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); if (!xfslogd_workqueue) goto out_free_buf_zone; -- cgit v1.2.3-18-g5258 From 9b5cd10e4c14a1a642076ace6a73be3d33c91fb6 Mon Sep 17 00:00:00 2001 From: Srinivas Eeda Date: Tue, 5 Oct 2010 15:53:06 -0700 Subject: ocfs2: validate bg_free_bits_count after update This patch adds a safe check to ensure bg_free_bits_count doesn't exceed bg_bits in a group descriptor. This is to avoid on disk corruption that was seen recently. debugfs: group <52803072> Group Chain: 179 Parent Inode: 11 Generation: 2959379682 CRC32: 00000000 ECC: 0000 ## Block# Total Used Free Contig Size 0 52803072 32256 4294965350 34202 18207 4032 ...... Signed-off-by: Srinivas Eeda Signed-off-by: Joel Becker --- fs/ocfs2/suballoc.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index 8a286f54dca..64f2c50a1c3 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -1380,6 +1380,14 @@ static inline int ocfs2_block_group_set_bits(handle_t *handle, } le16_add_cpu(&bg->bg_free_bits_count, -num_bits); + if (le16_to_cpu(bg->bg_free_bits_count) > le16_to_cpu(bg->bg_bits)) { + ocfs2_error(alloc_inode->i_sb, "Group descriptor # %llu has bit" + " count %u but claims %u are freed. num_bits %d", + (unsigned long long)le64_to_cpu(bg->bg_blkno), + le16_to_cpu(bg->bg_bits), + le16_to_cpu(bg->bg_free_bits_count), num_bits); + return -EROFS; + } while(num_bits--) ocfs2_set_bit(bit_off++, bitmap); @@ -2419,6 +2427,14 @@ static int ocfs2_block_group_clear_bits(handle_t *handle, (unsigned long *) undo_bg->bg_bitmap); } le16_add_cpu(&bg->bg_free_bits_count, num_bits); + if (le16_to_cpu(bg->bg_free_bits_count) > le16_to_cpu(bg->bg_bits)) { + ocfs2_error(alloc_inode->i_sb, "Group descriptor # %llu has bit" + " count %u but claims %u are freed. num_bits %d", + (unsigned long long)le64_to_cpu(bg->bg_blkno), + le16_to_cpu(bg->bg_bits), + le16_to_cpu(bg->bg_free_bits_count), num_bits); + return -EROFS; + } if (undo_fn) jbd_unlock_bh_state(group_bh); -- cgit v1.2.3-18-g5258 From f30d44f3e54a94e037da7a71d714b585dab28d9e Mon Sep 17 00:00:00 2001 From: Poyo VL Date: Mon, 11 Oct 2010 13:45:52 -0700 Subject: When I tried to compile I got the following warning: fs/ocfs2/slot_map.c: In function ‘ocfs2_init_slot_info’: fs/ocfs2/slot_map.c:360: warning: ‘bytes’ may be used uninitialized in this function fs/ocfs2/slot_map.c:360: note: ‘bytes’ was declared here Compiler: gcc version 4.4.3 (GCC) on Mandriva I'm not sure why this warning occurs, I think compiler don't know that variable "bytes" is initialized when it is sent by reference to ocfs2_slot_map_physical_size and it throws that ugly warning. However, a simple initialization of "bytes" variable with 0 will fix it. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ionut Gabriel Popescu Signed-off-by: Joel Becker --- fs/ocfs2/slot_map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c index bfbd7e9e949..ab4e0172cc1 100644 --- a/fs/ocfs2/slot_map.c +++ b/fs/ocfs2/slot_map.c @@ -357,7 +357,7 @@ static int ocfs2_map_slot_buffers(struct ocfs2_super *osb, { int status = 0; u64 blkno; - unsigned long long blocks, bytes; + unsigned long long blocks, bytes = 0; unsigned int i; struct buffer_head *bh; -- cgit v1.2.3-18-g5258 From 75d9bbc73804285020aa4d99bd2a9600edea8945 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Mon, 11 Oct 2010 12:57:09 -0500 Subject: Initialize max_slots early Functions such as ocfs2_recovery_init() make use of osb->max_slots. Initialize osb->max_slots early so the functions may use the correct value. Signed-off-by: Goldwyn Rodrigues Signed-off-by: Joel Becker --- fs/ocfs2/super.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 350e8b5a939..b578644b663 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -2105,6 +2105,15 @@ static int ocfs2_initialize_super(struct super_block *sb, snprintf(osb->dev_str, sizeof(osb->dev_str), "%u,%u", MAJOR(osb->sb->s_dev), MINOR(osb->sb->s_dev)); + osb->max_slots = le16_to_cpu(di->id2.i_super.s_max_slots); + if (osb->max_slots > OCFS2_MAX_SLOTS || osb->max_slots == 0) { + mlog(ML_ERROR, "Invalid number of node slots (%u)\n", + osb->max_slots); + status = -EINVAL; + goto bail; + } + mlog(0, "max_slots for this device: %u\n", osb->max_slots); + ocfs2_orphan_scan_init(osb); status = ocfs2_recovery_init(osb); @@ -2143,15 +2152,6 @@ static int ocfs2_initialize_super(struct super_block *sb, goto bail; } - osb->max_slots = le16_to_cpu(di->id2.i_super.s_max_slots); - if (osb->max_slots > OCFS2_MAX_SLOTS || osb->max_slots == 0) { - mlog(ML_ERROR, "Invalid number of node slots (%u)\n", - osb->max_slots); - status = -EINVAL; - goto bail; - } - mlog(0, "max_slots for this device: %u\n", osb->max_slots); - osb->slot_recovery_generations = kcalloc(osb->max_slots, sizeof(*osb->slot_recovery_generations), GFP_KERNEL); -- cgit v1.2.3-18-g5258 From 7bdb0d18bfd381cc5491eb95973ec5604b356c7e Mon Sep 17 00:00:00 2001 From: Tristan Ye Date: Mon, 11 Oct 2010 16:46:39 +0800 Subject: ocfs2: Add a mount option "coherency=*" to handle cluster coherency for O_DIRECT writes. Currently, the default behavior of O_DIRECT writes was allowing concurrent writing among nodes to the same file, with no cluster coherency guaranteed (no EX lock held). This can leave stale data in the cache for buffered reads on other nodes. The new mount option introduce a chance to choose two different behaviors for O_DIRECT writes: * coherency=full, as the default value, will disallow concurrent O_DIRECT writes by taking EX locks. * coherency=buffered, allow concurrent O_DIRECT writes without EX lock among nodes, which gains high performance at risk of getting stale data on other nodes. Signed-off-by: Tristan Ye Signed-off-by: Joel Becker --- fs/ocfs2/file.c | 29 +++++++++++++++++++++++++++-- fs/ocfs2/ocfs2.h | 3 +++ fs/ocfs2/super.c | 15 +++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 13af9937bdd..9e8cc4346b7 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2225,6 +2225,8 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, struct file *file = iocb->ki_filp; struct inode *inode = file->f_path.dentry->d_inode; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + int full_coherency = !(osb->s_mount_opt & + OCFS2_MOUNT_COHERENCY_BUFFERED); mlog_entry("(0x%p, %u, '%.*s')\n", file, (unsigned int)nr_segs, @@ -2248,14 +2250,37 @@ relock: have_alloc_sem = 1; } - /* concurrent O_DIRECT writes are allowed */ - rw_level = !direct_io; + /* + * Concurrent O_DIRECT writes are allowed with + * mount_option "coherency=buffered". + */ + rw_level = (!direct_io || full_coherency); + ret = ocfs2_rw_lock(inode, rw_level); if (ret < 0) { mlog_errno(ret); goto out_sems; } + /* + * O_DIRECT writes with "coherency=full" need to take EX cluster + * inode_lock to guarantee coherency. + */ + if (direct_io && full_coherency) { + /* + * We need to take and drop the inode lock to force + * other nodes to drop their caches. Buffered I/O + * already does this in write_begin(). + */ + ret = ocfs2_inode_lock(inode, NULL, 1); + if (ret < 0) { + mlog_errno(ret); + goto out_sems; + } + + ocfs2_inode_unlock(inode, 1); + } + can_do_direct = direct_io; ret = ocfs2_prepare_inode_for_write(file, ppos, iocb->ki_left, appending, diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 687e291d73f..3064feef143 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -263,6 +263,9 @@ enum ocfs2_mount_options control lists */ OCFS2_MOUNT_USRQUOTA = 1 << 10, /* We support user quotas */ OCFS2_MOUNT_GRPQUOTA = 1 << 11, /* We support group quotas */ + + OCFS2_MOUNT_COHERENCY_BUFFERED = 1 << 12 /* Allow concurrent O_DIRECT + writes */ }; #define OCFS2_OSB_SOFT_RO 0x0001 diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index b578644b663..9122d59f812 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -177,6 +177,8 @@ enum { Opt_noacl, Opt_usrquota, Opt_grpquota, + Opt_coherency_buffered, + Opt_coherency_full, Opt_resv_level, Opt_dir_resv_level, Opt_err, @@ -205,6 +207,8 @@ static const match_table_t tokens = { {Opt_noacl, "noacl"}, {Opt_usrquota, "usrquota"}, {Opt_grpquota, "grpquota"}, + {Opt_coherency_buffered, "coherency=buffered"}, + {Opt_coherency_full, "coherency=full"}, {Opt_resv_level, "resv_level=%u"}, {Opt_dir_resv_level, "dir_resv_level=%u"}, {Opt_err, NULL} @@ -1452,6 +1456,12 @@ static int ocfs2_parse_options(struct super_block *sb, case Opt_grpquota: mopt->mount_opt |= OCFS2_MOUNT_GRPQUOTA; break; + case Opt_coherency_buffered: + mopt->mount_opt |= OCFS2_MOUNT_COHERENCY_BUFFERED; + break; + case Opt_coherency_full: + mopt->mount_opt &= ~OCFS2_MOUNT_COHERENCY_BUFFERED; + break; case Opt_acl: mopt->mount_opt |= OCFS2_MOUNT_POSIX_ACL; mopt->mount_opt &= ~OCFS2_MOUNT_NO_POSIX_ACL; @@ -1550,6 +1560,11 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) if (opts & OCFS2_MOUNT_GRPQUOTA) seq_printf(s, ",grpquota"); + if (opts & OCFS2_MOUNT_COHERENCY_BUFFERED) + seq_printf(s, ",coherency=buffered"); + else + seq_printf(s, ",coherency=full"); + if (opts & OCFS2_MOUNT_NOUSERXATTR) seq_printf(s, ",nouser_xattr"); else -- cgit v1.2.3-18-g5258 From 7c5347733dcc4ba0bac0baf86d99fae0561f33b7 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 11 Oct 2010 18:13:31 -0400 Subject: fanotify: disable fanotify syscalls This patch disables the fanotify syscalls by just not building them and letting the cond_syscall() statements in kernel/sys_ni.c redirect them to sys_ni_syscall(). It was pointed out by Tvrtko Ursulin that the fanotify interface did not include an explicit prioritization between groups. This is necessary for fanotify to be usable for hierarchical storage management software, as they must get first access to the file, before inotify-like notifiers see the file. This feature can be added in an ABI compatible way in the next release (by using a number of bits in the flags field to carry the info) but it was suggested by Alan that maybe we should just hold off and do it in the next cycle, likely with an (new) explicit argument to the syscall. I don't like this approach best as I know people are already starting to use the current interface, but Alan is all wise and noone on list backed me up with just using what we have. I feel this is needlessly ripping the rug out from under people at the last minute, but if others think it needs to be a new argument it might be the best way forward. Three choices: Go with what we got (and implement the new feature next cycle). Add a new field right now (and implement the new feature next cycle). Wait till next cycle to release the ABI (and implement the new feature next cycle). This is number 3. Signed-off-by: Eric Paris Signed-off-by: Linus Torvalds --- fs/notify/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/Kconfig b/fs/notify/Kconfig index 22c629eedd8..b388443c3a0 100644 --- a/fs/notify/Kconfig +++ b/fs/notify/Kconfig @@ -3,4 +3,4 @@ config FSNOTIFY source "fs/notify/dnotify/Kconfig" source "fs/notify/inotify/Kconfig" -source "fs/notify/fanotify/Kconfig" +#source "fs/notify/fanotify/Kconfig" -- cgit v1.2.3-18-g5258 From 9daa42e22030f0c5c357a5e1f8af658411beda6b Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Sun, 10 Oct 2010 13:21:05 -0500 Subject: CIFS ntlm authentication and signing - Build a proper av/ti pair blob for ntlmv2 without extended security authentication Build an av pair blob as part of ntlmv2 (without extended security) auth request. Include netbios and dns names for domain and server and a timestamp in the blob. Signed-off-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 80 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 89fb94fac4b..e3edd8a6840 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -263,27 +263,85 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, } #endif /* CIFS_WEAK_PW_HASH */ -/* This is just a filler for ntlmv2 type of security mechanisms. - * Older servers are not very particular about the contents of av pairs - * in the blob and for sec mechs like ntlmv2, there is no negotiation - * as in ntlmssp, so unless domain and server netbios and dns names - * are specified, there is no way to obtain name. In case of ntlmssp, - * server provides that info in type 2 challenge packet +/* Build a proper attribute value/target info pairs blob. + * Fill in netbios and dns domain name and workstation name + * and client time (total five av pairs and + one end of fields indicator. + * Allocate domain name which gets freed when session struct is deallocated. */ static int -build_avpair_blob(struct cifsSesInfo *ses) +build_avpair_blob(struct cifsSesInfo *ses, const struct nls_table *nls_cp) { + unsigned int dlen; + unsigned int wlen; + unsigned int size = 6 * sizeof(struct ntlmssp2_name); + __le64 curtime; + char *defdmname = "WORKGROUP"; + unsigned char *blobptr; struct ntlmssp2_name *attrptr; - ses->tilen = 2 * sizeof(struct ntlmssp2_name); + if (!ses->domainName) { + ses->domainName = kstrdup(defdmname, GFP_KERNEL); + if (!ses->domainName) + return -ENOMEM; + } + + dlen = strlen(ses->domainName); + wlen = strlen(ses->server->hostname); + + /* The length of this blob is a size which is + * six times the size of a structure which holds name/size + + * two times the unicode length of a domain name + + * two times the unicode length of a server name + + * size of a timestamp (which is 8 bytes). + */ + ses->tilen = size + 2 * (2 * dlen) + 2 * (2 * wlen) + 8; ses->tiblob = kzalloc(ses->tilen, GFP_KERNEL); if (!ses->tiblob) { ses->tilen = 0; cERROR(1, "Challenge target info allocation failure"); return -ENOMEM; } - attrptr = (struct ntlmssp2_name *) ses->tiblob; - attrptr->type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE); + + blobptr = ses->tiblob; + attrptr = (struct ntlmssp2_name *) blobptr; + + attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME); + attrptr->length = cpu_to_le16(2 * dlen); + blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); + cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp); + + blobptr += 2 * dlen; + attrptr = (struct ntlmssp2_name *) blobptr; + + attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_COMPUTER_NAME); + attrptr->length = cpu_to_le16(2 * wlen); + blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); + cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp); + + blobptr += 2 * wlen; + attrptr = (struct ntlmssp2_name *) blobptr; + + attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_DOMAIN_NAME); + attrptr->length = cpu_to_le16(2 * dlen); + blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); + cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp); + + blobptr += 2 * dlen; + attrptr = (struct ntlmssp2_name *) blobptr; + + attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_COMPUTER_NAME); + attrptr->length = cpu_to_le16(2 * wlen); + blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); + cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp); + + blobptr += 2 * wlen; + attrptr = (struct ntlmssp2_name *) blobptr; + + attrptr->type = cpu_to_le16(NTLMSSP_AV_TIMESTAMP); + attrptr->length = cpu_to_le16(sizeof(__le64)); + blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name); + curtime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); + memcpy(blobptr, &curtime, sizeof(__le64)); return 0; } @@ -429,7 +487,7 @@ setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, } } } else { - rc = build_avpair_blob(ses); + rc = build_avpair_blob(ses, nls_cp); if (rc) { cERROR(1, "error %d building av pair blob", rc); return rc; -- cgit v1.2.3-18-g5258 From 4a9410355406c31695eab9daeba694d7d9714e9b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Sep 2010 14:33:08 +0000 Subject: hfs: Convert tree_lock to mutex tree_lock is used as mutex so make it a mutex. Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Acked-by: Christoph Hellwig Cc: Al Viro LKML-Reference: <20100907125056.416332114@linutronix.de> --- fs/hfs/bfind.c | 4 ++-- fs/hfs/btree.c | 2 +- fs/hfs/btree.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c index 4129cdb3f0d..571abe97b42 100644 --- a/fs/hfs/bfind.c +++ b/fs/hfs/bfind.c @@ -23,7 +23,7 @@ int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) fd->search_key = ptr; fd->key = ptr + tree->max_key_len + 2; dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n", tree->cnid, __builtin_return_address(0)); - down(&tree->tree_lock); + mutex_lock(&tree->tree_lock); return 0; } @@ -32,7 +32,7 @@ void hfs_find_exit(struct hfs_find_data *fd) hfs_bnode_put(fd->bnode); kfree(fd->search_key); dprint(DBG_BNODE_REFS, "find_exit: %d (%p)\n", fd->tree->cnid, __builtin_return_address(0)); - up(&fd->tree->tree_lock); + mutex_unlock(&fd->tree->tree_lock); fd->tree = NULL; } diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c index 38a0a9917d7..3ebc437736f 100644 --- a/fs/hfs/btree.c +++ b/fs/hfs/btree.c @@ -27,7 +27,7 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp ke if (!tree) return NULL; - init_MUTEX(&tree->tree_lock); + mutex_init(&tree->tree_lock); spin_lock_init(&tree->hash_lock); /* Set the correct compare function */ tree->sb = sb; diff --git a/fs/hfs/btree.h b/fs/hfs/btree.h index cc51905ac21..2a1d712f85d 100644 --- a/fs/hfs/btree.h +++ b/fs/hfs/btree.h @@ -33,7 +33,7 @@ struct hfs_btree { unsigned int depth; //unsigned int map1_size, map_size; - struct semaphore tree_lock; + struct mutex tree_lock; unsigned int pages_per_bnode; spinlock_t hash_lock; -- cgit v1.2.3-18-g5258 From 756b0322e50aebc4b9afb4488a2d3f6c802b4e64 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Sep 2010 14:33:11 +0000 Subject: affs: Use sema_init instead of init_MUTEX Get rid of init_MUTE() and use sema_init() instead. Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: Christoph Hellwig Cc: Al Viro LKML-Reference: <20100907125056.511395595@linutronix.de> --- fs/affs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/affs/super.c b/fs/affs/super.c index 33c4e7eef47..9581ea94d5a 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -109,8 +109,8 @@ static void init_once(void *foo) { struct affs_inode_info *ei = (struct affs_inode_info *) foo; - init_MUTEX(&ei->i_link_lock); - init_MUTEX(&ei->i_ext_lock); + sema_init(&ei->i_link_lock, 1); + sema_init(&ei->i_ext_lock, 1); inode_init_once(&ei->vfs_inode); } -- cgit v1.2.3-18-g5258 From 1c456013e96f10915578dc61095d19b4906f64ac Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 12 Oct 2010 11:32:42 -0400 Subject: cifs: on multiuser mount, set ownership to current_fsuid/current_fsgid (try #7) commit 3aa1c8c2900065a51268430ab48a1b42fdfe5b45 made cifs_getattr set the ownership of files to current_fsuid/current_fsgid when multiuser mounts were in use and when mnt_uid/mnt_gid were non-zero. It should have instead based that decision on the CIFS_MOUNT_OVERR_UID/GID flags. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/inode.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f629159be4e..94979309698 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1755,7 +1755,7 @@ check_inval: } int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) + struct kstat *stat) { struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); @@ -1765,11 +1765,17 @@ int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, generic_fillattr(dentry->d_inode, stat); stat->blksize = CIFS_MAX_MSGSIZE; stat->ino = CIFS_I(dentry->d_inode)->uniqueid; + + /* + * If on a multiuser mount without unix extensions, and the + * admin hasn't overridden them, set the ownership to the + * fsuid/fsgid of the current process. + */ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && !tcon->unix_ext) { - if (!cifs_sb->mnt_uid) + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) stat->uid = current_fsuid(); - if (!cifs_sb->mnt_uid) + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) stat->gid = current_fsgid(); } } -- cgit v1.2.3-18-g5258 From a5e18bc36e9e05ce0338d370a2ce4290910e43ea Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 11 Oct 2010 15:07:18 -0400 Subject: cifs: keep dentry reference in cifsFileInfo instead of inode reference cifsFileInfo is a bit problematic. It contains a reference back to the struct file itself. This makes it difficult for a cifsFileInfo to exist without a corresponding struct file. It would be better instead of the cifsFileInfo just held info pertaining to the open file on the server instead without any back refrences to the struct file. This would allow it to exist after the filp to which it was originally attached was closed. Much of the use of the file pointer in this struct is to get at the dentry. Begin divorcing the cifsFileInfo from the struct file by keeping a reference to the dentry. Since the dentry will have a reference to the inode, we can eliminate the "pInode" field too and convert the igrab/iput to dget/dput. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Acked-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 4 ++-- fs/cifs/dir.c | 3 ++- fs/cifs/file.c | 2 +- fs/cifs/misc.c | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 4f85dfdf197..8289e61937a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -387,7 +387,7 @@ struct cifsFileInfo { /* BB add lock scope info here if needed */ ; /* lock scope id (0 if none) */ struct file *pfile; /* needed for writepage */ - struct inode *pInode; /* needed for oplock break */ + struct dentry *dentry; struct vfsmount *mnt; struct tcon_link *tlink; struct mutex lock_mutex; @@ -412,7 +412,7 @@ static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file) { if (atomic_dec_and_test(&cifs_file->count)) { cifs_put_tlink(cifs_file->tlink); - iput(cifs_file->pInode); + dput(cifs_file->dentry); kfree(cifs_file); } } diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index e249b561ce8..6887c412c61 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -135,6 +135,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, struct vfsmount *mnt, struct tcon_link *tlink, unsigned int oflags, __u32 oplock) { + struct dentry *dentry = file->f_path.dentry; struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; @@ -145,7 +146,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, pCifsFile->netfid = fileHandle; pCifsFile->pid = current->tgid; pCifsFile->uid = current_fsuid(); - pCifsFile->pInode = igrab(newinode); + pCifsFile->dentry = dget(dentry); pCifsFile->mnt = mnt; pCifsFile->pfile = file; pCifsFile->invalidHandle = false; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 80856f18071..c302b9c5264 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2335,7 +2335,7 @@ void cifs_oplock_break(struct work_struct *work) { struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, oplock_break); - struct inode *inode = cfile->pInode; + struct inode *inode = cfile->dentry->d_inode; struct cifsInodeInfo *cinode = CIFS_I(inode); int rc, waitrc = 0; diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 252f2768db8..9bac3e74b31 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -578,7 +578,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) } cFYI(1, "file id match, oplock break"); - pCifsInode = CIFS_I(netfile->pInode); + pCifsInode = CIFS_I(netfile->dentry->d_inode); pCifsInode->clientCanCacheAll = false; if (pSMB->OplockLevel == 0) pCifsInode->clientCanCacheRead = false; -- cgit v1.2.3-18-g5258 From d7c86ff8cd00abc730fe5d031f43dc9138b6324e Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 11 Oct 2010 15:07:19 -0400 Subject: cifs: don't use vfsmount to pin superblock for oplock breaks Filesystems aren't really supposed to do anything with a vfsmount. It's considered a layering violation since vfsmounts are entirely managed at the VFS layer. CIFS currently keeps an active reference to a vfsmount in order to prevent the superblock vanishing before an oplock break has completed. What we really want to do instead is to keep sb->s_active high until the oplock break has completed. This patch borrows the scheme that NFS uses for handling sillyrenames. An atomic_t is added to the cifs_sb_info. When it transitions from 0 to 1, an extra reference to the superblock is taken (by bumping the s_active value). When it transitions from 1 to 0, that reference is dropped and a the superblock teardown may proceed if there are no more references to it. Also, the vfsmount pointer is removed from cifsFileInfo and from cifs_new_fileinfo, and some bogus forward declarations are removed from cifsfs.h. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Acked-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/cifsfs.c | 18 ++++++++++++++++++ fs/cifs/cifsfs.h | 6 ++---- fs/cifs/cifsglob.h | 1 - fs/cifs/cifsproto.h | 2 +- fs/cifs/dir.c | 10 +++------- fs/cifs/file.c | 9 ++++----- 7 files changed, 29 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 586ee3d527d..525ba59a410 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -48,6 +48,7 @@ struct cifs_sb_info { struct nls_table *local_nls; unsigned int rsize; unsigned int wsize; + atomic_t active; uid_t mnt_uid; gid_t mnt_gid; mode_t mnt_file_mode; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 3258c822328..cbd468c880c 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -83,6 +83,24 @@ extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_mid_poolp; +void +cifs_sb_active(struct super_block *sb) +{ + struct cifs_sb_info *server = CIFS_SB(sb); + + if (atomic_inc_return(&server->active) == 1) + atomic_inc(&sb->s_active); +} + +void +cifs_sb_deactive(struct super_block *sb) +{ + struct cifs_sb_info *server = CIFS_SB(sb); + + if (atomic_dec_and_test(&server->active)) + deactivate_super(sb); +} + static int cifs_read_super(struct super_block *sb, void *data, const char *devname, int silent) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 786bdf36aeb..85544bcab51 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -42,10 +42,8 @@ extern const struct address_space_operations cifs_addr_ops; extern const struct address_space_operations cifs_addr_ops_smallbuf; /* Functions related to super block operations */ -/* extern const struct super_operations cifs_super_ops;*/ -extern void cifs_read_inode(struct inode *); -/*extern void cifs_delete_inode(struct inode *);*/ /* BB not needed yet */ -/* extern void cifs_write_inode(struct inode *); */ /* BB not needed yet */ +extern void cifs_sb_active(struct super_block *sb); +extern void cifs_sb_deactive(struct super_block *sb); /* Functions related to inodes */ extern const struct inode_operations cifs_dir_inode_ops; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 8289e61937a..e2b760ef22f 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -388,7 +388,6 @@ struct cifsFileInfo { /* lock scope id (0 if none) */ struct file *pfile; /* needed for writepage */ struct dentry *dentry; - struct vfsmount *mnt; struct tcon_link *tlink; struct mutex lock_mutex; struct list_head llist; /* list of byte range locks we have. */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 29a2ee8ae51..7f416abd34c 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -107,7 +107,7 @@ extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, - struct vfsmount *mnt, struct tcon_link *tlink, + struct tcon_link *tlink, unsigned int oflags, __u32 oplock); extern int cifs_posix_open(char *full_path, struct inode **pinode, struct super_block *sb, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 6887c412c61..c205ec9293e 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -132,8 +132,7 @@ cifs_bp_rename_retry: struct cifsFileInfo * cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, - struct vfsmount *mnt, struct tcon_link *tlink, - unsigned int oflags, __u32 oplock) + struct tcon_link *tlink, unsigned int oflags, __u32 oplock) { struct dentry *dentry = file->f_path.dentry; struct cifsFileInfo *pCifsFile; @@ -147,7 +146,6 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, pCifsFile->pid = current->tgid; pCifsFile->uid = current_fsuid(); pCifsFile->dentry = dget(dentry); - pCifsFile->mnt = mnt; pCifsFile->pfile = file; pCifsFile->invalidHandle = false; pCifsFile->closePend = false; @@ -485,8 +483,7 @@ cifs_create_set_dentry: } pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp, - nd->path.mnt, tlink, oflags, - oplock); + tlink, oflags, oplock); if (pfile_info == NULL) { fput(filp); CIFSSMBClose(xid, tcon, fileHandle); @@ -760,8 +757,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, } cfile = cifs_new_fileinfo(newInode, fileHandle, filp, - nd->path.mnt, tlink, - nd->intent.open.flags, + tlink, nd->intent.open.flags, oplock); if (cfile == NULL) { fput(filp); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index c302b9c5264..fd78a355f63 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -282,7 +282,6 @@ int cifs_open(struct inode *inode, struct file *file) } pCifsFile = cifs_new_fileinfo(inode, netfid, file, - file->f_path.mnt, tlink, oflags, oplock); if (pCifsFile == NULL) { CIFSSMBClose(xid, tcon, netfid); @@ -375,8 +374,8 @@ int cifs_open(struct inode *inode, struct file *file) if (rc != 0) goto out; - pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, - tlink, file->f_flags, oplock); + pCifsFile = cifs_new_fileinfo(inode, netfid, file, tlink, + file->f_flags, oplock); if (pCifsFile == NULL) { rc = -ENOMEM; goto out; @@ -2381,14 +2380,14 @@ void cifs_oplock_break(struct work_struct *work) void cifs_oplock_break_get(struct cifsFileInfo *cfile) { - mntget(cfile->mnt); + cifs_sb_active(cfile->dentry->d_sb); cifsFileInfo_get(cfile); } void cifs_oplock_break_put(struct cifsFileInfo *cfile) { - mntput(cfile->mnt); cifsFileInfo_put(cfile); + cifs_sb_deactive(cfile->dentry->d_sb); } const struct address_space_operations cifs_addr_ops = { -- cgit v1.2.3-18-g5258 From b1e86db1de2e8bc2be9fb94fae3451c2a776e8c1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 13 Oct 2010 14:46:17 -0400 Subject: nfsd: fix BUG at fs/nfsd/nfsfh.h:199 on unlink As of commit 43a9aa64a2f4330a9cb59aaf5c5636566bce067c "NFSD: Fill in WCC data for REMOVE, RMDIR, MKNOD, and MKDIR", we sometimes call fh_unlock on a filehandle that isn't fully initialized. We should fix up the callers, but as a quick fix it is also sufficient just to remove this assertion. Reported-by: Marius Tolzmann Cc: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsfh.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h index cdfb8c6a420..c16f8d8331b 100644 --- a/fs/nfsd/nfsfh.h +++ b/fs/nfsd/nfsfh.h @@ -196,8 +196,6 @@ fh_lock(struct svc_fh *fhp) static inline void fh_unlock(struct svc_fh *fhp) { - BUG_ON(!fhp->fh_dentry); - if (fhp->fh_locked) { fill_post_wcc(fhp); mutex_unlock(&fhp->fh_dentry->d_inode->i_mutex); -- cgit v1.2.3-18-g5258 From ee52716245877b821f5ddbb3ace85b73084fb450 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 14 Oct 2010 09:53:37 -0400 Subject: hfsplus: fix oops on mount with corrupted btree extent records A particular fsfuzzer run caused an hfs file system to crash on mount. This is due to a corrupted MDB extent record causing a miscalculation of HFSPLUS_I(inode)->first_blocks for the extent tree. If the extent records are zereod out, then it won't trigger the first_blocks special case and instead falls through to the extent code, which we're in the middle of initializing. This patch catches the 0 size extent records, reports the corruption, and fails the mount. [hch: ported of commit 47f365eb575735c6b2edf5d08e0d16d26a9c23bd from hfs] Reported-by: Ramon de Carvalho Valle Signed-off-by: Jeff Mahoney Signed-off-by: Christoph Hellwig --- fs/hfsplus/btree.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 8306479f6b3..d4bd864ce10 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -39,10 +39,16 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) goto free_tree; tree->inode = inode; + if (!HFSPLUS_I(tree->inode)->first_blocks) { + printk(KERN_ERR + "hfs: invalid btree extent records (0 size).\n"); + goto free_inode; + } + mapping = tree->inode->i_mapping; page = read_mapping_page(mapping, 0, NULL); if (IS_ERR(page)) - goto free_tree; + goto free_inode; /* Load the header */ head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc)); @@ -89,8 +95,9 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) fail_page: tree->inode->i_mapping->a_ops = &hfsplus_aops; page_cache_release(page); - free_tree: + free_inode: iput(tree->inode); + free_tree: kfree(tree); return NULL; } -- cgit v1.2.3-18-g5258 From b6b41424f0ec28e9a167fa29b003327860b4b71b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 14 Oct 2010 09:53:42 -0400 Subject: hfsplus: hfs_bnode_find() can fail, resulting in hfs_bnode_split() breakage oops and fs corruption; the latter can happen even on valid fs in case of oom. [hch: port of commit 3d10a15d6919488204bdb264050d156ced20d9aa from hfs] Signed-off-by: Al Viro Signed-off-by: Christoph Hellwig --- fs/hfsplus/brec.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c index c88e5d72a40..fa903641992 100644 --- a/fs/hfsplus/brec.c +++ b/fs/hfsplus/brec.c @@ -216,7 +216,7 @@ skip: static struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd) { struct hfs_btree *tree; - struct hfs_bnode *node, *new_node; + struct hfs_bnode *node, *new_node, *next_node; struct hfs_bnode_desc node_desc; int num_recs, new_rec_off, new_off, old_rec_off; int data_start, data_end, size; @@ -235,6 +235,17 @@ static struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd) new_node->type = node->type; new_node->height = node->height; + if (node->next) + next_node = hfs_bnode_find(tree, node->next); + else + next_node = NULL; + + if (IS_ERR(next_node)) { + hfs_bnode_put(node); + hfs_bnode_put(new_node); + return next_node; + } + size = tree->node_size / 2 - node->num_recs * 2 - 14; old_rec_off = tree->node_size - 4; num_recs = 1; @@ -248,6 +259,8 @@ static struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd) /* panic? */ hfs_bnode_put(node); hfs_bnode_put(new_node); + if (next_node) + hfs_bnode_put(next_node); return ERR_PTR(-ENOSPC); } @@ -302,8 +315,7 @@ static struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd) hfs_bnode_write(node, &node_desc, 0, sizeof(node_desc)); /* update next bnode header */ - if (new_node->next) { - struct hfs_bnode *next_node = hfs_bnode_find(tree, new_node->next); + if (next_node) { next_node->prev = new_node->this; hfs_bnode_read(next_node, &node_desc, 0, sizeof(node_desc)); node_desc.prev = cpu_to_be32(next_node->prev); -- cgit v1.2.3-18-g5258 From 9250f925972d03ccc0c0a4dd4e9b794d2ef6d52b Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 14 Oct 2010 09:53:48 -0400 Subject: hfsplus: handle more on-disk corruptions without oopsing hfs seems prone to bad things when it encounters on disk corruption. Many values are read from disk, and used as lengths to memcpy, as an example. This patch fixes up several of these problematic cases. o sanity check the on-disk maximum key lengths on mount (these are set to a defined value at mkfs time and shouldn't differ) o check on-disk node keylens against the maximum key length for each tree o fix hfs_btree_open so that going out via free_tree: doesn't wind up in hfs_releasepage, which wants to follow the very pointer we were trying to set up: HFS_SB(sb)->cat_tree = hfs_btree_open() . failure gets to hfs_releasepage and tries to follow HFS_SB(sb)->cat_tree Tested with the fsfuzzer; it survives more than it used to. [hch: ported of commit cf0594625083111ae522496dc1c256f7476939c2 from hfs] [hch: added the fixes from 5581d018ed3493d226e7a4d645d9c8a5af6c36b] Signed-off-by: Eric Sandeen Signed-off-by: Christoph Hellwig --- fs/hfsplus/bfind.c | 13 +++++++++++++ fs/hfsplus/brec.c | 15 +++++++++++++-- fs/hfsplus/btree.c | 25 ++++++++++++++++++++----- fs/hfsplus/hfsplus_raw.h | 3 ++- 4 files changed, 48 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c index 68c7983f028..d182438c7ae 100644 --- a/fs/hfsplus/bfind.c +++ b/fs/hfsplus/bfind.c @@ -52,6 +52,10 @@ int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd) rec = (e + b) / 2; len = hfs_brec_lenoff(bnode, rec, &off); keylen = hfs_brec_keylen(bnode, rec); + if (keylen == 0) { + res = -EINVAL; + goto fail; + } hfs_bnode_read(bnode, fd->key, off, keylen); cmpval = bnode->tree->keycmp(fd->key, fd->search_key); if (!cmpval) { @@ -67,6 +71,10 @@ int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd) if (rec != e && e >= 0) { len = hfs_brec_lenoff(bnode, e, &off); keylen = hfs_brec_keylen(bnode, e); + if (keylen == 0) { + res = -EINVAL; + goto fail; + } hfs_bnode_read(bnode, fd->key, off, keylen); } done: @@ -75,6 +83,7 @@ done: fd->keylength = keylen; fd->entryoffset = off + keylen; fd->entrylength = len - keylen; +fail: return res; } @@ -198,6 +207,10 @@ int hfs_brec_goto(struct hfs_find_data *fd, int cnt) len = hfs_brec_lenoff(bnode, fd->record, &off); keylen = hfs_brec_keylen(bnode, fd->record); + if (keylen == 0) { + res = -EINVAL; + goto out; + } fd->keyoffset = off; fd->keylength = keylen; fd->entryoffset = off + keylen; diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c index fa903641992..790cd6a8605 100644 --- a/fs/hfsplus/brec.c +++ b/fs/hfsplus/brec.c @@ -42,10 +42,21 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec) recoff = hfs_bnode_read_u16(node, node->tree->node_size - (rec + 1) * 2); if (!recoff) return 0; - if (node->tree->attributes & HFS_TREE_BIGKEYS) + if (node->tree->attributes & HFS_TREE_BIGKEYS) { retval = hfs_bnode_read_u16(node, recoff) + 2; - else + if (retval > node->tree->max_key_len + 2) { + printk(KERN_ERR "hfs: keylen %d too large\n", + retval); + retval = 0; + } + } else { retval = (hfs_bnode_read_u8(node, recoff) | 1) + 1; + if (retval > node->tree->max_key_len + 1) { + printk(KERN_ERR "hfs: keylen %d too large\n", + retval); + retval = 0; + } + } } return retval; } diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index d4bd864ce10..82caff215e0 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -63,10 +63,23 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) tree->max_key_len = be16_to_cpu(head->max_key_len); tree->depth = be16_to_cpu(head->depth); - /* Set the correct compare function */ - if (id == HFSPLUS_EXT_CNID) { + /* Verify the tree and set the correct compare function */ + switch (id) { + case HFSPLUS_EXT_CNID: + if (tree->max_key_len != HFSPLUS_EXT_KEYLEN - sizeof(u16)) { + printk(KERN_ERR "hfs: invalid extent max_key_len %d\n", + tree->max_key_len); + goto fail_page; + } tree->keycmp = hfsplus_ext_cmp_key; - } else if (id == HFSPLUS_CAT_CNID) { + break; + case HFSPLUS_CAT_CNID: + if (tree->max_key_len != HFSPLUS_CAT_KEYLEN - sizeof(u16)) { + printk(KERN_ERR "hfs: invalid catalog max_key_len %d\n", + tree->max_key_len); + goto fail_page; + } + if (test_bit(HFSPLUS_SB_HFSX, &HFSPLUS_SB(sb)->flags) && (head->key_type == HFSPLUS_KEY_BINARY)) tree->keycmp = hfsplus_cat_bin_cmp_key; @@ -74,7 +87,8 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) tree->keycmp = hfsplus_cat_case_cmp_key; set_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags); } - } else { + break; + default: printk(KERN_ERR "hfs: unknown B*Tree requested\n"); goto fail_page; } @@ -84,6 +98,7 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) goto fail_page; if (!tree->node_count) goto fail_page; + tree->node_size_shift = ffs(size) - 1; tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; @@ -93,9 +108,9 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) return tree; fail_page: - tree->inode->i_mapping->a_ops = &hfsplus_aops; page_cache_release(page); free_inode: + tree->inode->i_mapping->a_ops = &hfsplus_aops; iput(tree->inode); free_tree: kfree(tree); diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h index fe99fe8db61..6892899fd6f 100644 --- a/fs/hfsplus/hfsplus_raw.h +++ b/fs/hfsplus/hfsplus_raw.h @@ -200,6 +200,7 @@ struct hfsplus_cat_key { struct hfsplus_unistr name; } __packed; +#define HFSPLUS_CAT_KEYLEN (sizeof(struct hfsplus_cat_key)) /* Structs from hfs.h */ struct hfsp_point { @@ -323,7 +324,7 @@ struct hfsplus_ext_key { __be32 start_block; } __packed; -#define HFSPLUS_EXT_KEYLEN 12 +#define HFSPLUS_EXT_KEYLEN sizeof(struct hfsplus_ext_key) /* HFS+ generic BTree key */ typedef union { -- cgit v1.2.3-18-g5258 From 13571a6977f821fab7d9c3cc5f75da52b7732e40 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 14 Oct 2010 09:54:23 -0400 Subject: hfsplus: validate btree flags Signed-off-by: Christoph Hellwig --- fs/hfsplus/brec.c | 20 ++++++-------------- fs/hfsplus/btree.c | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c index 790cd6a8605..2f39d05443e 100644 --- a/fs/hfsplus/brec.c +++ b/fs/hfsplus/brec.c @@ -42,20 +42,12 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec) recoff = hfs_bnode_read_u16(node, node->tree->node_size - (rec + 1) * 2); if (!recoff) return 0; - if (node->tree->attributes & HFS_TREE_BIGKEYS) { - retval = hfs_bnode_read_u16(node, recoff) + 2; - if (retval > node->tree->max_key_len + 2) { - printk(KERN_ERR "hfs: keylen %d too large\n", - retval); - retval = 0; - } - } else { - retval = (hfs_bnode_read_u8(node, recoff) | 1) + 1; - if (retval > node->tree->max_key_len + 1) { - printk(KERN_ERR "hfs: keylen %d too large\n", - retval); - retval = 0; - } + + retval = hfs_bnode_read_u16(node, recoff) + 2; + if (retval > node->tree->max_key_len + 2) { + printk(KERN_ERR "hfs: keylen %d too large\n", + retval); + retval = 0; } } return retval; diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 82caff215e0..22e4d4e3299 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -71,6 +71,11 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) tree->max_key_len); goto fail_page; } + if (tree->attributes & HFS_TREE_VARIDXKEYS) { + printk(KERN_ERR "hfs: invalid extent btree flag\n"); + goto fail_page; + } + tree->keycmp = hfsplus_ext_cmp_key; break; case HFSPLUS_CAT_CNID: @@ -79,6 +84,10 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) tree->max_key_len); goto fail_page; } + if (!(tree->attributes & HFS_TREE_VARIDXKEYS)) { + printk(KERN_ERR "hfs: invalid catalog btree flag\n"); + goto fail_page; + } if (test_bit(HFSPLUS_SB_HFSX, &HFSPLUS_SB(sb)->flags) && (head->key_type == HFSPLUS_KEY_BINARY)) @@ -93,6 +102,11 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) goto fail_page; } + if (!(tree->attributes & HFS_TREE_BIGKEYS)) { + printk(KERN_ERR "hfs: invalid btree flag\n"); + goto fail_page; + } + size = tree->node_size; if (!is_power_of_2(size)) goto fail_page; -- cgit v1.2.3-18-g5258 From f6089ff87d309a8ddb7b0d4dd92a570f1b0f689b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 14 Oct 2010 09:54:28 -0400 Subject: hfsplus: fix link corruption HFS implements hardlink by using indirect catalog entries that refer to a hidden directly. The link target is cached in the dev field in the HFS+ specific inode, which is also used for the device number for device files, and inside for passing the nlink value of the indirect node from hfsplus_cat_write_inode to a helper function. Now if we happen to write out the indirect node while hfsplus_link is creating the catalog entry we'll get a link pointing to the linkid of the current nlink value. This can easily be reproduced by a large enough loop of local git-clone operations. Stop abusing the dev field in the HFS+ inode for short term storage by refactoring the way the permission structure in the catalog entry is set up, and rename the dev field to linkid to avoid any confusion. While we're at it also prevent creating hard links to special files, as the HFS+ dev and linkid share the same space in the on-disk structure. Signed-off-by: Christoph Hellwig --- fs/hfsplus/catalog.c | 2 +- fs/hfsplus/dir.c | 6 ++++-- fs/hfsplus/hfsplus_fs.h | 8 +++++--- fs/hfsplus/inode.c | 14 ++++++++------ 4 files changed, 18 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 48979c4e8fa..9d1594b0a07 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -134,7 +134,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR); file->user_info.fdFlags = cpu_to_be16(0x100); file->create_date = HFSPLUS_I(sbi->hidden_dir)->create_date; - file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode)->dev); + file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode)->linkid); } return sizeof(*file); } diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 33aab211695..c05c8776e83 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -102,7 +102,7 @@ again: if (IS_ERR(inode)) return ERR_CAST(inode); if (S_ISREG(inode->i_mode)) - HFSPLUS_I(inode)->dev = linkid; + HFSPLUS_I(inode)->linkid = linkid; out: d_add(dentry, inode); return NULL; @@ -252,6 +252,8 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, if (HFSPLUS_IS_RSRC(inode)) return -EPERM; + if (!S_ISREG(inode->i_mode)) + return -EPERM; mutex_lock(&sbi->vh_mutex); if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { @@ -268,7 +270,7 @@ static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, if (res != -EEXIST) goto out; } - HFSPLUS_I(inode)->dev = id; + HFSPLUS_I(inode)->linkid = id; cnid = sbi->next_cnid++; src_dentry->d_fsdata = (void *)(unsigned long)cnid; res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 5cda96366ac..d92f590d663 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -178,7 +178,11 @@ struct hfsplus_inode_info { */ struct inode *rsrc_inode; __be32 create_date; - u32 dev; + + /* + * Protected by sbi->vh_mutex. + */ + u32 linkid; /* * Protected by i_mutex. @@ -427,6 +431,4 @@ static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode) #define hfsp_ut2mt(t) __hfsp_ut2mt((t).tv_sec) #define hfsp_now2mt() __hfsp_ut2mt(get_seconds()) -#define kdev_t_to_nr(x) (x) - #endif diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index a05b3afa723..746e0ee2071 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -267,7 +267,13 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) perms->mode = cpu_to_be16(inode->i_mode); perms->owner = cpu_to_be32(inode->i_uid); perms->group = cpu_to_be32(inode->i_gid); - perms->dev = cpu_to_be32(HFSPLUS_I(inode)->dev); + + if (S_ISREG(inode->i_mode)) + perms->dev = cpu_to_be32(inode->i_nlink); + else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) + perms->dev = cpu_to_be32(inode->i_rdev); + else + perms->dev = 0; } static int hfsplus_file_open(struct inode *inode, struct file *file) @@ -491,7 +497,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd) type = hfs_bnode_read_u16(fd->bnode, fd->entryoffset); - HFSPLUS_I(inode)->dev = 0; + HFSPLUS_I(inode)->linkid = 0; if (type == HFSPLUS_FOLDER) { struct hfsplus_cat_folder *folder = &entry.folder; @@ -595,10 +601,6 @@ int hfsplus_cat_write_inode(struct inode *inode) hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, sizeof(struct hfsplus_cat_file)); hfsplus_inode_write_fork(inode, &file->data_fork); - if (S_ISREG(inode->i_mode)) - HFSPLUS_I(inode)->dev = inode->i_nlink; - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - HFSPLUS_I(inode)->dev = kdev_t_to_nr(inode->i_rdev); hfsplus_set_perms(inode, &file->permissions); if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE) file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED); -- cgit v1.2.3-18-g5258 From 722c55d13e7296cc62ed8a38f926a915ff32e4ea Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 14 Oct 2010 09:54:33 -0400 Subject: hfsplus: remove superflous rootflags field in hfsplus_inode_info The rootflags field in hfsplus_inode_info only caches the immutable and append-only flags in the VFS inode, so we can easily get rid of it. Signed-off-by: Christoph Hellwig --- fs/hfsplus/catalog.c | 1 - fs/hfsplus/hfsplus_fs.h | 2 +- fs/hfsplus/inode.c | 1 - fs/hfsplus/ioctl.c | 25 +++++++++++-------------- 4 files changed, 12 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 9d1594b0a07..bbcf5a27b5d 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -77,7 +77,6 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) perms->rootflags |= HFSPLUS_FLG_APPEND; else perms->rootflags &= ~HFSPLUS_FLG_APPEND; - HFSPLUS_I(inode)->rootflags = perms->rootflags; HFSPLUS_I(inode)->userflags = perms->userflags; perms->mode = cpu_to_be16(inode->i_mode); perms->owner = cpu_to_be32(inode->i_uid); diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index d92f590d663..0d77844a00e 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -188,7 +188,7 @@ struct hfsplus_inode_info { * Protected by i_mutex. */ sector_t fs_blocks; - u8 rootflags, userflags; /* BSD system and user file flags */ + u8 userflags; /* BSD user file flags */ struct list_head open_dir_list; loff_t phys_size; diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 746e0ee2071..df57ea1f3a7 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -241,7 +241,6 @@ static void hfsplus_get_perms(struct inode *inode, struct hfsplus_perm *perms, i mode = S_IFREG | ((S_IRUGO|S_IWUGO) & ~(sbi->umask)); inode->i_mode = mode; - HFSPLUS_I(inode)->rootflags = perms->rootflags; HFSPLUS_I(inode)->userflags = perms->userflags; if (perms->rootflags & HFSPLUS_FLG_IMMUTABLE) inode->i_flags |= S_IMMUTABLE; diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c index c9ac443d202..80eb5b3a5ed 100644 --- a/fs/hfsplus/ioctl.c +++ b/fs/hfsplus/ioctl.c @@ -26,9 +26,9 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags) struct hfsplus_inode_info *hip = HFSPLUS_I(inode); unsigned int flags = 0; - if (hip->rootflags & HFSPLUS_FLG_IMMUTABLE) + if (inode->i_flags & S_IMMUTABLE) flags |= FS_IMMUTABLE_FL; - if (hip->rootflags & HFSPLUS_FLG_APPEND) + if (inode->i_flags |= S_APPEND) flags |= FS_APPEND_FL; if (hip->userflags & HFSPLUS_FLG_NODUMP) flags |= FS_NODUMP_FL; @@ -59,8 +59,8 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) mutex_lock(&inode->i_mutex); - if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) || - hip->rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) { + if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) || + inode->i_flags & (S_IMMUTABLE|S_APPEND)) { if (!capable(CAP_LINUX_IMMUTABLE)) { err = -EPERM; goto out_unlock_inode; @@ -72,20 +72,17 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags) err = -EOPNOTSUPP; goto out_unlock_inode; } - if (flags & FS_IMMUTABLE_FL) { + + if (flags & FS_IMMUTABLE_FL) inode->i_flags |= S_IMMUTABLE; - hip->rootflags |= HFSPLUS_FLG_IMMUTABLE; - } else { + else inode->i_flags &= ~S_IMMUTABLE; - hip->rootflags &= ~HFSPLUS_FLG_IMMUTABLE; - } - if (flags & FS_APPEND_FL) { + + if (flags & FS_APPEND_FL) inode->i_flags |= S_APPEND; - hip->rootflags |= HFSPLUS_FLG_APPEND; - } else { + else inode->i_flags &= ~S_APPEND; - hip->rootflags &= ~HFSPLUS_FLG_APPEND; - } + if (flags & FS_NODUMP_FL) hip->userflags |= HFSPLUS_FLG_NODUMP; else -- cgit v1.2.3-18-g5258 From 90e616905a423126805186cb5754e10a704b30c8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 14 Oct 2010 09:54:39 -0400 Subject: hfsplus: create correct initial catalog entries for device files Make sure the initial insertation of the catalog entry already contains the device number by calling init_special_inode early and setting writing out the dev field of the on-disk permission structure. The latter is facilitated by sharing the almost identical hfsplus_set_perms helpers between initial catalog entry creating and ->write_inode. Unless we crashed just after mknod this bug was harmless as the inode is marked dirty at the end of hfsplus_mknod, and hfsplus_write_inode will update the catalog entry to contain the correct value. Signed-off-by: Christoph Hellwig --- fs/hfsplus/catalog.c | 16 ++++++++++++---- fs/hfsplus/dir.c | 6 +++--- fs/hfsplus/hfsplus_fs.h | 1 + fs/hfsplus/inode.c | 27 ++------------------------- 4 files changed, 18 insertions(+), 32 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index bbcf5a27b5d..8af45fc5b05 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -67,7 +67,7 @@ static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent, key->key_len = cpu_to_be16(6 + ustrlen); } -static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) +void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms) { if (inode->i_flags & S_IMMUTABLE) perms->rootflags |= HFSPLUS_FLG_IMMUTABLE; @@ -77,10 +77,18 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) perms->rootflags |= HFSPLUS_FLG_APPEND; else perms->rootflags &= ~HFSPLUS_FLG_APPEND; - HFSPLUS_I(inode)->userflags = perms->userflags; + + perms->userflags = HFSPLUS_I(inode)->userflags; perms->mode = cpu_to_be16(inode->i_mode); perms->owner = cpu_to_be32(inode->i_uid); perms->group = cpu_to_be32(inode->i_gid); + + if (S_ISREG(inode->i_mode)) + perms->dev = cpu_to_be32(inode->i_nlink); + else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) + perms->dev = cpu_to_be32(inode->i_rdev); + else + perms->dev = 0; } static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct inode *inode) @@ -99,7 +107,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i folder->content_mod_date = folder->attribute_mod_date = folder->access_date = hfsp_now2mt(); - hfsplus_set_perms(inode, &folder->permissions); + hfsplus_cat_set_perms(inode, &folder->permissions); if (inode == sbi->hidden_dir) /* invisible and namelocked */ folder->user_info.frFlags = cpu_to_be16(0x5000); @@ -118,7 +126,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i file->attribute_mod_date = file->access_date = hfsp_now2mt(); if (cnid == inode->i_ino) { - hfsplus_set_perms(inode, &file->permissions); + hfsplus_cat_set_perms(inode, &file->permissions); if (S_ISLNK(inode->i_mode)) { file->user_info.fdType = cpu_to_be32(HFSP_SYMLINK_TYPE); file->user_info.fdCreator = cpu_to_be32(HFSP_SYMLINK_CREATOR); diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index c05c8776e83..d236d85ec9d 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -418,6 +418,9 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, if (!inode) goto out; + if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) + init_special_inode(inode, mode, rdev); + res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (res) { inode->i_nlink = 0; @@ -426,9 +429,6 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, goto out; } - if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) - init_special_inode(inode, mode, rdev); - hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); out: diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 0d77844a00e..d77dfd22ea0 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -326,6 +326,7 @@ int hfsplus_create_cat(u32, struct inode *, struct qstr *, struct inode *); int hfsplus_delete_cat(u32, struct inode *, struct qstr *); int hfsplus_rename_cat(u32, struct inode *, struct qstr *, struct inode *, struct qstr *); +void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms); /* dir.c */ extern const struct inode_operations hfsplus_dir_inode_operations; diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index df57ea1f3a7..78449280dae 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -252,29 +252,6 @@ static void hfsplus_get_perms(struct inode *inode, struct hfsplus_perm *perms, i inode->i_flags &= ~S_APPEND; } -static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) -{ - if (inode->i_flags & S_IMMUTABLE) - perms->rootflags |= HFSPLUS_FLG_IMMUTABLE; - else - perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE; - if (inode->i_flags & S_APPEND) - perms->rootflags |= HFSPLUS_FLG_APPEND; - else - perms->rootflags &= ~HFSPLUS_FLG_APPEND; - perms->userflags = HFSPLUS_I(inode)->userflags; - perms->mode = cpu_to_be16(inode->i_mode); - perms->owner = cpu_to_be32(inode->i_uid); - perms->group = cpu_to_be32(inode->i_gid); - - if (S_ISREG(inode->i_mode)) - perms->dev = cpu_to_be32(inode->i_nlink); - else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) - perms->dev = cpu_to_be32(inode->i_rdev); - else - perms->dev = 0; -} - static int hfsplus_file_open(struct inode *inode, struct file *file) { if (HFSPLUS_IS_RSRC(inode)) @@ -578,7 +555,7 @@ int hfsplus_cat_write_inode(struct inode *inode) hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, sizeof(struct hfsplus_cat_folder)); /* simple node checks? */ - hfsplus_set_perms(inode, &folder->permissions); + hfsplus_cat_set_perms(inode, &folder->permissions); folder->access_date = hfsp_ut2mt(inode->i_atime); folder->content_mod_date = hfsp_ut2mt(inode->i_mtime); folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime); @@ -600,7 +577,7 @@ int hfsplus_cat_write_inode(struct inode *inode) hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, sizeof(struct hfsplus_cat_file)); hfsplus_inode_write_fork(inode, &file->data_fork); - hfsplus_set_perms(inode, &file->permissions); + hfsplus_cat_set_perms(inode, &file->permissions); if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE) file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED); else -- cgit v1.2.3-18-g5258 From 32e39e19ccb0f25c9c1b7ff246e17e795366bbbd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 14 Oct 2010 09:54:43 -0400 Subject: hfsplus: remove the unused hfsplus_kmap/hfsplus_kunmap helpers Signed-off-by: Christoph Hellwig --- fs/hfsplus/hfsplus_fs.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'fs') diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index d77dfd22ea0..cb3653efb57 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -398,14 +398,6 @@ static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode) return list_entry(inode, struct hfsplus_inode_info, vfs_inode); } -#if 1 -#define hfsplus_kmap(p) ({ struct page *__p = (p); kmap(__p); }) -#define hfsplus_kunmap(p) ({ struct page *__p = (p); kunmap(__p); __p; }) -#else -#define hfsplus_kmap(p) kmap(p) -#define hfsplus_kunmap(p) kunmap(p) -#endif - #define sb_bread512(sb, sec, data) ({ \ struct buffer_head *__bh; \ sector_t __block; \ -- cgit v1.2.3-18-g5258 From 0eead9ab41da33644ae2c97c57ad03da636a0422 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 14 Oct 2010 10:57:40 -0700 Subject: Don't dump task struct in a.out core-dumps akiphie points out that a.out core-dumps have that odd task struct dumping that was never used and was never really a good idea (it goes back into the mists of history, probably the original core-dumping code). Just remove it. Also do the access_ok() check on dump_write(). It probably doesn't matter (since normal filesystems all seem to do it anyway), but he points out that it's normally done by the VFS layer, so ... [ I suspect that we should possibly do "vfs_write()" instead of calling ->write directly. That also does the whole fsnotify and write statistics thing, which may or may not be a good idea. ] And just to be anal, do this all for the x86-64 32-bit a.out emulation code too, even though it's not enabled (and won't currently even compile) Reported-by: akiphie Signed-off-by: Linus Torvalds --- fs/binfmt_aout.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'fs') diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index f96eff04e11..a6395bdb26a 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -134,10 +134,6 @@ static int aout_core_dump(struct coredump_params *cprm) if (!dump_write(file, dump_start, dump_size)) goto end_coredump; } -/* Finally dump the task struct. Not be used by gdb, but could be useful */ - set_fs(KERNEL_DS); - if (!dump_write(file, current, sizeof(*current))) - goto end_coredump; end_coredump: set_fs(fs); return has_dumped; -- cgit v1.2.3-18-g5258 From 5d0d28824c76409f0d1a645bf0ae81318c8ffa42 Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Wed, 13 Oct 2010 18:15:00 -0500 Subject: NTLM authentication and signing - Calculate auth response per smb session Start calculation auth response within a session. Move/Add pertinet data structures like session key, server challenge and ntlmv2_hash in a session structure. We should do the calculations within a session before copying session key and response over to server data structures because a session setup can fail. Only after a very first smb session succeeds, it copies/makes its session key, session key of smb connection. This key stays with the smb connection throughout its life. Signed-off-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 16 ++++++++-------- fs/cifs/cifsglob.h | 6 ++++-- fs/cifs/cifssmb.c | 4 ++-- fs/cifs/connect.c | 18 ++++++++++++++---- fs/cifs/sess.c | 28 ++++++++-------------------- 5 files changed, 36 insertions(+), 36 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index e3edd8a6840..7ac0056294c 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -458,7 +458,7 @@ calc_exit_1: calc_exit_2: /* BB FIXME what about bytes 24 through 40 of the signing key? compare with the NTLM example */ - hmac_md5_final(ses->server->ntlmv2_hash, pctxt); + hmac_md5_final(ses->ntlmv2_hash, pctxt); kfree(pctxt); return rc; @@ -502,14 +502,14 @@ setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, } CalcNTLMv2_response(ses, resp_buf); - /* now calculate the MAC key for NTLMv2 */ - hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); + /* now calculate the session key for NTLMv2 */ + hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context); hmac_md5_update(resp_buf, 16, &context); - hmac_md5_final(ses->server->session_key.data.ntlmv2.key, &context); + hmac_md5_final(ses->auth_key.data.ntlmv2.key, &context); - memcpy(&ses->server->session_key.data.ntlmv2.resp, resp_buf, + memcpy(&ses->auth_key.data.ntlmv2.resp, resp_buf, sizeof(struct ntlmv2_resp)); - ses->server->session_key.len = 16 + sizeof(struct ntlmv2_resp); + ses->auth_key.len = 16 + sizeof(struct ntlmv2_resp); return 0; @@ -526,8 +526,8 @@ void CalcNTLMv2_response(const struct cifsSesInfo *ses, { struct HMACMD5Context context; /* rest of v2 struct already generated */ - memcpy(v2_session_response + 8, ses->server->cryptKey, 8); - hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); + memcpy(v2_session_response + 8, ses->cryptKey, 8); + hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context); hmac_md5_update(v2_session_response+8, sizeof(struct ntlmv2_resp) - 8, &context); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index e2b760ef22f..6c69bd76249 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -179,12 +179,10 @@ struct TCP_Server_Info { int capabilities; /* allow selective disabling of caps by smb sess */ int timeAdj; /* Adjust for difference in server time zone in sec */ __u16 CurrentMid; /* multiplex id - rotating counter */ - char cryptKey[CIFS_CRYPTO_KEY_SIZE]; /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* needed for CIFS PDU signature */ struct session_key session_key; - char ntlmv2_hash[16]; unsigned long lstrp; /* when we got last response from this server */ u16 dialect; /* dialect index that server chose */ /* extended security flavors that server supports */ @@ -192,6 +190,7 @@ struct TCP_Server_Info { bool sec_mskerberos; /* supports legacy MS Kerberos */ bool sec_kerberosu2u; /* supports U2U Kerberos */ bool sec_ntlmssp; /* supports NTLMSSP */ + bool session_estab; /* mark when very first sess is established */ #ifdef CONFIG_CIFS_FSCACHE struct fscache_cookie *fscache; /* client index cache cookie */ #endif @@ -223,6 +222,9 @@ struct cifsSesInfo { char userName[MAX_USERNAME_SIZE + 1]; char *domainName; char *password; + char cryptKey[CIFS_CRYPTO_KEY_SIZE]; + struct session_key auth_key; + char ntlmv2_hash[16]; unsigned int tilen; /* length of the target info blob */ unsigned char *tiblob; /* target info blob in challenge response */ bool need_reconnect:1; /* connection reset, uid now invalid */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 54bd83af772..a420c7b0852 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -503,7 +503,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) if (rsp->EncryptionKeyLength == cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { - memcpy(server->cryptKey, rsp->EncryptionKey, + memcpy(ses->cryptKey, rsp->EncryptionKey, CIFS_CRYPTO_KEY_SIZE); } else if (server->secMode & SECMODE_PW_ENCRYPT) { rc = -EIO; /* need cryptkey unless plain text */ @@ -574,7 +574,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); server->timeAdj *= 60; if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { - memcpy(server->cryptKey, pSMBr->u.EncryptionKey, + memcpy(ses->cryptKey, pSMBr->u.EncryptionKey, CIFS_CRYPTO_KEY_SIZE); } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && (pSMBr->EncryptionKeyLength == 0)) { diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 4944fc84d5e..019f00380d1 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -173,6 +173,8 @@ cifs_reconnect(struct TCP_Server_Info *server) sock_release(server->ssocket); server->ssocket = NULL; } + server->sequence_number = 0; + server->session_estab = false; spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { @@ -205,7 +207,6 @@ cifs_reconnect(struct TCP_Server_Info *server) spin_lock(&GlobalMid_Lock); if (server->tcpStatus != CifsExiting) server->tcpStatus = CifsGood; - server->sequence_number = 0; spin_unlock(&GlobalMid_Lock); /* atomic_set(&server->inFlight,0);*/ wake_up(&server->response_q); @@ -1631,6 +1632,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); memcpy(tcp_ses->server_RFC1001_name, volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); + tcp_ses->session_estab = false; tcp_ses->sequence_number = 0; INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list); @@ -2983,14 +2985,13 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, #ifdef CONFIG_CIFS_WEAK_PW_HASH if ((global_secflags & CIFSSEC_MAY_LANMAN) && (ses->server->secType == LANMAN)) - calc_lanman_hash(tcon->password, ses->server->cryptKey, + calc_lanman_hash(tcon->password, ses->cryptKey, ses->server->secMode & SECMODE_PW_ENCRYPT ? true : false, bcc_ptr); else #endif /* CIFS_WEAK_PW_HASH */ - SMBNTencrypt(tcon->password, ses->server->cryptKey, - bcc_ptr); + SMBNTencrypt(tcon->password, ses->cryptKey, bcc_ptr); bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { @@ -3175,6 +3176,15 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, if (rc) { cERROR(1, "Send error in SessSetup = %d", rc); } else { + mutex_lock(&ses->server->srv_mutex); + if (!server->session_estab) { + memcpy(&server->session_key.data, + &ses->auth_key.data, ses->auth_key.len); + server->session_key.len = ses->auth_key.len; + ses->server->session_estab = true; + } + mutex_unlock(&server->srv_mutex); + cFYI(1, "CIFS Session Established successfully"); spin_lock(&GlobalMid_Lock); ses->status = CifsGood; diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index c926e6c7c0c..2111bed71b1 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -402,7 +402,7 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, return -EINVAL; } - memcpy(ses->server->cryptKey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); + memcpy(ses->cryptKey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); /* BB we could decode pblob->NegotiateFlags; some may be useful */ /* In particular we can examine sign flags */ /* BB spec says that if AvId field of MsvAvTimestamp is populated then @@ -591,17 +591,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int bytes_remaining; struct key *spnego_key = NULL; __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ - bool first_time; int blob_len; char *ntlmsspblob = NULL; if (ses == NULL) return -EINVAL; - read_lock(&cifs_tcp_ses_lock); - first_time = is_first_ses_reconnect(ses); - read_unlock(&cifs_tcp_ses_lock); - type = ses->server->secType; cFYI(1, "sess setup type %d", type); @@ -672,7 +667,7 @@ ssetup_ntlmssp_authenticate: /* BB calculate hash with password */ /* and copy into bcc */ - calc_lanman_hash(ses->password, ses->server->cryptKey, + calc_lanman_hash(ses->password, ses->cryptKey, ses->server->secMode & SECMODE_PW_ENCRYPT ? true : false, lnm_session_key); @@ -699,15 +694,11 @@ ssetup_ntlmssp_authenticate: cpu_to_le16(CIFS_SESS_KEY_SIZE); /* calculate session key */ - SMBNTencrypt(ses->password, ses->server->cryptKey, - ntlm_session_key); + SMBNTencrypt(ses->password, ses->cryptKey, ntlm_session_key); - if (first_time) /* should this be moved into common code - with similar ntlmv2 path? */ - cifs_calculate_session_key(&ses->server->session_key, - ntlm_session_key, ses->password); + cifs_calculate_session_key(&ses->auth_key, + ntlm_session_key, ses->password); /* copy session key */ - memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); bcc_ptr += CIFS_SESS_KEY_SIZE; memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); @@ -794,17 +785,14 @@ ssetup_ntlmssp_authenticate: } /* bail out if key is too long */ if (msg->sesskey_len > - sizeof(ses->server->session_key.data.krb5)) { + sizeof(ses->auth_key.data.krb5)) { cERROR(1, "Kerberos signing key too long (%u bytes)", msg->sesskey_len); rc = -EOVERFLOW; goto ssetup_exit; } - if (first_time) { - ses->server->session_key.len = msg->sesskey_len; - memcpy(ses->server->session_key.data.krb5, - msg->data, msg->sesskey_len); - } + ses->auth_key.len = msg->sesskey_len; + memcpy(ses->auth_key.data.krb5, msg->data, msg->sesskey_len); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; capabilities |= CAP_EXTENDED_SECURITY; pSMB->req.Capabilities = cpu_to_le32(capabilities); -- cgit v1.2.3-18-g5258 From 3aa0ce825ade0cf5506e32ccf51d01fc8d22a9cf Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 14 Oct 2010 14:32:06 -0700 Subject: Un-inline the core-dump helper functions Tony Luck reports that the addition of the access_ok() check in commit 0eead9ab41da ("Don't dump task struct in a.out core-dumps") broke the ia64 compile due to missing the necessary header file includes. Rather than add yet another include () to make everything happy, just uninline the silly core dump helper functions and move the bodies to fs/exec.c where they make a lot more sense. dump_seek() in particular was too big to be an inline function anyway, and none of them are in any way performance-critical. And we really don't need to mess up our include file headers more than they already are. Reported-and-tested-by: Tony Luck Signed-off-by: Linus Torvalds --- fs/exec.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'fs') diff --git a/fs/exec.c b/fs/exec.c index 828dd2461d6..03278c984ba 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2014,3 +2014,41 @@ fail_creds: fail: return; } + +/* + * Core dumping helper functions. These are the only things you should + * do on a core-file: use only these functions to write out all the + * necessary info. + */ +int dump_write(struct file *file, const void *addr, int nr) +{ + return access_ok(VERIFY_READ, addr, nr) && file->f_op->write(file, addr, nr, &file->f_pos) == nr; +} + +int dump_seek(struct file *file, loff_t off) +{ + int ret = 1; + + if (file->f_op->llseek && file->f_op->llseek != no_llseek) { + if (file->f_op->llseek(file, off, SEEK_CUR) < 0) + return 0; + } else { + char *buf = (char *)get_zeroed_page(GFP_KERNEL); + + if (!buf) + return 0; + while (off > 0) { + unsigned long n = off; + + if (n > PAGE_SIZE) + n = PAGE_SIZE; + if (!dump_write(file, buf, n)) { + ret = 0; + break; + } + off -= n; + } + free_page((unsigned long)buf); + } + return ret; +} -- cgit v1.2.3-18-g5258 From 8fd01d6cfbf75465d84a4e533ed70c5f57b3ff51 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 14 Oct 2010 19:15:28 -0700 Subject: Export dump_{write,seek} to binary loader modules If you build aout support as a module, you'll want these exported. Reported-by: Tetsuo Handa Signed-off-by: Linus Torvalds --- fs/exec.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/exec.c b/fs/exec.c index 03278c984ba..6d2b6f93685 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2024,6 +2024,7 @@ int dump_write(struct file *file, const void *addr, int nr) { return access_ok(VERIFY_READ, addr, nr) && file->f_op->write(file, addr, nr, &file->f_pos) == nr; } +EXPORT_SYMBOL(dump_write); int dump_seek(struct file *file, loff_t off) { @@ -2052,3 +2053,4 @@ int dump_seek(struct file *file, loff_t off) } return ret; } +EXPORT_SYMBOL(dump_seek); -- cgit v1.2.3-18-g5258 From 46bf36ecec79bbe5373ef68f0ed36cbf62e03482 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 15 Oct 2010 05:45:00 -0700 Subject: hfsplus: fix getxattr return value We need to support -EOPNOTSUPP for attributes that are not supported to match other filesystems and allow userspace to detect if Posix ACLs are supported or not. setxattr already gets this right. Signed-off-by: Christoph Hellwig --- fs/hfsplus/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c index 80eb5b3a5ed..5b4667e08ef 100644 --- a/fs/hfsplus/ioctl.c +++ b/fs/hfsplus/ioctl.c @@ -192,7 +192,7 @@ ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name, } else res = size ? -ERANGE : 4; } else - res = -ENODATA; + res = -EOPNOTSUPP; out: if (size) hfs_find_exit(&fd); -- cgit v1.2.3-18-g5258 From 6038f373a3dc1f1c26496e60b6c40b164716f07e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 15 Aug 2010 18:52:59 +0200 Subject: llseek: automatically add .llseek fop All file_operations should get a .llseek operation so we can make nonseekable_open the default for future file operations without a .llseek pointer. The three cases that we can automatically detect are no_llseek, seq_lseek and default_llseek. For cases where we can we can automatically prove that the file offset is always ignored, we use noop_llseek, which maintains the current behavior of not returning an error from a seek. New drivers should normally not use noop_llseek but instead use no_llseek and call nonseekable_open at open time. Existing drivers can be converted to do the same when the maintainer knows for certain that no user code relies on calling seek on the device file. The generated code is often incorrectly indented and right now contains comments that clarify for each added line why a specific variant was chosen. In the version that gets submitted upstream, the comments will be gone and I will manually fix the indentation, because there does not seem to be a way to do that using coccinelle. Some amount of new code is currently sitting in linux-next that should get the same modifications, which I will do at the end of the merge window. Many thanks to Julia Lawall for helping me learn to write a semantic patch that does all this. ===== begin semantic patch ===== // This adds an llseek= method to all file operations, // as a preparation for making no_llseek the default. // // The rules are // - use no_llseek explicitly if we do nonseekable_open // - use seq_lseek for sequential files // - use default_llseek if we know we access f_pos // - use noop_llseek if we know we don't access f_pos, // but we still want to allow users to call lseek // @ open1 exists @ identifier nested_open; @@ nested_open(...) { <+... nonseekable_open(...) ...+> } @ open exists@ identifier open_f; identifier i, f; identifier open1.nested_open; @@ int open_f(struct inode *i, struct file *f) { <+... ( nonseekable_open(...) | nested_open(...) ) ...+> } @ read disable optional_qualifier exists @ identifier read_f; identifier f, p, s, off; type ssize_t, size_t, loff_t; expression E; identifier func; @@ ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off) { <+... ( *off = E | *off += E | func(..., off, ...) | E = *off ) ...+> } @ read_no_fpos disable optional_qualifier exists @ identifier read_f; identifier f, p, s, off; type ssize_t, size_t, loff_t; @@ ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off) { ... when != off } @ write @ identifier write_f; identifier f, p, s, off; type ssize_t, size_t, loff_t; expression E; identifier func; @@ ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off) { <+... ( *off = E | *off += E | func(..., off, ...) | E = *off ) ...+> } @ write_no_fpos @ identifier write_f; identifier f, p, s, off; type ssize_t, size_t, loff_t; @@ ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off) { ... when != off } @ fops0 @ identifier fops; @@ struct file_operations fops = { ... }; @ has_llseek depends on fops0 @ identifier fops0.fops; identifier llseek_f; @@ struct file_operations fops = { ... .llseek = llseek_f, ... }; @ has_read depends on fops0 @ identifier fops0.fops; identifier read_f; @@ struct file_operations fops = { ... .read = read_f, ... }; @ has_write depends on fops0 @ identifier fops0.fops; identifier write_f; @@ struct file_operations fops = { ... .write = write_f, ... }; @ has_open depends on fops0 @ identifier fops0.fops; identifier open_f; @@ struct file_operations fops = { ... .open = open_f, ... }; // use no_llseek if we call nonseekable_open //////////////////////////////////////////// @ nonseekable1 depends on !has_llseek && has_open @ identifier fops0.fops; identifier nso ~= "nonseekable_open"; @@ struct file_operations fops = { ... .open = nso, ... +.llseek = no_llseek, /* nonseekable */ }; @ nonseekable2 depends on !has_llseek @ identifier fops0.fops; identifier open.open_f; @@ struct file_operations fops = { ... .open = open_f, ... +.llseek = no_llseek, /* open uses nonseekable */ }; // use seq_lseek for sequential files ///////////////////////////////////// @ seq depends on !has_llseek @ identifier fops0.fops; identifier sr ~= "seq_read"; @@ struct file_operations fops = { ... .read = sr, ... +.llseek = seq_lseek, /* we have seq_read */ }; // use default_llseek if there is a readdir /////////////////////////////////////////// @ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier readdir_e; @@ // any other fop is used that changes pos struct file_operations fops = { ... .readdir = readdir_e, ... +.llseek = default_llseek, /* readdir is present */ }; // use default_llseek if at least one of read/write touches f_pos ///////////////////////////////////////////////////////////////// @ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier read.read_f; @@ // read fops use offset struct file_operations fops = { ... .read = read_f, ... +.llseek = default_llseek, /* read accesses f_pos */ }; @ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier write.write_f; @@ // write fops use offset struct file_operations fops = { ... .write = write_f, ... + .llseek = default_llseek, /* write accesses f_pos */ }; // Use noop_llseek if neither read nor write accesses f_pos /////////////////////////////////////////////////////////// @ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier read_no_fpos.read_f; identifier write_no_fpos.write_f; @@ // write fops use offset struct file_operations fops = { ... .write = write_f, .read = read_f, ... +.llseek = noop_llseek, /* read and write both use no f_pos */ }; @ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier write_no_fpos.write_f; @@ struct file_operations fops = { ... .write = write_f, ... +.llseek = noop_llseek, /* write uses no f_pos */ }; @ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; identifier read_no_fpos.read_f; @@ struct file_operations fops = { ... .read = read_f, ... +.llseek = noop_llseek, /* read uses no f_pos */ }; @ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @ identifier fops0.fops; @@ struct file_operations fops = { ... +.llseek = noop_llseek, /* no read or write fn */ }; ===== End semantic patch ===== Signed-off-by: Arnd Bergmann Cc: Julia Lawall Cc: Christoph Hellwig --- fs/afs/mntpt.c | 1 + fs/autofs4/dev-ioctl.c | 1 + fs/binfmt_misc.c | 3 +++ fs/btrfs/super.c | 1 + fs/cachefiles/daemon.c | 1 + fs/char_dev.c | 1 + fs/coda/pioctl.c | 1 + fs/coda/psdev.c | 1 + fs/debugfs/file.c | 3 +++ fs/dlm/debug_fs.c | 3 ++- fs/dlm/plock.c | 3 ++- fs/dlm/user.c | 3 +++ fs/ecryptfs/file.c | 1 + fs/ecryptfs/miscdev.c | 1 + fs/eventfd.c | 1 + fs/eventpoll.c | 3 ++- fs/fifo.c | 1 + fs/fuse/control.c | 4 ++++ fs/fuse/cuse.c | 1 + fs/gfs2/file.c | 2 ++ fs/hppfs/hppfs.c | 1 + fs/hugetlbfs/inode.c | 1 + fs/logfs/dir.c | 1 + fs/nfsd/nfsctl.c | 1 + fs/no-block.c | 1 + fs/notify/fanotify/fanotify_user.c | 1 + fs/notify/inotify/inotify_user.c | 1 + fs/ocfs2/dlmfs/dlmfs.c | 1 + fs/ocfs2/stack_user.c | 1 + fs/proc/base.c | 8 ++++++++ fs/proc/proc_sysctl.c | 1 + fs/proc/root.c | 1 + fs/proc/task_mmu.c | 1 + fs/romfs/super.c | 1 + fs/signalfd.c | 1 + fs/squashfs/dir.c | 3 ++- fs/timerfd.c | 1 + fs/ubifs/debug.c | 1 + 38 files changed, 59 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index 6d552686c49..6153417caf5 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -29,6 +29,7 @@ static void afs_mntpt_expiry_timed_out(struct work_struct *work); const struct file_operations afs_mntpt_file_operations = { .open = afs_mntpt_open, + .llseek = noop_llseek, }; const struct inode_operations afs_mntpt_inode_operations = { diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index ba4a38b9c22..eff9a419469 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -724,6 +724,7 @@ static const struct file_operations _dev_ioctl_fops = { .unlocked_ioctl = autofs_dev_ioctl, .compat_ioctl = autofs_dev_ioctl_compat, .owner = THIS_MODULE, + .llseek = noop_llseek, }; static struct miscdevice _autofs_dev_ioctl_misc = { diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index fd0cc0bf9a4..139fc8083f5 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -576,6 +576,7 @@ static ssize_t bm_entry_write(struct file *file, const char __user *buffer, static const struct file_operations bm_entry_operations = { .read = bm_entry_read, .write = bm_entry_write, + .llseek = default_llseek, }; /* /register */ @@ -643,6 +644,7 @@ out: static const struct file_operations bm_register_operations = { .write = bm_register_write, + .llseek = noop_llseek, }; /* /status */ @@ -680,6 +682,7 @@ static ssize_t bm_status_write(struct file * file, const char __user * buffer, static const struct file_operations bm_status_operations = { .read = bm_status_read, .write = bm_status_write, + .llseek = default_llseek, }; /* Superblock handling */ diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 1776dbd8dc9..144f8a5730f 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -815,6 +815,7 @@ static const struct file_operations btrfs_ctl_fops = { .unlocked_ioctl = btrfs_control_ioctl, .compat_ioctl = btrfs_control_ioctl, .owner = THIS_MODULE, + .llseek = noop_llseek, }; static struct miscdevice btrfs_misc = { diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 727caedcdd9..0a1467b1551 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -55,6 +55,7 @@ const struct file_operations cachefiles_daemon_fops = { .read = cachefiles_daemon_read, .write = cachefiles_daemon_write, .poll = cachefiles_daemon_poll, + .llseek = noop_llseek, }; struct cachefiles_daemon_cmd { diff --git a/fs/char_dev.c b/fs/char_dev.c index f80a4f25123..e4a3318b812 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -454,6 +454,7 @@ static void cdev_purge(struct cdev *cdev) */ const struct file_operations def_chr_fops = { .open = chrdev_open, + .llseek = noop_llseek, }; static struct kobject *exact_match(dev_t dev, int *part, void *data) diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index ca25d96d45c..028a9a0f588 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -39,6 +39,7 @@ const struct inode_operations coda_ioctl_inode_operations = { const struct file_operations coda_ioctl_operations = { .owner = THIS_MODULE, .unlocked_ioctl = coda_pioctl, + .llseek = noop_llseek, }; /* the coda pioctl inode ops */ diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index de89645777c..9fa280bfcff 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -346,6 +346,7 @@ static const struct file_operations coda_psdev_fops = { .unlocked_ioctl = coda_psdev_ioctl, .open = coda_psdev_open, .release = coda_psdev_release, + .llseek = noop_llseek, }; static int init_coda_psdev(void) diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 0210898458b..89d394d8fe2 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -43,6 +43,7 @@ const struct file_operations debugfs_file_operations = { .read = default_read_file, .write = default_write_file, .open = default_open, + .llseek = noop_llseek, }; static void *debugfs_follow_link(struct dentry *dentry, struct nameidata *nd) @@ -454,6 +455,7 @@ static const struct file_operations fops_bool = { .read = read_file_bool, .write = write_file_bool, .open = default_open, + .llseek = default_llseek, }; /** @@ -498,6 +500,7 @@ static ssize_t read_file_blob(struct file *file, char __user *user_buf, static const struct file_operations fops_blob = { .read = read_file_blob, .open = default_open, + .llseek = default_llseek, }; /** diff --git a/fs/dlm/debug_fs.c b/fs/dlm/debug_fs.c index c6cf2515874..6b42ba807df 100644 --- a/fs/dlm/debug_fs.c +++ b/fs/dlm/debug_fs.c @@ -643,7 +643,8 @@ static ssize_t waiters_read(struct file *file, char __user *userbuf, static const struct file_operations waiters_fops = { .owner = THIS_MODULE, .open = waiters_open, - .read = waiters_read + .read = waiters_read, + .llseek = default_llseek, }; void dlm_delete_debug_file(struct dlm_ls *ls) diff --git a/fs/dlm/plock.c b/fs/dlm/plock.c index d45c02db694..30d8b85febb 100644 --- a/fs/dlm/plock.c +++ b/fs/dlm/plock.c @@ -412,7 +412,8 @@ static const struct file_operations dev_fops = { .read = dev_read, .write = dev_write, .poll = dev_poll, - .owner = THIS_MODULE + .owner = THIS_MODULE, + .llseek = noop_llseek, }; static struct miscdevice plock_dev_misc = { diff --git a/fs/dlm/user.c b/fs/dlm/user.c index b6272853130..66d6c16bf44 100644 --- a/fs/dlm/user.c +++ b/fs/dlm/user.c @@ -1009,6 +1009,7 @@ static const struct file_operations device_fops = { .write = device_write, .poll = device_poll, .owner = THIS_MODULE, + .llseek = noop_llseek, }; static const struct file_operations ctl_device_fops = { @@ -1017,6 +1018,7 @@ static const struct file_operations ctl_device_fops = { .read = device_read, .write = device_write, .owner = THIS_MODULE, + .llseek = noop_llseek, }; static struct miscdevice ctl_device = { @@ -1029,6 +1031,7 @@ static const struct file_operations monitor_device_fops = { .open = monitor_device_open, .release = monitor_device_close, .owner = THIS_MODULE, + .llseek = noop_llseek, }; static struct miscdevice monitor_device = { diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 622c9514080..86e9da1c787 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -332,6 +332,7 @@ const struct file_operations ecryptfs_dir_fops = { .fsync = ecryptfs_fsync, .fasync = ecryptfs_fasync, .splice_read = generic_file_splice_read, + .llseek = default_llseek, }; const struct file_operations ecryptfs_main_fops = { diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c index 00208c3d7e9..940a82e63dc 100644 --- a/fs/ecryptfs/miscdev.c +++ b/fs/ecryptfs/miscdev.c @@ -482,6 +482,7 @@ static const struct file_operations ecryptfs_miscdev_fops = { .read = ecryptfs_miscdev_read, .write = ecryptfs_miscdev_write, .release = ecryptfs_miscdev_release, + .llseek = noop_llseek, }; static struct miscdevice ecryptfs_miscdev = { diff --git a/fs/eventfd.c b/fs/eventfd.c index 6bd3f76fdf8..e0194b3e14d 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -293,6 +293,7 @@ static const struct file_operations eventfd_fops = { .poll = eventfd_poll, .read = eventfd_read, .write = eventfd_write, + .llseek = noop_llseek, }; /** diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 3817149919c..256bb7bb102 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -674,7 +674,8 @@ static unsigned int ep_eventpoll_poll(struct file *file, poll_table *wait) /* File callbacks that implement the eventpoll file behaviour */ static const struct file_operations eventpoll_fops = { .release = ep_eventpoll_release, - .poll = ep_eventpoll_poll + .poll = ep_eventpoll_poll, + .llseek = noop_llseek, }; /* Fast test to see if the file is an evenpoll file */ diff --git a/fs/fifo.c b/fs/fifo.c index 5d6606ffc2d..4e303c22d5e 100644 --- a/fs/fifo.c +++ b/fs/fifo.c @@ -151,4 +151,5 @@ err_nocleanup: */ const struct file_operations def_fifo_fops = { .open = fifo_open, /* will set read_ or write_pipefifo_fops */ + .llseek = noop_llseek, }; diff --git a/fs/fuse/control.c b/fs/fuse/control.c index 3773fd63d2f..7367e177186 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c @@ -179,23 +179,27 @@ static ssize_t fuse_conn_congestion_threshold_write(struct file *file, static const struct file_operations fuse_ctl_abort_ops = { .open = nonseekable_open, .write = fuse_conn_abort_write, + .llseek = no_llseek, }; static const struct file_operations fuse_ctl_waiting_ops = { .open = nonseekable_open, .read = fuse_conn_waiting_read, + .llseek = no_llseek, }; static const struct file_operations fuse_conn_max_background_ops = { .open = nonseekable_open, .read = fuse_conn_max_background_read, .write = fuse_conn_max_background_write, + .llseek = no_llseek, }; static const struct file_operations fuse_conn_congestion_threshold_ops = { .open = nonseekable_open, .read = fuse_conn_congestion_threshold_read, .write = fuse_conn_congestion_threshold_write, + .llseek = no_llseek, }; static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index e1f8171278b..3e87cce5837 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -182,6 +182,7 @@ static const struct file_operations cuse_frontend_fops = { .unlocked_ioctl = cuse_file_ioctl, .compat_ioctl = cuse_file_compat_ioctl, .poll = fuse_file_poll, + .llseek = noop_llseek, }; diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 4edd662c823..55d25a68b49 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -771,6 +771,7 @@ const struct file_operations gfs2_dir_fops = { .fsync = gfs2_fsync, .lock = gfs2_lock, .flock = gfs2_flock, + .llseek = default_llseek, }; #endif /* CONFIG_GFS2_FS_LOCKING_DLM */ @@ -797,5 +798,6 @@ const struct file_operations gfs2_dir_fops_nolock = { .open = gfs2_open, .release = gfs2_close, .fsync = gfs2_fsync, + .llseek = default_llseek, }; diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c index 7b027720d82..4e2a45ea614 100644 --- a/fs/hppfs/hppfs.c +++ b/fs/hppfs/hppfs.c @@ -598,6 +598,7 @@ static const struct file_operations hppfs_dir_fops = { .readdir = hppfs_readdir, .open = hppfs_dir_open, .fsync = hppfs_fsync, + .llseek = default_llseek, }; static int hppfs_statfs(struct dentry *dentry, struct kstatfs *sf) diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 6e5bd42f386..113eba3d3c3 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -674,6 +674,7 @@ const struct file_operations hugetlbfs_file_operations = { .mmap = hugetlbfs_file_mmap, .fsync = noop_fsync, .get_unmapped_area = hugetlb_get_unmapped_area, + .llseek = default_llseek, }; static const struct inode_operations hugetlbfs_dir_inode_operations = { diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index 9777eb5b552..1eb4e89e045 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -827,4 +827,5 @@ const struct file_operations logfs_dir_fops = { .unlocked_ioctl = logfs_ioctl, .readdir = logfs_readdir, .read = generic_read_dir, + .llseek = default_llseek, }; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index b53b1d042f1..06fa87e52e8 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -137,6 +137,7 @@ static const struct file_operations transaction_ops = { .write = nfsctl_transaction_write, .read = nfsctl_transaction_read, .release = simple_transaction_release, + .llseek = default_llseek, }; static int exports_open(struct inode *inode, struct file *file) diff --git a/fs/no-block.c b/fs/no-block.c index d269a93d346..6e40e42a43d 100644 --- a/fs/no-block.c +++ b/fs/no-block.c @@ -19,4 +19,5 @@ static int no_blkdev_open(struct inode * inode, struct file * filp) const struct file_operations def_blk_fops = { .open = no_blkdev_open, + .llseek = noop_llseek, }; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 5ed8e58d7bf..bbcb98e7fcc 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -433,6 +433,7 @@ static const struct file_operations fanotify_fops = { .release = fanotify_release, .unlocked_ioctl = fanotify_ioctl, .compat_ioctl = fanotify_ioctl, + .llseek = noop_llseek, }; static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index bf7f6d776c3..24edc1185d5 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -344,6 +344,7 @@ static const struct file_operations inotify_fops = { .release = inotify_release, .unlocked_ioctl = inotify_ioctl, .compat_ioctl = inotify_ioctl, + .llseek = noop_llseek, }; diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index c2903b84bb7..a7ebd9d42dc 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -612,6 +612,7 @@ static const struct file_operations dlmfs_file_operations = { .poll = dlmfs_file_poll, .read = dlmfs_file_read, .write = dlmfs_file_write, + .llseek = default_llseek, }; static const struct inode_operations dlmfs_dir_inode_operations = { diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index 2dc57bca068..0dbc6dae1de 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -628,6 +628,7 @@ static const struct file_operations ocfs2_control_fops = { .read = ocfs2_control_read, .write = ocfs2_control_write, .owner = THIS_MODULE, + .llseek = default_llseek, }; static struct miscdevice ocfs2_control_device = { diff --git a/fs/proc/base.c b/fs/proc/base.c index a1c43e7c8a7..bc307d7a5b7 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1151,6 +1151,7 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf, static const struct file_operations proc_oom_score_adj_operations = { .read = oom_score_adj_read, .write = oom_score_adj_write, + .llseek = default_llseek, }; #ifdef CONFIG_AUDITSYSCALL @@ -2039,11 +2040,13 @@ static ssize_t proc_fdinfo_read(struct file *file, char __user *buf, static const struct file_operations proc_fdinfo_file_operations = { .open = nonseekable_open, .read = proc_fdinfo_read, + .llseek = no_llseek, }; static const struct file_operations proc_fd_operations = { .read = generic_read_dir, .readdir = proc_readfd, + .llseek = default_llseek, }; /* @@ -2112,6 +2115,7 @@ static int proc_readfdinfo(struct file *filp, void *dirent, filldir_t filldir) static const struct file_operations proc_fdinfo_operations = { .read = generic_read_dir, .readdir = proc_readfdinfo, + .llseek = default_llseek, }; /* @@ -2343,6 +2347,7 @@ static int proc_attr_dir_readdir(struct file * filp, static const struct file_operations proc_attr_dir_operations = { .read = generic_read_dir, .readdir = proc_attr_dir_readdir, + .llseek = default_llseek, }; static struct dentry *proc_attr_dir_lookup(struct inode *dir, @@ -2751,6 +2756,7 @@ static int proc_tgid_base_readdir(struct file * filp, static const struct file_operations proc_tgid_base_operations = { .read = generic_read_dir, .readdir = proc_tgid_base_readdir, + .llseek = default_llseek, }; static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ @@ -3088,6 +3094,7 @@ static struct dentry *proc_tid_base_lookup(struct inode *dir, struct dentry *den static const struct file_operations proc_tid_base_operations = { .read = generic_read_dir, .readdir = proc_tid_base_readdir, + .llseek = default_llseek, }; static const struct inode_operations proc_tid_base_inode_operations = { @@ -3324,4 +3331,5 @@ static const struct inode_operations proc_task_inode_operations = { static const struct file_operations proc_task_operations = { .read = generic_read_dir, .readdir = proc_task_readdir, + .llseek = default_llseek, }; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 5be436ea088..2fc52552271 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -364,6 +364,7 @@ static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct static const struct file_operations proc_sys_file_operations = { .read = proc_sys_read, .write = proc_sys_write, + .llseek = default_llseek, }; static const struct file_operations proc_sys_dir_file_operations = { diff --git a/fs/proc/root.c b/fs/proc/root.c index 4258384ed22..93d99b31632 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -179,6 +179,7 @@ static int proc_root_readdir(struct file * filp, static const struct file_operations proc_root_operations = { .read = generic_read_dir, .readdir = proc_root_readdir, + .llseek = default_llseek, }; /* diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 271afc48b9a..aa56920df33 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -539,6 +539,7 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, const struct file_operations proc_clear_refs_operations = { .write = clear_refs_write, + .llseek = noop_llseek, }; struct pagemapread { diff --git a/fs/romfs/super.c b/fs/romfs/super.c index 42d21354689..268580535c9 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c @@ -282,6 +282,7 @@ error: static const struct file_operations romfs_dir_operations = { .read = generic_read_dir, .readdir = romfs_readdir, + .llseek = default_llseek, }; static const struct inode_operations romfs_dir_inode_operations = { diff --git a/fs/signalfd.c b/fs/signalfd.c index 1c5a6add779..74047304b01 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -206,6 +206,7 @@ static const struct file_operations signalfd_fops = { .release = signalfd_release, .poll = signalfd_poll, .read = signalfd_read, + .llseek = noop_llseek, }; SYSCALL_DEFINE4(signalfd4, int, ufd, sigset_t __user *, user_mask, diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c index 12b933ac658..0dc340aa2be 100644 --- a/fs/squashfs/dir.c +++ b/fs/squashfs/dir.c @@ -230,5 +230,6 @@ failed_read: const struct file_operations squashfs_dir_ops = { .read = generic_read_dir, - .readdir = squashfs_readdir + .readdir = squashfs_readdir, + .llseek = default_llseek, }; diff --git a/fs/timerfd.c b/fs/timerfd.c index b86ab8eff79..8c4fc1425b3 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -144,6 +144,7 @@ static const struct file_operations timerfd_fops = { .release = timerfd_release, .poll = timerfd_poll, .read = timerfd_read, + .llseek = noop_llseek, }; static struct file *timerfd_fget(int fd) diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index c2a68baa782..c6c553fd0b3 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -2625,6 +2625,7 @@ static const struct file_operations dfs_fops = { .open = open_debugfs_file, .write = write_debugfs_file, .owner = THIS_MODULE, + .llseek = default_llseek, }; /** -- cgit v1.2.3-18-g5258 From ab91261f5c43f196ec7ff1d113847b87b7606b26 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 7 Jul 2010 22:55:17 +0200 Subject: vfs: don't use BKL in default_llseek There are currently 191 users of default_llseek. Nine of these are in device drivers that use the big kernel lock. None of these ever touch file->f_pos outside of llseek or file_pos_write. Consequently, we never rely on the BKL in the default_llseek function and can replace that with i_mutex, which is also used in generic_file_llseek. Signed-off-by: Arnd Bergmann --- fs/read_write.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/read_write.c b/fs/read_write.c index 74e36586e4d..fd09f6166cc 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -124,7 +124,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin) { loff_t retval; - lock_kernel(); + mutex_lock(&file->f_dentry->d_inode->i_mutex); switch (origin) { case SEEK_END: offset += i_size_read(file->f_path.dentry->d_inode); @@ -145,7 +145,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin) retval = offset; } out: - unlock_kernel(); + mutex_unlock(&file->f_dentry->d_inode->i_mutex); return retval; } EXPORT_SYMBOL(default_llseek); -- cgit v1.2.3-18-g5258 From 776c163b1b93c8dfa5edba885bc2bfbc2d228a5f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 7 Jul 2010 23:10:11 +0200 Subject: vfs: make no_llseek the default All file operations now have an explicit .llseek operation pointer, so we can change the default action for future code. This makes changes the default from default_llseek to no_llseek, which always returns -ESPIPE if a user tries to seek on a file without a .llseek operation. The name of the default_llseek function remains unchanged, if anyone thinks we should change it, please speak up. Signed-off-by: Arnd Bergmann Cc: Christoph Hellwig Cc: Al Viro Cc: linux-fsdevel@vger.kernel.org --- fs/read_write.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/read_write.c b/fs/read_write.c index fd09f6166cc..e757ef26e4c 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -156,7 +156,6 @@ loff_t vfs_llseek(struct file *file, loff_t offset, int origin) fn = no_llseek; if (file->f_mode & FMODE_LSEEK) { - fn = default_llseek; if (file->f_op && file->f_op->llseek) fn = file->f_op->llseek; } -- cgit v1.2.3-18-g5258 From 6221ddd0f5e2ddc1d5d928119a2cde033d16f96d Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Fri, 1 Oct 2010 21:23:33 +0530 Subject: cifs: handle FindFirst failure gracefully FindFirst failure due to permission errors or any other errors are silently ignored by cifs_readdir(). This could cause problem to applications that depend on the error to do further processing. Reproducer: - mount a cifs share - mkdir tdir;touch tdir/1 tdir/2 tdir/3 - chmod -x tdir - ls tdir Currently, we start calling filldir() for '.' and '..' before we know we whether FindFirst could succeed or not. If FindFirst fails later, there is no way to notify VFS by setting buf.error and so VFS won't be able to catch this. Fix this by moving the call to initiate_cifs_search() before we start doing filldir(). This fixes https://bugzilla.samba.org/show_bug.cgi?id=7535 Reported-by: Tom Dexter Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/readdir.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 1f0bd0f972d..6f3d13ff947 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -790,6 +790,17 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); + /* + * Ensure FindFirst doesn't fail before doing filldir() for '.' and + * '..'. Otherwise we won't be able to notify VFS in case of failure. + */ + if (file->private_data == NULL) { + rc = initiate_cifs_search(xid, file); + cFYI(1, "initiate cifs search rc %d", rc); + if (rc) + goto rddir2_exit; + } + switch ((int) file->f_pos) { case 0: if (filldir(direntry, ".", 1, file->f_pos, @@ -813,14 +824,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) if it before then restart search if after then keep searching till find it */ - if (file->private_data == NULL) { - rc = initiate_cifs_search(xid, file); - cFYI(1, "initiate cifs search rc %d", rc); - if (rc) { - FreeXid(xid); - return rc; - } - } if (file->private_data == NULL) { rc = -EINVAL; FreeXid(xid); -- cgit v1.2.3-18-g5258 From 2f4f26fcf393ef4a44abe10e79c1966e64e86055 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 13 Oct 2010 18:50:39 -0400 Subject: cifs: eliminate cifs_posix_open_inode_helper cifs: eliminate cifs_posix_open_inode_helper This function is redundant. The only thing it does is set the canCache flags, but those get set in cifs_new_fileinfo anyway. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Acked-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/file.c | 67 ---------------------------------------------------------- 1 file changed, 67 deletions(-) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index fd78a355f63..c10f0851cd2 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -106,64 +106,6 @@ static inline int cifs_get_disposition(unsigned int flags) return FILE_OPEN; } -/* all arguments to this function must be checked for validity in caller */ -static inline int -cifs_posix_open_inode_helper(struct inode *inode, struct file *file, - struct cifsInodeInfo *pCifsInode, __u32 oplock, - u16 netfid) -{ - - write_lock(&GlobalSMBSeslock); - - pCifsInode = CIFS_I(file->f_path.dentry->d_inode); - if (pCifsInode == NULL) { - write_unlock(&GlobalSMBSeslock); - return -EINVAL; - } - - if (pCifsInode->clientCanCacheRead) { - /* we have the inode open somewhere else - no need to discard cache data */ - goto psx_client_can_cache; - } - - /* BB FIXME need to fix this check to move it earlier into posix_open - BB fIX following section BB FIXME */ - - /* if not oplocked, invalidate inode pages if mtime or file - size changed */ -/* temp = cifs_NTtimeToUnix(le64_to_cpu(buf->LastWriteTime)); - if (timespec_equal(&file->f_path.dentry->d_inode->i_mtime, &temp) && - (file->f_path.dentry->d_inode->i_size == - (loff_t)le64_to_cpu(buf->EndOfFile))) { - cFYI(1, "inode unchanged on server"); - } else { - if (file->f_path.dentry->d_inode->i_mapping) { - rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping); - if (rc != 0) - CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc; - } - cFYI(1, "invalidating remote inode since open detected it " - "changed"); - invalidate_remote_inode(file->f_path.dentry->d_inode); - } */ - -psx_client_can_cache: - if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { - pCifsInode->clientCanCacheAll = true; - pCifsInode->clientCanCacheRead = true; - cFYI(1, "Exclusive Oplock granted on inode %p", - file->f_path.dentry->d_inode); - } else if ((oplock & 0xF) == OPLOCK_READ) - pCifsInode->clientCanCacheRead = true; - - /* will have to change the unlock if we reenable the - filemap_fdatawrite (which does not seem necessary */ - write_unlock(&GlobalSMBSeslock); - return 0; -} - -/* all arguments to this function must be checked for validity in caller */ static inline int cifs_open_inode_helper(struct inode *inode, struct cifsTconInfo *pTcon, __u32 oplock, FILE_ALL_INFO *buf, char *full_path, int xid) @@ -271,15 +213,6 @@ int cifs_open(struct inode *inode, struct file *file) oflags, &oplock, &netfid, xid); if (rc == 0) { cFYI(1, "posix open succeeded"); - /* no need for special case handling of setting mode - on read only files needed here */ - - rc = cifs_posix_open_inode_helper(inode, file, - pCifsInode, oplock, netfid); - if (rc != 0) { - CIFSSMBClose(xid, tcon, netfid); - goto out; - } pCifsFile = cifs_new_fileinfo(inode, netfid, file, tlink, oflags, oplock); -- cgit v1.2.3-18-g5258 From d4396eafe402b710a8535137b3bf2abe6c059a15 Mon Sep 17 00:00:00 2001 From: Sunil Mushran Date: Fri, 15 Oct 2010 11:57:21 -0700 Subject: ocfs2/cluster: Release debugfs file elapsed_time_in_ms An earlier commit forgot to remove a debugfs file, elapsed_time_in_ms. Signed-off-by: Sunil Mushran --- fs/ocfs2/cluster/heartbeat.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 6a1280a013e..52c7557f3e2 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1384,6 +1384,7 @@ static void o2hb_region_release(struct config_item *item) kfree(reg->hr_db_livenodes); debugfs_remove(reg->hr_debug_livenodes); debugfs_remove(reg->hr_debug_regnum); + debugfs_remove(reg->hr_debug_elapsed_time); debugfs_remove(reg->hr_debug_dir); spin_lock(&o2hb_live_lock); -- cgit v1.2.3-18-g5258 From 2decd65a2630633cee04d0b83fdcee46ad2989a1 Mon Sep 17 00:00:00 2001 From: Jeff Liu Date: Tue, 12 Oct 2010 11:18:18 +0800 Subject: ocfs2: Avoid to evaluate xattr block flags again. It was evaludated to indexed before, check it is ok i think. Signed-off-by: Jeff Liu Signed-off-by: Joel Becker --- fs/ocfs2/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 06fa5e77c40..67cd4391464 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -7081,7 +7081,7 @@ static int ocfs2_reflink_xattr_in_block(struct ocfs2_xattr_reflink *args, goto out; } - if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) + if (!indexed) ret = ocfs2_reflink_xattr_block(args, blk_bh, new_blk_bh); else ret = ocfs2_reflink_xattr_tree(args, blk_bh, new_blk_bh); -- cgit v1.2.3-18-g5258 From 69d44ffbd772bede8c2a6d182e6e14f94826520b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sat, 25 Sep 2010 23:34:22 +0200 Subject: sysfs: Add sysfs_merge_group() and sysfs_unmerge_group() This patch (as1420) adds sysfs_merge_group() and sysfs_unmerge_group() functions, allowing drivers easily to add and remove sets of attributes to a pre-existing attribute group directory. Signed-off-by: Alan Stern Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki --- fs/sysfs/group.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'fs') diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 23c1e598792..442f34ff1af 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -148,6 +148,65 @@ void sysfs_remove_group(struct kobject * kobj, sysfs_put(sd); } +/** + * sysfs_merge_group - merge files into a pre-existing attribute group. + * @kobj: The kobject containing the group. + * @grp: The files to create and the attribute group they belong to. + * + * This function returns an error if the group doesn't exist or any of the + * files already exist in that group, in which case none of the new files + * are created. + */ +int sysfs_merge_group(struct kobject *kobj, + const struct attribute_group *grp) +{ + struct sysfs_dirent *dir_sd; + int error = 0; + struct attribute *const *attr; + int i; + + if (grp) + dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); + else + dir_sd = sysfs_get(kobj->sd); + if (!dir_sd) + return -ENOENT; + + for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) + error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); + if (error) { + while (--i >= 0) + sysfs_hash_and_remove(dir_sd, NULL, (*--attr)->name); + } + sysfs_put(dir_sd); + + return error; +} +EXPORT_SYMBOL_GPL(sysfs_merge_group); + +/** + * sysfs_unmerge_group - remove files from a pre-existing attribute group. + * @kobj: The kobject containing the group. + * @grp: The files to remove and the attribute group they belong to. + */ +void sysfs_unmerge_group(struct kobject *kobj, + const struct attribute_group *grp) +{ + struct sysfs_dirent *dir_sd; + struct attribute *const *attr; + + if (grp) + dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); + else + dir_sd = sysfs_get(kobj->sd); + if (dir_sd) { + for (attr = grp->attrs; *attr; ++attr) + sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); + sysfs_put(dir_sd); + } +} +EXPORT_SYMBOL_GPL(sysfs_unmerge_group); + EXPORT_SYMBOL_GPL(sysfs_create_group); EXPORT_SYMBOL_GPL(sysfs_update_group); -- cgit v1.2.3-18-g5258 From 7d08ae3c9205b559f90c3d7a3abba3c6479673c7 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 17 Oct 2010 15:50:19 +0300 Subject: UBIFS: add a commentary about log recovery Add a commentary which elaborates that 'ubifs_recover_log_leb()' recovers only the last log LEB, not any. Also remove some unneeded newlines. Signed-off-by: Artem Bityutskiy --- fs/ubifs/recovery.c | 3 ++- fs/ubifs/replay.c | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index c902a5de90a..77e9b874b6c 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c @@ -772,7 +772,8 @@ out_free: * @sbuf: LEB-sized buffer to use * * This function does a scan of a LEB, but caters for errors that might have - * been caused by the unclean unmount from which we are attempting to recover. + * been caused by unclean reboots from which we are attempting to recover + * (assume that only the last log LEB can be corrupted by an unclean reboot). * * This function returns %0 on success and a negative error code on failure. */ diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index 730598cf634..7df04ba4878 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -839,6 +839,11 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) if (IS_ERR(sleb)) { if (PTR_ERR(sleb) != -EUCLEAN || !c->need_recovery) return PTR_ERR(sleb); + /* + * Note, the below function will recover this log LEB only if + * it is the last, because unclean reboots can possibly corrupt + * only the tail of the log. + */ sleb = ubifs_recover_log_leb(c, lnum, offs, sbuf); if (IS_ERR(sleb)) return PTR_ERR(sleb); @@ -850,7 +855,6 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) } node = sleb->buf; - snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list); if (c->cs_sqnum == 0) { /* @@ -897,7 +901,6 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) } list_for_each_entry(snod, &sleb->nodes, list) { - cond_resched(); if (snod->sqnum >= SQNUM_WATERMARK) { @@ -1030,9 +1033,7 @@ int ubifs_replay_journal(struct ubifs_info *c) return -ENOMEM; dbg_mnt("start replaying the journal"); - c->replaying = 1; - lnum = c->ltail_lnum = c->lhead_lnum; offs = c->lhead_offs; -- cgit v1.2.3-18-g5258 From 608712fe8609492a8670638ea86b97fafe49ebba Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Oct 2010 15:33:56 -0400 Subject: cifs: fix flags handling in cifs_posix_open The way flags are passed and converted for cifs_posix_open is rather non-sensical. Some callers call cifs_posix_convert_flags on the flags before they pass them to cifs_posix_open, whereas some don't. Two flag conversion steps is just confusing though. Change the function instead to clearly expect input in f_flags format, and fix the callers to pass that in. Then, have cifs_posix_open call cifs_convert_posix_flags to do the conversion. Move cifs_posix_open to file.c as well so we can keep cifs_convert_posix_flags as a static function. Fix it also to not ignore O_CREAT, O_EXCL and O_TRUNC, and instead have cifs_reopen_file mask those bits off before calling cifs_posix_open. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 2 +- fs/cifs/dir.c | 99 +++------------------------------------------ fs/cifs/file.c | 113 ++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 95 insertions(+), 119 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7f416abd34c..4f7edbaf011 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -111,7 +111,7 @@ extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, unsigned int oflags, __u32 oplock); extern int cifs_posix_open(char *full_path, struct inode **pinode, struct super_block *sb, - int mode, int oflags, + int mode, unsigned int f_flags, __u32 *poplock, __u16 *pnetfid, int xid); void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr); extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index c205ec9293e..8c1af712838 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -181,93 +181,6 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, return pCifsFile; } -int cifs_posix_open(char *full_path, struct inode **pinode, - struct super_block *sb, int mode, int oflags, - __u32 *poplock, __u16 *pnetfid, int xid) -{ - int rc; - FILE_UNIX_BASIC_INFO *presp_data; - __u32 posix_flags = 0; - struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifs_fattr fattr; - struct tcon_link *tlink; - struct cifsTconInfo *tcon; - - cFYI(1, "posix open %s", full_path); - - presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); - if (presp_data == NULL) - return -ENOMEM; - -/* So far cifs posix extensions can only map the following flags. - There are other valid fmode oflags such as FMODE_LSEEK, FMODE_PREAD, but - so far we do not seem to need them, and we can treat them as local only */ - if ((oflags & (FMODE_READ | FMODE_WRITE)) == - (FMODE_READ | FMODE_WRITE)) - posix_flags = SMB_O_RDWR; - else if (oflags & FMODE_READ) - posix_flags = SMB_O_RDONLY; - else if (oflags & FMODE_WRITE) - posix_flags = SMB_O_WRONLY; - if (oflags & O_CREAT) - posix_flags |= SMB_O_CREAT; - if (oflags & O_EXCL) - posix_flags |= SMB_O_EXCL; - if (oflags & O_TRUNC) - posix_flags |= SMB_O_TRUNC; - /* be safe and imply O_SYNC for O_DSYNC */ - if (oflags & O_DSYNC) - posix_flags |= SMB_O_SYNC; - if (oflags & O_DIRECTORY) - posix_flags |= SMB_O_DIRECTORY; - if (oflags & O_NOFOLLOW) - posix_flags |= SMB_O_NOFOLLOW; - if (oflags & O_DIRECT) - posix_flags |= SMB_O_DIRECT; - - mode &= ~current_umask(); - - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - goto posix_open_ret; - } - - tcon = tlink_tcon(tlink); - rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data, - poplock, full_path, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); - cifs_put_tlink(tlink); - - if (rc) - goto posix_open_ret; - - if (presp_data->Type == cpu_to_le32(-1)) - goto posix_open_ret; /* open ok, caller does qpathinfo */ - - if (!pinode) - goto posix_open_ret; /* caller does not need info */ - - cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb); - - /* get new inode and set it up */ - if (*pinode == NULL) { - cifs_fill_uniqueid(sb, &fattr); - *pinode = cifs_iget(sb, &fattr); - if (!*pinode) { - rc = -ENOMEM; - goto posix_open_ret; - } - } else { - cifs_fattr_to_inode(*pinode, &fattr); - } - -posix_open_ret: - kfree(presp_data); - return rc; -} - static void setup_cifs_dentry(struct cifsTconInfo *tcon, struct dentry *direntry, struct inode *newinode) @@ -321,9 +234,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, oplock = REQ_OPLOCK; if (nd && (nd->flags & LOOKUP_OPEN)) - oflags = nd->intent.open.flags; + oflags = nd->intent.open.file->f_flags; else - oflags = FMODE_READ | SMB_O_CREAT; + oflags = O_RDONLY | O_CREAT; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -359,9 +272,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, /* if the file is going to stay open, then we need to set the desired access properly */ desiredAccess = 0; - if (oflags & FMODE_READ) + if (OPEN_FMODE(oflags) & FMODE_READ) desiredAccess |= GENERIC_READ; /* is this too little? */ - if (oflags & FMODE_WRITE) + if (OPEN_FMODE(oflags) & FMODE_WRITE) desiredAccess |= GENERIC_WRITE; if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) @@ -716,11 +629,11 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, if (pTcon->unix_ext) { if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) && (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open && - (nd->intent.open.flags & O_CREAT)) { + (nd->intent.open.file->f_flags & O_CREAT)) { rc = cifs_posix_open(full_path, &newInode, parent_dir_inode->i_sb, nd->intent.open.create_mode, - nd->intent.open.flags, &oplock, + nd->intent.open.file->f_flags, &oplock, &fileHandle, xid); /* * The check below works around a bug in POSIX diff --git a/fs/cifs/file.c b/fs/cifs/file.c index c10f0851cd2..b1ca6a43ac1 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -60,34 +60,32 @@ static inline int cifs_convert_flags(unsigned int flags) FILE_READ_DATA); } -static inline fmode_t cifs_posix_convert_flags(unsigned int flags) +static u32 cifs_posix_convert_flags(unsigned int flags) { - fmode_t posix_flags = 0; + u32 posix_flags = 0; if ((flags & O_ACCMODE) == O_RDONLY) - posix_flags = FMODE_READ; + posix_flags = SMB_O_RDONLY; else if ((flags & O_ACCMODE) == O_WRONLY) - posix_flags = FMODE_WRITE; - else if ((flags & O_ACCMODE) == O_RDWR) { - /* GENERIC_ALL is too much permission to request - can cause unnecessary access denied on create */ - /* return GENERIC_ALL; */ - posix_flags = FMODE_READ | FMODE_WRITE; - } - /* can not map O_CREAT or O_EXCL or O_TRUNC flags when - reopening a file. They had their effect on the original open */ - if (flags & O_APPEND) - posix_flags |= (fmode_t)O_APPEND; + posix_flags = SMB_O_WRONLY; + else if ((flags & O_ACCMODE) == O_RDWR) + posix_flags = SMB_O_RDWR; + + if (flags & O_CREAT) + posix_flags |= SMB_O_CREAT; + if (flags & O_EXCL) + posix_flags |= SMB_O_EXCL; + if (flags & O_TRUNC) + posix_flags |= SMB_O_TRUNC; + /* be safe and imply O_SYNC for O_DSYNC */ if (flags & O_DSYNC) - posix_flags |= (fmode_t)O_DSYNC; - if (flags & __O_SYNC) - posix_flags |= (fmode_t)__O_SYNC; + posix_flags |= SMB_O_SYNC; if (flags & O_DIRECTORY) - posix_flags |= (fmode_t)O_DIRECTORY; + posix_flags |= SMB_O_DIRECTORY; if (flags & O_NOFOLLOW) - posix_flags |= (fmode_t)O_NOFOLLOW; + posix_flags |= SMB_O_NOFOLLOW; if (flags & O_DIRECT) - posix_flags |= (fmode_t)O_DIRECT; + posix_flags |= SMB_O_DIRECT; return posix_flags; } @@ -159,6 +157,68 @@ client_can_cache: return rc; } +int cifs_posix_open(char *full_path, struct inode **pinode, + struct super_block *sb, int mode, unsigned int f_flags, + __u32 *poplock, __u16 *pnetfid, int xid) +{ + int rc; + FILE_UNIX_BASIC_INFO *presp_data; + __u32 posix_flags = 0; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_fattr fattr; + struct tcon_link *tlink; + struct cifsTconInfo *tcon; + + cFYI(1, "posix open %s", full_path); + + presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); + if (presp_data == NULL) + return -ENOMEM; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto posix_open_ret; + } + + tcon = tlink_tcon(tlink); + mode &= ~current_umask(); + + posix_flags = cifs_posix_convert_flags(f_flags); + rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data, + poplock, full_path, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); + + if (rc) + goto posix_open_ret; + + if (presp_data->Type == cpu_to_le32(-1)) + goto posix_open_ret; /* open ok, caller does qpathinfo */ + + if (!pinode) + goto posix_open_ret; /* caller does not need info */ + + cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb); + + /* get new inode and set it up */ + if (*pinode == NULL) { + cifs_fill_uniqueid(sb, &fattr); + *pinode = cifs_iget(sb, &fattr); + if (!*pinode) { + rc = -ENOMEM; + goto posix_open_ret; + } + } else { + cifs_fattr_to_inode(*pinode, &fattr); + } + +posix_open_ret: + kfree(presp_data); + return rc; +} + int cifs_open(struct inode *inode, struct file *file) { int rc = -EACCES; @@ -205,12 +265,10 @@ int cifs_open(struct inode *inode, struct file *file) (tcon->ses->capabilities & CAP_UNIX) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { - int oflags = (int) cifs_posix_convert_flags(file->f_flags); - oflags |= SMB_O_CREAT; /* can not refresh inode info since size could be stale */ rc = cifs_posix_open(full_path, &inode, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, - oflags, &oplock, &netfid, xid); + file->f_flags, &oplock, &netfid, xid); if (rc == 0) { cFYI(1, "posix open succeeded"); @@ -426,8 +484,13 @@ reopen_error_exit: if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { - int oflags = (int) cifs_posix_convert_flags(file->f_flags); - /* can not refresh inode info since size could be stale */ + + /* + * O_CREAT, O_EXCL and O_TRUNC already had their effect on the + * original open. Must mask them off for a reopen. + */ + unsigned int oflags = file->f_flags & ~(O_CREAT|O_EXCL|O_TRUNC); + rc = cifs_posix_open(full_path, NULL, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, oflags, &oplock, &netfid, xid); -- cgit v1.2.3-18-g5258 From f6a53460e2a105904deeada737b3f878d78517b3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Oct 2010 15:33:57 -0400 Subject: cifs: eliminate oflags option from cifs_new_fileinfo Eliminate the poor, misunderstood "oflags" option from cifs_new_fileinfo. The callers mostly pass in the filp->f_flags here. That's not correct however since we're checking that value for the presence of FMODE_READ. Luckily that only affects how the f_list is ordered. What it really wants here is the file->f_mode. Just use that field from the filp to determine it. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 3 +-- fs/cifs/dir.c | 9 ++++----- fs/cifs/file.c | 5 ++--- 3 files changed, 7 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 4f7edbaf011..781676e9cfc 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -107,8 +107,7 @@ extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, - struct tcon_link *tlink, - unsigned int oflags, __u32 oplock); + struct tcon_link *tlink, __u32 oplock); extern int cifs_posix_open(char *full_path, struct inode **pinode, struct super_block *sb, int mode, unsigned int f_flags, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 8c1af712838..ce1fa3027b2 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -132,7 +132,7 @@ cifs_bp_rename_retry: struct cifsFileInfo * cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, - struct tcon_link *tlink, unsigned int oflags, __u32 oplock) + struct tcon_link *tlink, __u32 oplock) { struct dentry *dentry = file->f_path.dentry; struct cifsFileInfo *pCifsFile; @@ -161,7 +161,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, pCifsInode = CIFS_I(newinode); if (pCifsInode) { /* if readable file instance put first in list*/ - if (oflags & FMODE_READ) + if (file->f_mode & FMODE_READ) list_add(&pCifsFile->flist, &pCifsInode->openFileList); else list_add_tail(&pCifsFile->flist, @@ -396,7 +396,7 @@ cifs_create_set_dentry: } pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp, - tlink, oflags, oplock); + tlink, oplock); if (pfile_info == NULL) { fput(filp); CIFSSMBClose(xid, tcon, fileHandle); @@ -670,8 +670,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, } cfile = cifs_new_fileinfo(newInode, fileHandle, filp, - tlink, nd->intent.open.flags, - oplock); + tlink, oplock); if (cfile == NULL) { fput(filp); CIFSSMBClose(xid, pTcon, fileHandle); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index b1ca6a43ac1..774e3ac1208 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -273,7 +273,7 @@ int cifs_open(struct inode *inode, struct file *file) cFYI(1, "posix open succeeded"); pCifsFile = cifs_new_fileinfo(inode, netfid, file, - tlink, oflags, oplock); + tlink, oplock); if (pCifsFile == NULL) { CIFSSMBClose(xid, tcon, netfid); rc = -ENOMEM; @@ -365,8 +365,7 @@ int cifs_open(struct inode *inode, struct file *file) if (rc != 0) goto out; - pCifsFile = cifs_new_fileinfo(inode, netfid, file, tlink, - file->f_flags, oplock); + pCifsFile = cifs_new_fileinfo(inode, netfid, file, tlink, oplock); if (pCifsFile == NULL) { rc = -ENOMEM; goto out; -- cgit v1.2.3-18-g5258 From abfe1eedd682ea0f20e7035445982e6d371a2024 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Oct 2010 15:33:58 -0400 Subject: cifs: eliminate the inode argument from cifs_new_fileinfo It already takes a file pointer. The inode associated with that had damn well better be the same one we're passing in anyway. Thus, there's no need for a separate argument here. Also, get rid of the bogus check for a null pCifsInode pointer. The CIFS_I macro uses container_of(), and that will virtually never return a NULL pointer anyway. Finally, move the setting of the canCache* flags outside of the lock. Other places in the code don't hold that lock when setting it, so I assume it's not really needed here either. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Acked-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 3 +-- fs/cifs/dir.c | 40 ++++++++++++++++++---------------------- fs/cifs/file.c | 6 +++--- 3 files changed, 22 insertions(+), 27 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 781676e9cfc..ca6a0d9130c 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -105,8 +105,7 @@ extern u64 cifs_UnixTimeToNT(struct timespec); extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset); -extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, - __u16 fileHandle, struct file *file, +extern struct cifsFileInfo *cifs_new_fileinfo(__u16 fileHandle, struct file *file, struct tcon_link *tlink, __u32 oplock); extern int cifs_posix_open(char *full_path, struct inode **pinode, struct super_block *sb, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index ce1fa3027b2..36cf3e2ec25 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -131,12 +131,13 @@ cifs_bp_rename_retry: } struct cifsFileInfo * -cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, +cifs_new_fileinfo(__u16 fileHandle, struct file *file, struct tcon_link *tlink, __u32 oplock) { struct dentry *dentry = file->f_path.dentry; + struct inode *inode = dentry->d_inode; + struct cifsInodeInfo *pCifsInode = CIFS_I(inode); struct cifsFileInfo *pCifsFile; - struct cifsInodeInfo *pCifsInode; pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); if (pCifsFile == NULL) @@ -158,24 +159,20 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, write_lock(&GlobalSMBSeslock); list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList)); - pCifsInode = CIFS_I(newinode); - if (pCifsInode) { - /* if readable file instance put first in list*/ - if (file->f_mode & FMODE_READ) - list_add(&pCifsFile->flist, &pCifsInode->openFileList); - else - list_add_tail(&pCifsFile->flist, - &pCifsInode->openFileList); - - if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { - pCifsInode->clientCanCacheAll = true; - pCifsInode->clientCanCacheRead = true; - cFYI(1, "Exclusive Oplock inode %p", newinode); - } else if ((oplock & 0xF) == OPLOCK_READ) - pCifsInode->clientCanCacheRead = true; - } + /* if readable file instance put first in list*/ + if (file->f_mode & FMODE_READ) + list_add(&pCifsFile->flist, &pCifsInode->openFileList); + else + list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList); write_unlock(&GlobalSMBSeslock); + if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { + pCifsInode->clientCanCacheAll = true; + pCifsInode->clientCanCacheRead = true; + cFYI(1, "Exclusive Oplock inode %p", inode); + } else if ((oplock & 0xF) == OPLOCK_READ) + pCifsInode->clientCanCacheRead = true; + file->private_data = pCifsFile; return pCifsFile; @@ -395,8 +392,7 @@ cifs_create_set_dentry: goto cifs_create_out; } - pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp, - tlink, oplock); + pfile_info = cifs_new_fileinfo(fileHandle, filp, tlink, oplock); if (pfile_info == NULL) { fput(filp); CIFSSMBClose(xid, tcon, fileHandle); @@ -669,8 +665,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, goto lookup_out; } - cfile = cifs_new_fileinfo(newInode, fileHandle, filp, - tlink, oplock); + cfile = cifs_new_fileinfo(fileHandle, filp, tlink, + oplock); if (cfile == NULL) { fput(filp); CIFSSMBClose(xid, pTcon, fileHandle); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 774e3ac1208..394cf28f080 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -272,8 +272,8 @@ int cifs_open(struct inode *inode, struct file *file) if (rc == 0) { cFYI(1, "posix open succeeded"); - pCifsFile = cifs_new_fileinfo(inode, netfid, file, - tlink, oplock); + pCifsFile = cifs_new_fileinfo(netfid, file, tlink, + oplock); if (pCifsFile == NULL) { CIFSSMBClose(xid, tcon, netfid); rc = -ENOMEM; @@ -365,7 +365,7 @@ int cifs_open(struct inode *inode, struct file *file) if (rc != 0) goto out; - pCifsFile = cifs_new_fileinfo(inode, netfid, file, tlink, oplock); + pCifsFile = cifs_new_fileinfo(netfid, file, tlink, oplock); if (pCifsFile == NULL) { rc = -ENOMEM; goto out; -- cgit v1.2.3-18-g5258 From 15886177e412db00aa86155fe72608c4ebf5a08f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Oct 2010 15:33:59 -0400 Subject: cifs: clean up cifs_reopen_file Add a f_flags field that holds the f_flags field from the filp. We'll need this info in case the filp ever goes away before the cifsFileInfo does. Have cifs_reopen_file use that value instead of filp->f_flags too and have it take a cifsFileInfo arg instead of a filp. While we're at it, get rid of some bogus cargo-cult NULL pointer checks in that function and reduce the level of indentation. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Acked-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 + fs/cifs/dir.c | 1 + fs/cifs/file.c | 129 +++++++++++++++++++++++------------------------------ 3 files changed, 57 insertions(+), 74 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6c69bd76249..b746dec8839 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -390,6 +390,7 @@ struct cifsFileInfo { /* lock scope id (0 if none) */ struct file *pfile; /* needed for writepage */ struct dentry *dentry; + unsigned int f_flags; struct tcon_link *tlink; struct mutex lock_mutex; struct list_head llist; /* list of byte range locks we have. */ diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 36cf3e2ec25..3c23d882a44 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -147,6 +147,7 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, pCifsFile->pid = current->tgid; pCifsFile->uid = current_fsuid(); pCifsFile->dentry = dget(dentry); + pCifsFile->f_flags = file->f_flags; pCifsFile->pfile = file; pCifsFile->invalidHandle = false; pCifsFile->closePend = false; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 394cf28f080..dd922a5f9ca 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -412,14 +412,13 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile) return rc; } -static int cifs_reopen_file(struct file *file, bool can_flush) +static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush) { int rc = -EACCES; int xid; __u32 oplock; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *tcon; - struct cifsFileInfo *pCifsFile; struct cifsInodeInfo *pCifsInode; struct inode *inode; char *full_path = NULL; @@ -427,11 +426,6 @@ static int cifs_reopen_file(struct file *file, bool can_flush) int disposition = FILE_OPEN; __u16 netfid; - if (file->private_data) - pCifsFile = file->private_data; - else - return -EBADF; - xid = GetXid(); mutex_lock(&pCifsFile->fh_mutex); if (!pCifsFile->invalidHandle) { @@ -441,21 +435,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) return rc; } - if (file->f_path.dentry == NULL) { - cERROR(1, "no valid name if dentry freed"); - dump_stack(); - rc = -EBADF; - goto reopen_error_exit; - } - - inode = file->f_path.dentry->d_inode; - if (inode == NULL) { - cERROR(1, "inode not valid"); - dump_stack(); - rc = -EBADF; - goto reopen_error_exit; - } - + inode = pCifsFile->dentry->d_inode; cifs_sb = CIFS_SB(inode->i_sb); tcon = tlink_tcon(pCifsFile->tlink); @@ -463,17 +443,16 @@ static int cifs_reopen_file(struct file *file, bool can_flush) those that already have the rename sem can end up causing writepage to get called and if the server was down that means we end up here, and we can never tell if the caller already has the rename_sem */ - full_path = build_path_from_dentry(file->f_path.dentry); + full_path = build_path_from_dentry(pCifsFile->dentry); if (full_path == NULL) { rc = -ENOMEM; -reopen_error_exit: mutex_unlock(&pCifsFile->fh_mutex); FreeXid(xid); return rc; } cFYI(1, "inode = 0x%p file flags 0x%x for %s", - inode, file->f_flags, full_path); + inode, pCifsFile->f_flags, full_path); if (oplockEnabled) oplock = REQ_OPLOCK; @@ -488,7 +467,8 @@ reopen_error_exit: * O_CREAT, O_EXCL and O_TRUNC already had their effect on the * original open. Must mask them off for a reopen. */ - unsigned int oflags = file->f_flags & ~(O_CREAT|O_EXCL|O_TRUNC); + unsigned int oflags = pCifsFile->f_flags & + ~(O_CREAT | O_EXCL | O_TRUNC); rc = cifs_posix_open(full_path, NULL, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, @@ -501,7 +481,7 @@ reopen_error_exit: in the reconnect path it is important to retry hard */ } - desiredAccess = cifs_convert_flags(file->f_flags); + desiredAccess = cifs_convert_flags(pCifsFile->f_flags); /* Can not refresh inode by passing in file_info buf to be returned by SMBOpen and then calling get_inode_info with returned buf @@ -517,49 +497,50 @@ reopen_error_exit: mutex_unlock(&pCifsFile->fh_mutex); cFYI(1, "cifs_open returned 0x%x", rc); cFYI(1, "oplock: %d", oplock); - } else { + goto reopen_error_exit; + } + reopen_success: - pCifsFile->netfid = netfid; - pCifsFile->invalidHandle = false; - mutex_unlock(&pCifsFile->fh_mutex); - pCifsInode = CIFS_I(inode); - if (pCifsInode) { - if (can_flush) { - rc = filemap_write_and_wait(inode->i_mapping); - if (rc != 0) - CIFS_I(inode)->write_behind_rc = rc; - /* temporarily disable caching while we - go to server to get inode info */ - pCifsInode->clientCanCacheAll = false; - pCifsInode->clientCanCacheRead = false; - if (tcon->unix_ext) - rc = cifs_get_inode_info_unix(&inode, - full_path, inode->i_sb, xid); - else - rc = cifs_get_inode_info(&inode, - full_path, NULL, inode->i_sb, - xid, NULL); - } /* else we are writing out data to server already - and could deadlock if we tried to flush data, and - since we do not know if we have data that would - invalidate the current end of file on the server - we can not go to the server to get the new inod - info */ - if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { - pCifsInode->clientCanCacheAll = true; - pCifsInode->clientCanCacheRead = true; - cFYI(1, "Exclusive Oplock granted on inode %p", - file->f_path.dentry->d_inode); - } else if ((oplock & 0xF) == OPLOCK_READ) { - pCifsInode->clientCanCacheRead = true; - pCifsInode->clientCanCacheAll = false; - } else { - pCifsInode->clientCanCacheRead = false; - pCifsInode->clientCanCacheAll = false; - } - cifs_relock_file(pCifsFile); - } + pCifsFile->netfid = netfid; + pCifsFile->invalidHandle = false; + mutex_unlock(&pCifsFile->fh_mutex); + pCifsInode = CIFS_I(inode); + + if (can_flush) { + rc = filemap_write_and_wait(inode->i_mapping); + if (rc != 0) + CIFS_I(inode)->write_behind_rc = rc; + + pCifsInode->clientCanCacheAll = false; + pCifsInode->clientCanCacheRead = false; + if (tcon->unix_ext) + rc = cifs_get_inode_info_unix(&inode, + full_path, inode->i_sb, xid); + else + rc = cifs_get_inode_info(&inode, + full_path, NULL, inode->i_sb, + xid, NULL); + } /* else we are writing out data to server already + and could deadlock if we tried to flush data, and + since we do not know if we have data that would + invalidate the current end of file on the server + we can not go to the server to get the new inod + info */ + if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { + pCifsInode->clientCanCacheAll = true; + pCifsInode->clientCanCacheRead = true; + cFYI(1, "Exclusive Oplock granted on inode %p", + pCifsFile->dentry->d_inode); + } else if ((oplock & 0xF) == OPLOCK_READ) { + pCifsInode->clientCanCacheRead = true; + pCifsInode->clientCanCacheAll = false; + } else { + pCifsInode->clientCanCacheRead = false; + pCifsInode->clientCanCacheAll = false; } + cifs_relock_file(pCifsFile); + +reopen_error_exit: kfree(full_path); FreeXid(xid); return rc; @@ -998,7 +979,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, filemap_fdatawait from here so tell reopen_file not to flush data to server now */ - rc = cifs_reopen_file(file, false); + rc = cifs_reopen_file(open_file, false); if (rc != 0) break; } @@ -1096,7 +1077,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, filemap_fdatawait from here so tell reopen_file not to flush data to server now */ - rc = cifs_reopen_file(file, false); + rc = cifs_reopen_file(open_file, false); if (rc != 0) break; } @@ -1244,7 +1225,7 @@ refind_writable: read_unlock(&GlobalSMBSeslock); /* Had to unlock since following call can block */ - rc = cifs_reopen_file(open_file->pfile, false); + rc = cifs_reopen_file(open_file, false); if (!rc) { if (!open_file->closePend) return open_file; @@ -1792,7 +1773,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, int buf_type = CIFS_NO_BUFFER; if ((open_file->invalidHandle) && (!open_file->closePend)) { - rc = cifs_reopen_file(file, true); + rc = cifs_reopen_file(open_file, true); if (rc != 0) break; } @@ -1878,7 +1859,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, while (rc == -EAGAIN) { if ((open_file->invalidHandle) && (!open_file->closePend)) { - rc = cifs_reopen_file(file, true); + rc = cifs_reopen_file(open_file, true); if (rc != 0) break; } @@ -2043,7 +2024,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, while (rc == -EAGAIN) { if ((open_file->invalidHandle) && (!open_file->closePend)) { - rc = cifs_reopen_file(file, true); + rc = cifs_reopen_file(open_file, true); if (rc != 0) break; } -- cgit v1.2.3-18-g5258 From 7da4b49a0ec481239d9cd51f7053aa8e4e191fa7 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Oct 2010 15:34:00 -0400 Subject: cifs: cifs_write argument change and cleanup Have cifs_write take a cifsFileInfo pointer instead of a filp. Since cifsFileInfo holds references on the dentry, and that holds one to the inode, we can eliminate some unneeded NULL pointer checks. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Acked-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/file.c | 51 +++++++++++++++++---------------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index dd922a5f9ca..d5f3007364e 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1027,8 +1027,9 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, return total_written; } -static ssize_t cifs_write(struct file *file, const char *write_data, - size_t write_size, loff_t *poffset) +static ssize_t cifs_write(struct cifsFileInfo *open_file, + const char *write_data, size_t write_size, + loff_t *poffset) { int rc = 0; unsigned int bytes_written = 0; @@ -1036,17 +1037,14 @@ static ssize_t cifs_write(struct file *file, const char *write_data, struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; int xid, long_op; - struct cifsFileInfo *open_file; - struct cifsInodeInfo *cifsi = CIFS_I(file->f_path.dentry->d_inode); + struct dentry *dentry = open_file->dentry; + struct cifsInodeInfo *cifsi = CIFS_I(dentry->d_inode); - cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); + cifs_sb = CIFS_SB(dentry->d_sb); cFYI(1, "write %zd bytes to offset %lld of %s", write_size, - *poffset, file->f_path.dentry->d_name.name); + *poffset, dentry->d_name.name); - if (file->private_data == NULL) - return -EBADF; - open_file = file->private_data; pTcon = tlink_tcon(open_file->tlink); xid = GetXid(); @@ -1056,15 +1054,6 @@ static ssize_t cifs_write(struct file *file, const char *write_data, total_written += bytes_written) { rc = -EAGAIN; while (rc == -EAGAIN) { - if (file->private_data == NULL) { - /* file has been closed on us */ - FreeXid(xid); - /* if we have gotten here we have written some data - and blocked, and the file has been freed on us - while we blocked so return what we managed to - write */ - return total_written; - } if (open_file->closePend) { FreeXid(xid); if (total_written) @@ -1124,20 +1113,13 @@ static ssize_t cifs_write(struct file *file, const char *write_data, cifs_stats_bytes_written(pTcon, total_written); - /* since the write may have blocked check these pointers again */ - if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) { -/*BB We could make this contingent on superblock ATIME flag too */ -/* file->f_path.dentry->d_inode->i_ctime = - file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/ - if (total_written > 0) { - spin_lock(&file->f_path.dentry->d_inode->i_lock); - if (*poffset > file->f_path.dentry->d_inode->i_size) - i_size_write(file->f_path.dentry->d_inode, - *poffset); - spin_unlock(&file->f_path.dentry->d_inode->i_lock); - } - mark_inode_dirty_sync(file->f_path.dentry->d_inode); + if (total_written > 0) { + spin_lock(&dentry->d_inode->i_lock); + if (*poffset > dentry->d_inode->i_size) + i_size_write(dentry->d_inode, *poffset); + spin_unlock(&dentry->d_inode->i_lock); } + mark_inode_dirty_sync(dentry->d_inode); FreeXid(xid); return total_written; } @@ -1308,8 +1290,8 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) open_file = find_writable_file(CIFS_I(mapping->host), false); if (open_file) { - bytes_written = cifs_write(open_file->pfile, write_data, - to-from, &offset); + bytes_written = cifs_write(open_file, write_data, + to - from, &offset); cifsFileInfo_put(open_file); /* Does mm or vfs already set times? */ inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); @@ -1624,7 +1606,8 @@ static int cifs_write_end(struct file *file, struct address_space *mapping, /* BB check if anything else missing out of ppw such as updating last write time */ page_data = kmap(page); - rc = cifs_write(file, page_data + offset, copied, &pos); + rc = cifs_write(file->private_data, page_data + offset, + copied, &pos); /* if (rc < 0) should we set writebehind rc? */ kunmap(page); -- cgit v1.2.3-18-g5258 From 2e396b83f6087b78dac5a18d6d0cf9f8426a00b3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Oct 2010 15:34:01 -0400 Subject: cifs: eliminate pfile pointer from cifsFileInfo All the remaining users of cifsFileInfo->pfile just use it to get at the f_flags/f_mode. Now that we store that separately in the cifsFileInfo, there's no need to consult the pfile at all from a cifsFileInfo pointer. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Acked-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 - fs/cifs/dir.c | 1 - fs/cifs/file.c | 11 +++-------- 3 files changed, 3 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index b746dec8839..9f99afab9a0 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -388,7 +388,6 @@ struct cifsFileInfo { __u16 netfid; /* file id from remote */ /* BB add lock scope info here if needed */ ; /* lock scope id (0 if none) */ - struct file *pfile; /* needed for writepage */ struct dentry *dentry; unsigned int f_flags; struct tcon_link *tlink; diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 3c23d882a44..600eac18cb2 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -148,7 +148,6 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, pCifsFile->uid = current_fsuid(); pCifsFile->dentry = dget(dentry); pCifsFile->f_flags = file->f_flags; - pCifsFile->pfile = file; pCifsFile->invalidHandle = false; pCifsFile->closePend = false; pCifsFile->tlink = cifs_get_tlink(tlink); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index d5f3007364e..7935816fa11 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1144,8 +1144,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, continue; if (fsuid_only && open_file->uid != current_fsuid()) continue; - if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || - (open_file->pfile->f_flags & O_RDONLY))) { + if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { if (!open_file->invalidHandle) { /* found a good file */ /* lock it so it will not be closed on us */ @@ -1194,9 +1193,7 @@ refind_writable: continue; if (fsuid_only && open_file->uid != current_fsuid()) continue; - if (open_file->pfile && - ((open_file->pfile->f_flags & O_RDWR) || - (open_file->pfile->f_flags & O_WRONLY))) { + if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { cifsFileInfo_get(open_file); if (!open_file->invalidHandle) { @@ -2160,9 +2157,7 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode) list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (open_file->closePend) continue; - if (open_file->pfile && - ((open_file->pfile->f_flags & O_RDWR) || - (open_file->pfile->f_flags & O_WRONLY))) { + if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { read_unlock(&GlobalSMBSeslock); return 1; } -- cgit v1.2.3-18-g5258 From 15ecb436c00fcb13b6dc32bdcbb9f75fc9b7613e Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Oct 2010 15:34:02 -0400 Subject: cifs: move cifs_new_fileinfo to file.c It's currently in dir.c which makes little sense... Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Acked-by: Dave Kleikamp Signed-off-by: Steve French --- fs/cifs/dir.c | 48 ------------------------------------------------ fs/cifs/file.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 48 deletions(-) (limited to 'fs') diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 600eac18cb2..3840eddbfb7 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -130,54 +130,6 @@ cifs_bp_rename_retry: return full_path; } -struct cifsFileInfo * -cifs_new_fileinfo(__u16 fileHandle, struct file *file, - struct tcon_link *tlink, __u32 oplock) -{ - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = dentry->d_inode; - struct cifsInodeInfo *pCifsInode = CIFS_I(inode); - struct cifsFileInfo *pCifsFile; - - pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); - if (pCifsFile == NULL) - return pCifsFile; - - pCifsFile->netfid = fileHandle; - pCifsFile->pid = current->tgid; - pCifsFile->uid = current_fsuid(); - pCifsFile->dentry = dget(dentry); - pCifsFile->f_flags = file->f_flags; - pCifsFile->invalidHandle = false; - pCifsFile->closePend = false; - pCifsFile->tlink = cifs_get_tlink(tlink); - mutex_init(&pCifsFile->fh_mutex); - mutex_init(&pCifsFile->lock_mutex); - INIT_LIST_HEAD(&pCifsFile->llist); - atomic_set(&pCifsFile->count, 1); - INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); - - write_lock(&GlobalSMBSeslock); - list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList)); - /* if readable file instance put first in list*/ - if (file->f_mode & FMODE_READ) - list_add(&pCifsFile->flist, &pCifsInode->openFileList); - else - list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList); - write_unlock(&GlobalSMBSeslock); - - if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { - pCifsInode->clientCanCacheAll = true; - pCifsInode->clientCanCacheRead = true; - cFYI(1, "Exclusive Oplock inode %p", inode); - } else if ((oplock & 0xF) == OPLOCK_READ) - pCifsInode->clientCanCacheRead = true; - - file->private_data = pCifsFile; - - return pCifsFile; -} - static void setup_cifs_dentry(struct cifsTconInfo *tcon, struct dentry *direntry, struct inode *newinode) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 7935816fa11..293e9b76762 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -219,6 +219,53 @@ posix_open_ret: return rc; } +struct cifsFileInfo * +cifs_new_fileinfo(__u16 fileHandle, struct file *file, + struct tcon_link *tlink, __u32 oplock) +{ + struct dentry *dentry = file->f_path.dentry; + struct inode *inode = dentry->d_inode; + struct cifsInodeInfo *pCifsInode = CIFS_I(inode); + struct cifsFileInfo *pCifsFile; + + pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); + if (pCifsFile == NULL) + return pCifsFile; + + pCifsFile->netfid = fileHandle; + pCifsFile->pid = current->tgid; + pCifsFile->uid = current_fsuid(); + pCifsFile->dentry = dget(dentry); + pCifsFile->f_flags = file->f_flags; + pCifsFile->invalidHandle = false; + pCifsFile->closePend = false; + pCifsFile->tlink = cifs_get_tlink(tlink); + mutex_init(&pCifsFile->fh_mutex); + mutex_init(&pCifsFile->lock_mutex); + INIT_LIST_HEAD(&pCifsFile->llist); + atomic_set(&pCifsFile->count, 1); + INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); + + write_lock(&GlobalSMBSeslock); + list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList)); + /* if readable file instance put first in list*/ + if (file->f_mode & FMODE_READ) + list_add(&pCifsFile->flist, &pCifsInode->openFileList); + else + list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList); + write_unlock(&GlobalSMBSeslock); + + if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { + pCifsInode->clientCanCacheAll = true; + pCifsInode->clientCanCacheRead = true; + cFYI(1, "Exclusive Oplock inode %p", inode); + } else if ((oplock & 0xF) == OPLOCK_READ) + pCifsInode->clientCanCacheRead = true; + + file->private_data = pCifsFile; + return pCifsFile; +} + int cifs_open(struct inode *inode, struct file *file) { int rc = -EACCES; -- cgit v1.2.3-18-g5258 From 7a16f1961a5c61d1f60d9e0d3d171cf7793fb5cb Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 18 Oct 2010 01:09:48 +0000 Subject: [CIFS] Fix minor checkpatch warning and update cifs version Signed-off-by: Steve French --- fs/cifs/cifsfs.h | 2 +- fs/cifs/cifsproto.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 85544bcab51..f35795a16b4 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -112,5 +112,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.65" +#define CIFS_VERSION "1.67" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ca6a0d9130c..e593c40ba7b 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -105,8 +105,9 @@ extern u64 cifs_UnixTimeToNT(struct timespec); extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset); -extern struct cifsFileInfo *cifs_new_fileinfo(__u16 fileHandle, struct file *file, - struct tcon_link *tlink, __u32 oplock); +extern struct cifsFileInfo *cifs_new_fileinfo(__u16 fileHandle, + struct file *file, struct tcon_link *tlink, + __u32 oplock); extern int cifs_posix_open(char *full_path, struct inode **pinode, struct super_block *sb, int mode, unsigned int f_flags, -- cgit v1.2.3-18-g5258 From 4477288a103631980750c86547d1fd54bfd2ba7d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Oct 2010 15:34:03 -0400 Subject: cifs: convert GlobalSMBSeslock from a rwlock to regular spinlock Convert this lock to a regular spinlock A rwlock_t offers little value here. It's more expensive than a regular spinlock unless you have a fairly large section of code that runs under the read lock and can benefit from the concurrency. Additionally, we need to ensure that the refcounting for files isn't racy and to do that we need to lock areas that can increment it for write. That means that the areas that can actually use a read_lock are very few and relatively infrequently used. While we're at it, change the name to something easier to type, and fix a bug in find_writable_file. cifsFileInfo_put can sleep and shouldn't be called while holding the lock. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 2 +- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifssmb.c | 4 ++-- fs/cifs/file.c | 54 +++++++++++++++++++++++++++--------------------------- fs/cifs/misc.c | 8 ++++---- fs/cifs/readdir.c | 6 +++--- 6 files changed, 38 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index cbd468c880c..f1d9c71e807 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -940,8 +940,8 @@ init_cifs(void) GlobalTotalActiveXid = 0; GlobalMaxActiveXid = 0; memset(Local_System_Name, 0, 15); - rwlock_init(&GlobalSMBSeslock); rwlock_init(&cifs_tcp_ses_lock); + spin_lock_init(&cifs_file_list_lock); spin_lock_init(&GlobalMid_Lock); if (cifs_max_pending < 2) { diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9f99afab9a0..53899a8d7c4 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -720,7 +720,7 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; * If cifs_tcp_ses_lock and the lock below are both needed to be held, then * the cifs_tcp_ses_lock must be grabbed first and released last. */ -GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; +GLOBAL_EXTERN spinlock_t cifs_file_list_lock; /* Outstanding dir notify requests */ GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a420c7b0852..bfb59a68e4f 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -91,13 +91,13 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon) struct list_head *tmp1; /* list all files open on tree connection and mark them invalid */ - write_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { open_file = list_entry(tmp, struct cifsFileInfo, tlist); open_file->invalidHandle = true; open_file->oplock_break_cancelled = true; } - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); /* BB Add call to invalidate_inodes(sb) for all superblocks mounted to this tcon */ } diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 293e9b76762..26048dc9069 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -246,14 +246,14 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, atomic_set(&pCifsFile->count, 1); INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); - write_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList)); /* if readable file instance put first in list*/ if (file->f_mode & FMODE_READ) list_add(&pCifsFile->flist, &pCifsInode->openFileList); else list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList); - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = true; @@ -607,13 +607,13 @@ int cifs_close(struct inode *inode, struct file *file) pTcon = tlink_tcon(pSMBFile->tlink); if (pSMBFile) { struct cifsLockInfo *li, *tmp; - write_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); pSMBFile->closePend = true; if (pTcon) { /* no sense reconnecting to close a file that is already closed */ if (!pTcon->need_reconnect) { - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); timeout = 2; while ((atomic_read(&pSMBFile->count) != 1) && (timeout <= 2048)) { @@ -633,9 +633,9 @@ int cifs_close(struct inode *inode, struct file *file) rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid); } else - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); } else - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); /* Delete any outstanding lock records. We'll lose them when the file is closed anyway. */ @@ -646,16 +646,16 @@ int cifs_close(struct inode *inode, struct file *file) } mutex_unlock(&pSMBFile->lock_mutex); - write_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); list_del(&pSMBFile->flist); list_del(&pSMBFile->tlist); - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); cifsFileInfo_put(file->private_data); file->private_data = NULL; } else rc = -EBADF; - read_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); if (list_empty(&(CIFS_I(inode)->openFileList))) { cFYI(1, "closing last open instance for inode %p", inode); /* if the file is not open we do not know if we can cache info @@ -663,7 +663,7 @@ int cifs_close(struct inode *inode, struct file *file) CIFS_I(inode)->clientCanCacheRead = false; CIFS_I(inode)->clientCanCacheAll = false; } - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); if ((rc == 0) && CIFS_I(inode)->write_behind_rc) rc = CIFS_I(inode)->write_behind_rc; FreeXid(xid); @@ -685,18 +685,18 @@ int cifs_closedir(struct inode *inode, struct file *file) struct cifsTconInfo *pTcon = tlink_tcon(pCFileStruct->tlink); cFYI(1, "Freeing private data in close dir"); - write_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); if (!pCFileStruct->srch_inf.endOfSearch && !pCFileStruct->invalidHandle) { pCFileStruct->invalidHandle = true; - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid); cFYI(1, "Closing uncompleted readdir with rc %d", rc); /* not much we can do if it fails anyway, ignore rc */ rc = 0; } else - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); ptmp = pCFileStruct->srch_inf.ntwrk_buf_start; if (ptmp) { cFYI(1, "closedir free smb buf in srch struct"); @@ -1182,7 +1182,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) fsuid_only = false; - read_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); /* we could simply get the first_list_entry since write-only entries are always at the end of the list but since the first entry might have a close pending, we go through the whole list */ @@ -1196,7 +1196,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, /* found a good file */ /* lock it so it will not be closed on us */ cifsFileInfo_get(open_file); - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); return open_file; } /* else might as well continue, and look for another, or simply have the caller reopen it @@ -1204,7 +1204,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, } else /* write only file */ break; /* write only files are last so must be done */ } - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); return NULL; } #endif @@ -1231,7 +1231,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) fsuid_only = false; - read_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); refind_writable: list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (open_file->closePend) @@ -1245,11 +1245,11 @@ refind_writable: if (!open_file->invalidHandle) { /* found a good writable file */ - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); return open_file; } - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); /* Had to unlock since following call can block */ rc = cifs_reopen_file(open_file, false); if (!rc) { @@ -1257,7 +1257,7 @@ refind_writable: return open_file; else { /* start over in case this was deleted */ /* since the list could be modified */ - read_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); cifsFileInfo_put(open_file); goto refind_writable; } @@ -1271,7 +1271,7 @@ refind_writable: to hold up writepages here (rather than in caller) with continuous retries */ cFYI(1, "wp failed on reopen file"); - read_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); /* can not use this handle, no write pending on this one after all */ cifsFileInfo_put(open_file); @@ -1292,7 +1292,7 @@ refind_writable: any_available = true; goto refind_writable; } - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); return NULL; } @@ -2200,16 +2200,16 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file; - read_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (open_file->closePend) continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); return 1; } } - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); return 0; } @@ -2373,8 +2373,8 @@ void cifs_oplock_break(struct work_struct *work) * finished grabbing reference for us. Make sure it's done by * waiting for GlobalSMSSeslock. */ - write_lock(&GlobalSMBSeslock); - write_unlock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); + spin_unlock(&cifs_file_list_lock); cifs_oplock_break_put(cfile); } diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 9bac3e74b31..de6073cccd9 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -560,7 +560,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) continue; cifs_stats_inc(&tcon->num_oplock_brks); - read_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); list_for_each(tmp2, &tcon->openFileList) { netfile = list_entry(tmp2, struct cifsFileInfo, tlist); @@ -572,7 +572,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) * closed anyway. */ if (netfile->closePend) { - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); read_unlock(&cifs_tcp_ses_lock); return true; } @@ -594,11 +594,11 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) cifs_oplock_break_get(netfile); netfile->oplock_break_cancelled = false; - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); read_unlock(&cifs_tcp_ses_lock); return true; } - read_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); read_unlock(&cifs_tcp_ses_lock); cFYI(1, "No matching file for oplock break"); return true; diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 6f3d13ff947..ef7bb7b50f5 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -528,14 +528,14 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, (index_to_find < first_entry_in_buffer)) { /* close and restart search */ cFYI(1, "search backing up - close and restart search"); - write_lock(&GlobalSMBSeslock); + spin_lock(&cifs_file_list_lock); if (!cifsFile->srch_inf.endOfSearch && !cifsFile->invalidHandle) { cifsFile->invalidHandle = true; - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); CIFSFindClose(xid, pTcon, cifsFile->netfid); } else - write_unlock(&GlobalSMBSeslock); + spin_unlock(&cifs_file_list_lock); if (cifsFile->srch_inf.ntwrk_buf_start) { cFYI(1, "freeing SMB ff cache buf on search rewind"); if (cifsFile->srch_inf.smallBuf) -- cgit v1.2.3-18-g5258 From b33879aa834ebe03ced3dca4e3b822bd8894a474 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Oct 2010 15:34:04 -0400 Subject: cifs: move cifsFileInfo_put to file.c ...and make it non-inlined in preparation for the move of most of cifs_close to it. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 10 +--------- fs/cifs/file.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 53899a8d7c4..18ee0adda30 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -408,15 +408,7 @@ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file) atomic_inc(&cifs_file->count); } -/* Release a reference on the file private data */ -static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file) -{ - if (atomic_dec_and_test(&cifs_file->count)) { - cifs_put_tlink(cifs_file->tlink); - dput(cifs_file->dentry); - kfree(cifs_file); - } -} +void cifsFileInfo_put(struct cifsFileInfo *cifs_file); /* * One of these for each file inode diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 26048dc9069..a3634e43bd4 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -266,6 +266,16 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, return pCifsFile; } +/* Release a reference on the file private data */ +void cifsFileInfo_put(struct cifsFileInfo *cifs_file) +{ + if (atomic_dec_and_test(&cifs_file->count)) { + cifs_put_tlink(cifs_file->tlink); + dput(cifs_file->dentry); + kfree(cifs_file); + } +} + int cifs_open(struct inode *inode, struct file *file) { int rc = -EACCES; -- cgit v1.2.3-18-g5258 From 33027af637da3f69bd17488cc3e68493c9052a7d Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Sat, 16 Oct 2010 15:19:22 +0200 Subject: GFS2: fixed typo Signed-off-by: Andrea Gelmini Signed-off-by: Steven Whitehouse --- fs/gfs2/glock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index 2bda1911b15..db1c26d6d22 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -215,7 +215,7 @@ void gfs2_glock_dq_uninit_m(unsigned int num_gh, struct gfs2_holder *ghs); void gfs2_print_dbg(struct seq_file *seq, const char *fmt, ...); /** - * gfs2_glock_nq_init - intialize a holder and enqueue it on a glock + * gfs2_glock_nq_init - initialize a holder and enqueue it on a glock * @gl: the glock * @state: the state we're requesting * @flags: the modifier flags -- cgit v1.2.3-18-g5258 From 3ae4c9deb30a8d5ee305b461625dcb298c9804a9 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 24 Aug 2010 12:01:50 +1000 Subject: xfs: use range primitives for xfs page cache operations While XFS passes ranges to operate on from the core code, the functions being called ignore the either the entire range or the end of the range. This is historical because when the function were written linux didn't have the necessary range operations. Update the functions to use the correct operations. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/linux-2.6/xfs_fs_subr.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_fs_subr.c b/fs/xfs/linux-2.6/xfs_fs_subr.c index 1f279b012f9..ed88ed16811 100644 --- a/fs/xfs/linux-2.6/xfs_fs_subr.c +++ b/fs/xfs/linux-2.6/xfs_fs_subr.c @@ -32,10 +32,9 @@ xfs_tosspages( xfs_off_t last, int fiopt) { - struct address_space *mapping = VFS_I(ip)->i_mapping; - - if (mapping->nrpages) - truncate_inode_pages(mapping, first); + /* can't toss partial tail pages, so mask them out */ + last &= ~(PAGE_SIZE - 1); + truncate_inode_pages_range(VFS_I(ip)->i_mapping, first, last - 1); } int @@ -50,12 +49,11 @@ xfs_flushinval_pages( trace_xfs_pagecache_inval(ip, first, last); - if (mapping->nrpages) { - xfs_iflags_clear(ip, XFS_ITRUNCATED); - ret = filemap_write_and_wait(mapping); - if (!ret) - truncate_inode_pages(mapping, first); - } + xfs_iflags_clear(ip, XFS_ITRUNCATED); + ret = filemap_write_and_wait_range(mapping, first, + last == -1 ? LLONG_MAX : last); + if (!ret) + truncate_inode_pages_range(mapping, first, last); return -ret; } @@ -71,10 +69,9 @@ xfs_flush_pages( int ret = 0; int ret2; - if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) { - xfs_iflags_clear(ip, XFS_ITRUNCATED); - ret = -filemap_fdatawrite(mapping); - } + xfs_iflags_clear(ip, XFS_ITRUNCATED); + ret = -filemap_fdatawrite_range(mapping, first, + last == -1 ? LLONG_MAX : last); if (flags & XBF_ASYNC) return ret; ret2 = xfs_wait_on_pages(ip, first, last); @@ -91,7 +88,9 @@ xfs_wait_on_pages( { struct address_space *mapping = VFS_I(ip)->i_mapping; - if (mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK)) - return -filemap_fdatawait(mapping); + if (mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK)) { + return -filemap_fdatawait_range(mapping, first, + last == -1 ? ip->i_size - 1 : last); + } return 0; } -- cgit v1.2.3-18-g5258 From 447223520520b17d3b6d0631aa4838fbaf8eddb4 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 24 Aug 2010 12:02:11 +1000 Subject: xfs: Introduce XFS_IOC_ZERO_RANGE XFS_IOC_ZERO_RANGE is the equivalent of an atomic XFS_IOC_UNRESVSP/ XFS_IOC_RESVSP call pair. It enabled ranges of written data to be turned into zeroes without requiring IO or having to free and reallocate the extents in the range given as would occur if we had to punch and then preallocate them separately. This enables applications to zero parts of files very quickly without changing the layout of the files in any way. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/linux-2.6/xfs_ioctl.c | 3 ++- fs/xfs/linux-2.6/xfs_ioctl32.c | 1 + fs/xfs/xfs_bmap.c | 12 +++++++++--- fs/xfs/xfs_bmap.h | 9 ++++++--- fs/xfs/xfs_fs.h | 1 + fs/xfs/xfs_vnodeops.c | 10 ++++++++-- 6 files changed, 27 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index 3b9e626f7cd..03aa908a9cb 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -1301,7 +1301,8 @@ xfs_file_ioctl( case XFS_IOC_ALLOCSP64: case XFS_IOC_FREESP64: case XFS_IOC_RESVSP64: - case XFS_IOC_UNRESVSP64: { + case XFS_IOC_UNRESVSP64: + case XFS_IOC_ZERO_RANGE: { xfs_flock64_t bf; if (copy_from_user(&bf, arg, sizeof(bf))) diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index 6c83f7f62dc..464bcc76e98 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -574,6 +574,7 @@ xfs_file_compat_ioctl( case XFS_IOC_FSGEOMETRY_V1: case XFS_IOC_FSGROWFSDATA: case XFS_IOC_FSGROWFSRT: + case XFS_IOC_ZERO_RANGE: return xfs_file_ioctl(filp, cmd, p); #else case XFS_IOC_ALLOCSP_32: diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index f90dadd5a96..5e33b7862d4 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -4744,8 +4744,12 @@ xfs_bmapi( * Check if writing previously allocated but * unwritten extents. */ - if (wr && mval->br_state == XFS_EXT_UNWRITTEN && - ((flags & (XFS_BMAPI_PREALLOC|XFS_BMAPI_DELAY)) == 0)) { + if (wr && + ((mval->br_state == XFS_EXT_UNWRITTEN && + ((flags & (XFS_BMAPI_PREALLOC|XFS_BMAPI_DELAY)) == 0)) || + (mval->br_state == XFS_EXT_NORM && + ((flags & (XFS_BMAPI_PREALLOC|XFS_BMAPI_CONVERT)) == + (XFS_BMAPI_PREALLOC|XFS_BMAPI_CONVERT))))) { /* * Modify (by adding) the state flag, if writing. */ @@ -4757,7 +4761,9 @@ xfs_bmapi( *firstblock; cur->bc_private.b.flist = flist; } - mval->br_state = XFS_EXT_NORM; + mval->br_state = (mval->br_state == XFS_EXT_UNWRITTEN) + ? XFS_EXT_NORM + : XFS_EXT_UNWRITTEN; error = xfs_bmap_add_extent(ip, lastx, &cur, mval, firstblock, flist, &tmp_logflags, whichfork, (flags & XFS_BMAPI_RSVBLOCKS)); diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h index b13569a6179..71ec9b6ecdf 100644 --- a/fs/xfs/xfs_bmap.h +++ b/fs/xfs/xfs_bmap.h @@ -74,9 +74,12 @@ typedef struct xfs_bmap_free #define XFS_BMAPI_IGSTATE 0x080 /* Ignore state - */ /* combine contig. space */ #define XFS_BMAPI_CONTIG 0x100 /* must allocate only one extent */ -#define XFS_BMAPI_CONVERT 0x200 /* unwritten extent conversion - */ - /* need write cache flushing and no */ - /* additional allocation alignments */ +/* + * unwritten extent conversion - this needs write cache flushing and no additional + * allocation alignments. When specified with XFS_BMAPI_PREALLOC it converts + * from written to unwritten, otherwise convert from unwritten to written. + */ +#define XFS_BMAPI_CONVERT 0x200 #define XFS_BMAPI_FLAGS \ { XFS_BMAPI_WRITE, "WRITE" }, \ diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h index 87c2e9d0228..10f6093671b 100644 --- a/fs/xfs/xfs_fs.h +++ b/fs/xfs/xfs_fs.h @@ -448,6 +448,7 @@ typedef struct xfs_handle { /* XFS_IOC_SETBIOSIZE ---- deprecated 46 */ /* XFS_IOC_GETBIOSIZE ---- deprecated 47 */ #define XFS_IOC_GETBMAPX _IOWR('X', 56, struct getbmap) +#define XFS_IOC_ZERO_RANGE _IOW ('X', 57, struct xfs_flock64) /* * ioctl commands that replace IRIX syssgi()'s diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 4c7c7bfb2b2..dc6e4fb8bbc 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -2272,7 +2272,7 @@ xfs_alloc_file_space( count = len; imapp = &imaps[0]; nimaps = 1; - bmapi_flag = XFS_BMAPI_WRITE | (alloc_type ? XFS_BMAPI_PREALLOC : 0); + bmapi_flag = XFS_BMAPI_WRITE | alloc_type; startoffset_fsb = XFS_B_TO_FSBT(mp, offset); allocatesize_fsb = XFS_B_TO_FSB(mp, count); @@ -2711,6 +2711,7 @@ xfs_change_file_space( xfs_off_t llen; xfs_trans_t *tp; struct iattr iattr; + int prealloc_type; if (!S_ISREG(ip->i_d.di_mode)) return XFS_ERROR(EINVAL); @@ -2753,12 +2754,17 @@ xfs_change_file_space( * size to be changed. */ setprealloc = clrprealloc = 0; + prealloc_type = XFS_BMAPI_PREALLOC; switch (cmd) { + case XFS_IOC_ZERO_RANGE: + prealloc_type |= XFS_BMAPI_CONVERT; + xfs_tosspages(ip, startoffset, startoffset + bf->l_len, 0); + /* FALLTHRU */ case XFS_IOC_RESVSP: case XFS_IOC_RESVSP64: error = xfs_alloc_file_space(ip, startoffset, bf->l_len, - 1, attr_flags); + prealloc_type, attr_flags); if (error) return error; setprealloc = 1; -- cgit v1.2.3-18-g5258 From 52fda114249578311776b25da9f73a9c34f4fd8c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 6 Sep 2010 01:44:22 +0000 Subject: xfs: simplify xfs_qm_dqusage_adjust There is no need to have the users and group/project quota locked at the same time. Get rid of xfs_qm_dqget_noattach and just do a xfs_qm_dqget inside xfs_qm_quotacheck_dqadjust for the quota we are operating on right now. The new version of xfs_qm_quotacheck_dqadjust holds the inode lock over it's operations, which is not a problem as it simply increments counters and there is no concern about log contention during mount time. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/quota/xfs_qm.c | 203 +++++++++++++++----------------------------------- 1 file changed, 61 insertions(+), 142 deletions(-) (limited to 'fs') diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 9a92407109a..121f632213a 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -1199,87 +1199,6 @@ xfs_qm_list_destroy( mutex_destroy(&(list->qh_lock)); } - -/* - * Stripped down version of dqattach. This doesn't attach, or even look at the - * dquots attached to the inode. The rationale is that there won't be any - * attached at the time this is called from quotacheck. - */ -STATIC int -xfs_qm_dqget_noattach( - xfs_inode_t *ip, - xfs_dquot_t **O_udqpp, - xfs_dquot_t **O_gdqpp) -{ - int error; - xfs_mount_t *mp; - xfs_dquot_t *udqp, *gdqp; - - ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); - mp = ip->i_mount; - udqp = NULL; - gdqp = NULL; - - if (XFS_IS_UQUOTA_ON(mp)) { - ASSERT(ip->i_udquot == NULL); - /* - * We want the dquot allocated if it doesn't exist. - */ - if ((error = xfs_qm_dqget(mp, ip, ip->i_d.di_uid, XFS_DQ_USER, - XFS_QMOPT_DQALLOC | XFS_QMOPT_DOWARN, - &udqp))) { - /* - * Shouldn't be able to turn off quotas here. - */ - ASSERT(error != ESRCH); - ASSERT(error != ENOENT); - return error; - } - ASSERT(udqp); - } - - if (XFS_IS_OQUOTA_ON(mp)) { - ASSERT(ip->i_gdquot == NULL); - if (udqp) - xfs_dqunlock(udqp); - error = XFS_IS_GQUOTA_ON(mp) ? - xfs_qm_dqget(mp, ip, - ip->i_d.di_gid, XFS_DQ_GROUP, - XFS_QMOPT_DQALLOC|XFS_QMOPT_DOWARN, - &gdqp) : - xfs_qm_dqget(mp, ip, - ip->i_d.di_projid, XFS_DQ_PROJ, - XFS_QMOPT_DQALLOC|XFS_QMOPT_DOWARN, - &gdqp); - if (error) { - if (udqp) - xfs_qm_dqrele(udqp); - ASSERT(error != ESRCH); - ASSERT(error != ENOENT); - return error; - } - ASSERT(gdqp); - - /* Reacquire the locks in the right order */ - if (udqp) { - if (! xfs_qm_dqlock_nowait(udqp)) { - xfs_dqunlock(gdqp); - xfs_dqlock(udqp); - xfs_dqlock(gdqp); - } - } - } - - *O_udqpp = udqp; - *O_gdqpp = gdqp; - -#ifdef QUOTADEBUG - if (udqp) ASSERT(XFS_DQ_IS_LOCKED(udqp)); - if (gdqp) ASSERT(XFS_DQ_IS_LOCKED(gdqp)); -#endif - return 0; -} - /* * Create an inode and return with a reference already taken, but unlocked * This is how we create quota inodes @@ -1546,18 +1465,34 @@ xfs_qm_dqiterate( /* * Called by dqusage_adjust in doing a quotacheck. - * Given the inode, and a dquot (either USR or GRP, doesn't matter), - * this updates its incore copy as well as the buffer copy. This is - * so that once the quotacheck is done, we can just log all the buffers, - * as opposed to logging numerous updates to individual dquots. + * + * Given the inode, and a dquot id this updates both the incore dqout as well + * as the buffer copy. This is so that once the quotacheck is done, we can + * just log all the buffers, as opposed to logging numerous updates to + * individual dquots. */ -STATIC void +STATIC int xfs_qm_quotacheck_dqadjust( - xfs_dquot_t *dqp, + struct xfs_inode *ip, + xfs_dqid_t id, + uint type, xfs_qcnt_t nblks, xfs_qcnt_t rtblks) { - ASSERT(XFS_DQ_IS_LOCKED(dqp)); + struct xfs_mount *mp = ip->i_mount; + struct xfs_dquot *dqp; + int error; + + error = xfs_qm_dqget(mp, ip, id, type, + XFS_QMOPT_DQALLOC | XFS_QMOPT_DOWARN, &dqp); + if (error) { + /* + * Shouldn't be able to turn off quotas here. + */ + ASSERT(error != ESRCH); + ASSERT(error != ENOENT); + return error; + } trace_xfs_dqadjust(dqp); @@ -1582,11 +1517,13 @@ xfs_qm_quotacheck_dqadjust( * There are no timers for the default values set in the root dquot. */ if (dqp->q_core.d_id) { - xfs_qm_adjust_dqlimits(dqp->q_mount, &dqp->q_core); - xfs_qm_adjust_dqtimers(dqp->q_mount, &dqp->q_core); + xfs_qm_adjust_dqlimits(mp, &dqp->q_core); + xfs_qm_adjust_dqtimers(mp, &dqp->q_core); } dqp->dq_flags |= XFS_DQ_DIRTY; + xfs_qm_dqput(dqp); + return 0; } STATIC int @@ -1629,8 +1566,7 @@ xfs_qm_dqusage_adjust( int *res) /* result code value */ { xfs_inode_t *ip; - xfs_dquot_t *udqp, *gdqp; - xfs_qcnt_t nblks, rtblks; + xfs_qcnt_t nblks, rtblks = 0; int error; ASSERT(XFS_IS_QUOTA_RUNNING(mp)); @@ -1650,51 +1586,24 @@ xfs_qm_dqusage_adjust( * the case in all other instances. It's OK that we do this because * quotacheck is done only at mount time. */ - if ((error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_EXCL, &ip))) { + error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_EXCL, &ip); + if (error) { *res = BULKSTAT_RV_NOTHING; return error; } - /* - * Obtain the locked dquots. In case of an error (eg. allocation - * fails for ENOSPC), we return the negative of the error number - * to bulkstat, so that it can get propagated to quotacheck() and - * making us disable quotas for the file system. - */ - if ((error = xfs_qm_dqget_noattach(ip, &udqp, &gdqp))) { - xfs_iunlock(ip, XFS_ILOCK_EXCL); - IRELE(ip); - *res = BULKSTAT_RV_GIVEUP; - return error; - } + ASSERT(ip->i_delayed_blks == 0); - rtblks = 0; - if (! XFS_IS_REALTIME_INODE(ip)) { - nblks = (xfs_qcnt_t)ip->i_d.di_nblocks; - } else { + if (XFS_IS_REALTIME_INODE(ip)) { /* * Walk thru the extent list and count the realtime blocks. */ - if ((error = xfs_qm_get_rtblks(ip, &rtblks))) { - xfs_iunlock(ip, XFS_ILOCK_EXCL); - IRELE(ip); - if (udqp) - xfs_qm_dqput(udqp); - if (gdqp) - xfs_qm_dqput(gdqp); - *res = BULKSTAT_RV_GIVEUP; - return error; - } - nblks = (xfs_qcnt_t)ip->i_d.di_nblocks - rtblks; + error = xfs_qm_get_rtblks(ip, &rtblks); + if (error) + goto error0; } - ASSERT(ip->i_delayed_blks == 0); - /* - * We can't release the inode while holding its dquot locks. - * The inode can go into inactive and might try to acquire the dquotlocks. - * So, just unlock here and do a vn_rele at the end. - */ - xfs_iunlock(ip, XFS_ILOCK_EXCL); + nblks = (xfs_qcnt_t)ip->i_d.di_nblocks - rtblks; /* * Add the (disk blocks and inode) resources occupied by this @@ -1709,26 +1618,36 @@ xfs_qm_dqusage_adjust( * and quotaoffs don't race. (Quotachecks happen at mount time only). */ if (XFS_IS_UQUOTA_ON(mp)) { - ASSERT(udqp); - xfs_qm_quotacheck_dqadjust(udqp, nblks, rtblks); - xfs_qm_dqput(udqp); + error = xfs_qm_quotacheck_dqadjust(ip, ip->i_d.di_uid, + XFS_DQ_USER, nblks, rtblks); + if (error) + goto error0; } - if (XFS_IS_OQUOTA_ON(mp)) { - ASSERT(gdqp); - xfs_qm_quotacheck_dqadjust(gdqp, nblks, rtblks); - xfs_qm_dqput(gdqp); + + if (XFS_IS_GQUOTA_ON(mp)) { + error = xfs_qm_quotacheck_dqadjust(ip, ip->i_d.di_gid, + XFS_DQ_GROUP, nblks, rtblks); + if (error) + goto error0; } - /* - * Now release the inode. This will send it to 'inactive', and - * possibly even free blocks. - */ - IRELE(ip); - /* - * Goto next inode. - */ + if (XFS_IS_PQUOTA_ON(mp)) { + error = xfs_qm_quotacheck_dqadjust(ip, ip->i_d.di_projid, + XFS_DQ_PROJ, nblks, rtblks); + if (error) + goto error0; + } + + xfs_iunlock(ip, XFS_ILOCK_EXCL); + IRELE(ip); *res = BULKSTAT_RV_DIDONE; return 0; + +error0: + xfs_iunlock(ip, XFS_ILOCK_EXCL); + IRELE(ip); + *res = BULKSTAT_RV_GIVEUP; + return error; } /* -- cgit v1.2.3-18-g5258 From acecf1b5d8a846bf818bf74df454330f0b444b0a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 6 Sep 2010 01:44:45 +0000 Subject: xfs: stop using xfs_qm_dqtobp in xfs_qm_dqflush In xfs_qm_dqflush we know that q_blkno must be initialized already from a previous xfs_qm_dqread. So instead of calling xfs_qm_dqtobp we can simply read the quota buffer directly. This also saves us from a duplicate xfs_qm_dqcheck call check and allows xfs_qm_dqtobp to be simplified now that it is always called for a newly initialized inode. In addition to that properly unwind all locks in xfs_qm_dqflush when xfs_qm_dqcheck fails. This mirrors a similar cleanup in the inode lookup done earlier. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/quota/xfs_dquot.c | 164 ++++++++++++++++++++++------------------------- 1 file changed, 76 insertions(+), 88 deletions(-) (limited to 'fs') diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c index e1a2f6800e0..faf8e1a83a1 100644 --- a/fs/xfs/quota/xfs_dquot.c +++ b/fs/xfs/quota/xfs_dquot.c @@ -463,87 +463,68 @@ xfs_qm_dqtobp( uint flags) { xfs_bmbt_irec_t map; - int nmaps, error; + int nmaps = 1, error; xfs_buf_t *bp; - xfs_inode_t *quotip; - xfs_mount_t *mp; + xfs_inode_t *quotip = XFS_DQ_TO_QIP(dqp); + xfs_mount_t *mp = dqp->q_mount; xfs_disk_dquot_t *ddq; - xfs_dqid_t id; - boolean_t newdquot; + xfs_dqid_t id = be32_to_cpu(dqp->q_core.d_id); xfs_trans_t *tp = (tpp ? *tpp : NULL); - mp = dqp->q_mount; - id = be32_to_cpu(dqp->q_core.d_id); - nmaps = 1; - newdquot = B_FALSE; + dqp->q_fileoffset = (xfs_fileoff_t)id / mp->m_quotainfo->qi_dqperchunk; - /* - * If we don't know where the dquot lives, find out. - */ - if (dqp->q_blkno == (xfs_daddr_t) 0) { - /* We use the id as an index */ - dqp->q_fileoffset = (xfs_fileoff_t)id / - mp->m_quotainfo->qi_dqperchunk; - nmaps = 1; - quotip = XFS_DQ_TO_QIP(dqp); - xfs_ilock(quotip, XFS_ILOCK_SHARED); + xfs_ilock(quotip, XFS_ILOCK_SHARED); + if (XFS_IS_THIS_QUOTA_OFF(dqp)) { /* - * Return if this type of quotas is turned off while we didn't - * have an inode lock + * Return if this type of quotas is turned off while we + * didn't have the quota inode lock. */ - if (XFS_IS_THIS_QUOTA_OFF(dqp)) { - xfs_iunlock(quotip, XFS_ILOCK_SHARED); - return (ESRCH); - } + xfs_iunlock(quotip, XFS_ILOCK_SHARED); + return ESRCH; + } + + /* + * Find the block map; no allocations yet + */ + error = xfs_bmapi(NULL, quotip, dqp->q_fileoffset, + XFS_DQUOT_CLUSTER_SIZE_FSB, XFS_BMAPI_METADATA, + NULL, 0, &map, &nmaps, NULL); + + xfs_iunlock(quotip, XFS_ILOCK_SHARED); + if (error) + return error; + + ASSERT(nmaps == 1); + ASSERT(map.br_blockcount == 1); + + /* + * Offset of dquot in the (fixed sized) dquot chunk. + */ + dqp->q_bufoffset = (id % mp->m_quotainfo->qi_dqperchunk) * + sizeof(xfs_dqblk_t); + + ASSERT(map.br_startblock != DELAYSTARTBLOCK); + if (map.br_startblock == HOLESTARTBLOCK) { /* - * Find the block map; no allocations yet + * We don't allocate unless we're asked to */ - error = xfs_bmapi(NULL, quotip, dqp->q_fileoffset, - XFS_DQUOT_CLUSTER_SIZE_FSB, - XFS_BMAPI_METADATA, - NULL, 0, &map, &nmaps, NULL); + if (!(flags & XFS_QMOPT_DQALLOC)) + return ENOENT; - xfs_iunlock(quotip, XFS_ILOCK_SHARED); + ASSERT(tp); + error = xfs_qm_dqalloc(tpp, mp, dqp, quotip, + dqp->q_fileoffset, &bp); if (error) - return (error); - ASSERT(nmaps == 1); - ASSERT(map.br_blockcount == 1); + return error; + tp = *tpp; + } else { + trace_xfs_dqtobp_read(dqp); /* - * offset of dquot in the (fixed sized) dquot chunk. + * store the blkno etc so that we don't have to do the + * mapping all the time */ - dqp->q_bufoffset = (id % mp->m_quotainfo->qi_dqperchunk) * - sizeof(xfs_dqblk_t); - if (map.br_startblock == HOLESTARTBLOCK) { - /* - * We don't allocate unless we're asked to - */ - if (!(flags & XFS_QMOPT_DQALLOC)) - return (ENOENT); - - ASSERT(tp); - if ((error = xfs_qm_dqalloc(tpp, mp, dqp, quotip, - dqp->q_fileoffset, &bp))) - return (error); - tp = *tpp; - newdquot = B_TRUE; - } else { - /* - * store the blkno etc so that we don't have to do the - * mapping all the time - */ - dqp->q_blkno = XFS_FSB_TO_DADDR(mp, map.br_startblock); - } - } - ASSERT(dqp->q_blkno != DELAYSTARTBLOCK); - ASSERT(dqp->q_blkno != HOLESTARTBLOCK); - - /* - * Read in the buffer, unless we've just done the allocation - * (in which case we already have the buf). - */ - if (!newdquot) { - trace_xfs_dqtobp_read(dqp); + dqp->q_blkno = XFS_FSB_TO_DADDR(mp, map.br_startblock); error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, dqp->q_blkno, @@ -552,13 +533,14 @@ xfs_qm_dqtobp( if (error || !bp) return XFS_ERROR(error); } + ASSERT(XFS_BUF_ISBUSY(bp)); ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); /* * calculate the location of the dquot inside the buffer. */ - ddq = (xfs_disk_dquot_t *)((char *)XFS_BUF_PTR(bp) + dqp->q_bufoffset); + ddq = (struct xfs_disk_dquot *)(XFS_BUF_PTR(bp) + dqp->q_bufoffset); /* * A simple sanity check in case we got a corrupted dquot... @@ -1176,18 +1158,18 @@ xfs_qm_dqflush( xfs_dquot_t *dqp, uint flags) { - xfs_mount_t *mp; - xfs_buf_t *bp; - xfs_disk_dquot_t *ddqp; + struct xfs_mount *mp = dqp->q_mount; + struct xfs_buf *bp; + struct xfs_disk_dquot *ddqp; int error; ASSERT(XFS_DQ_IS_LOCKED(dqp)); ASSERT(!completion_done(&dqp->q_flush)); + trace_xfs_dqflush(dqp); /* - * If not dirty, or it's pinned and we are not supposed to - * block, nada. + * If not dirty, or it's pinned and we are not supposed to block, nada. */ if (!XFS_DQ_IS_DIRTY(dqp) || (!(flags & SYNC_WAIT) && atomic_read(&dqp->q_pincount) > 0)) { @@ -1201,40 +1183,46 @@ xfs_qm_dqflush( * down forcibly. If that's the case we must not write this dquot * to disk, because the log record didn't make it to disk! */ - if (XFS_FORCED_SHUTDOWN(dqp->q_mount)) { - dqp->dq_flags &= ~(XFS_DQ_DIRTY); + if (XFS_FORCED_SHUTDOWN(mp)) { + dqp->dq_flags &= ~XFS_DQ_DIRTY; xfs_dqfunlock(dqp); return XFS_ERROR(EIO); } /* * Get the buffer containing the on-disk dquot - * We don't need a transaction envelope because we know that the - * the ondisk-dquot has already been allocated for. */ - if ((error = xfs_qm_dqtobp(NULL, dqp, &ddqp, &bp, XFS_QMOPT_DOWARN))) { + error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, dqp->q_blkno, + mp->m_quotainfo->qi_dqchunklen, 0, &bp); + if (error) { ASSERT(error != ENOENT); - /* - * Quotas could have gotten turned off (ESRCH) - */ xfs_dqfunlock(dqp); - return (error); + return error; } - if (xfs_qm_dqcheck(&dqp->q_core, be32_to_cpu(ddqp->d_id), - 0, XFS_QMOPT_DOWARN, "dqflush (incore copy)")) { - xfs_force_shutdown(dqp->q_mount, SHUTDOWN_CORRUPT_INCORE); + /* + * Calculate the location of the dquot inside the buffer. + */ + ddqp = (struct xfs_disk_dquot *)(XFS_BUF_PTR(bp) + dqp->q_bufoffset); + + /* + * A simple sanity check in case we got a corrupted dquot.. + */ + if (xfs_qm_dqcheck(&dqp->q_core, be32_to_cpu(ddqp->d_id), 0, + XFS_QMOPT_DOWARN, "dqflush (incore copy)")) { + xfs_buf_relse(bp); + xfs_dqfunlock(dqp); + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); return XFS_ERROR(EIO); } /* This is the only portion of data that needs to persist */ - memcpy(ddqp, &(dqp->q_core), sizeof(xfs_disk_dquot_t)); + memcpy(ddqp, &dqp->q_core, sizeof(xfs_disk_dquot_t)); /* * Clear the dirty field and remember the flush lsn for later use. */ - dqp->dq_flags &= ~(XFS_DQ_DIRTY); - mp = dqp->q_mount; + dqp->dq_flags &= ~XFS_DQ_DIRTY; xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_lsn, &dqp->q_logitem.qli_item.li_lsn); -- cgit v1.2.3-18-g5258 From c0e59e1ac0a106bbab93404024bb6e7927ad9d6d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 7 Sep 2010 23:34:07 +0000 Subject: xfs: remove the ->kill_root btree operation The implementation os ->kill_root only differ by either simply zeroing out the now unused buffer in the btree cursor in the inode allocation btree or using xfs_btree_setbuf in the allocation btree. Initially both of them used xfs_btree_setbuf, but the use in the ialloc btree was removed early on because it interacted badly with xfs_trans_binval. In addition to zeroing out the buffer in the cursor xfs_btree_setbuf updates the bc_ra array in the btree cursor, and calls xfs_trans_brelse on the buffer previous occupying the slot. The bc_ra update should be done for the alloc btree updated too, although the lack of it does not cause serious problems. The xfs_trans_brelse call on the other hand is effectively a no-op in the end - it keeps decrementing the bli_recur refcount until it hits zero, and then just skips out because the buffer will always be dirty at this point. So removing it for the allocation btree is just fine. So unify the code and move it to xfs_btree.c. While we're at it also replace the call to xfs_btree_setbuf with a NULL bp argument in xfs_btree_del_cursor with a direct call to xfs_trans_brelse given that the cursor is beeing freed just after this and the state updates are superflous. After this xfs_btree_setbuf is only used with a non-NULL bp argument and can thus be simplified. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_alloc_btree.c | 33 ------------------------------ fs/xfs/xfs_btree.c | 52 +++++++++++++++++++++++++++++++++++++++-------- fs/xfs/xfs_btree.h | 14 +------------ fs/xfs/xfs_ialloc_btree.c | 33 ------------------------------ 4 files changed, 44 insertions(+), 88 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c index 97f7328967f..3916925e258 100644 --- a/fs/xfs/xfs_alloc_btree.c +++ b/fs/xfs/xfs_alloc_btree.c @@ -280,38 +280,6 @@ xfs_allocbt_key_diff( return (__int64_t)be32_to_cpu(kp->ar_startblock) - rec->ar_startblock; } -STATIC int -xfs_allocbt_kill_root( - struct xfs_btree_cur *cur, - struct xfs_buf *bp, - int level, - union xfs_btree_ptr *newroot) -{ - int error; - - XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY); - XFS_BTREE_STATS_INC(cur, killroot); - - /* - * Update the root pointer, decreasing the level by 1 and then - * free the old root. - */ - xfs_allocbt_set_root(cur, newroot, -1); - error = xfs_allocbt_free_block(cur, bp); - if (error) { - XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR); - return error; - } - - XFS_BTREE_STATS_INC(cur, free); - - xfs_btree_setbuf(cur, level, NULL); - cur->bc_nlevels--; - - XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); - return 0; -} - #ifdef DEBUG STATIC int xfs_allocbt_keys_inorder( @@ -423,7 +391,6 @@ static const struct xfs_btree_ops xfs_allocbt_ops = { .dup_cursor = xfs_allocbt_dup_cursor, .set_root = xfs_allocbt_set_root, - .kill_root = xfs_allocbt_kill_root, .alloc_block = xfs_allocbt_alloc_block, .free_block = xfs_allocbt_free_block, .update_lastrec = xfs_allocbt_update_lastrec, diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c index 829af92f0fb..735dc2e671b 100644 --- a/fs/xfs/xfs_btree.c +++ b/fs/xfs/xfs_btree.c @@ -217,7 +217,7 @@ xfs_btree_del_cursor( */ for (i = 0; i < cur->bc_nlevels; i++) { if (cur->bc_bufs[i]) - xfs_btree_setbuf(cur, i, NULL); + xfs_trans_brelse(cur->bc_tp, cur->bc_bufs[i]); else if (!error) break; } @@ -763,22 +763,19 @@ xfs_btree_readahead( * Set the buffer for level "lev" in the cursor to bp, releasing * any previous buffer. */ -void +STATIC void xfs_btree_setbuf( xfs_btree_cur_t *cur, /* btree cursor */ int lev, /* level in btree */ xfs_buf_t *bp) /* new buffer to set */ { struct xfs_btree_block *b; /* btree block */ - xfs_buf_t *obp; /* old buffer pointer */ - obp = cur->bc_bufs[lev]; - if (obp) - xfs_trans_brelse(cur->bc_tp, obp); + if (cur->bc_bufs[lev]) + xfs_trans_brelse(cur->bc_tp, cur->bc_bufs[lev]); cur->bc_bufs[lev] = bp; cur->bc_ra[lev] = 0; - if (!bp) - return; + b = XFS_BUF_TO_BLOCK(bp); if (cur->bc_flags & XFS_BTREE_LONG_PTRS) { if (be64_to_cpu(b->bb_u.l.bb_leftsib) == NULLDFSBNO) @@ -3011,6 +3008,43 @@ out0: return 0; } +/* + * Kill the current root node, and replace it with it's only child node. + */ +STATIC int +xfs_btree_kill_root( + struct xfs_btree_cur *cur, + struct xfs_buf *bp, + int level, + union xfs_btree_ptr *newroot) +{ + int error; + + XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY); + XFS_BTREE_STATS_INC(cur, killroot); + + /* + * Update the root pointer, decreasing the level by 1 and then + * free the old root. + */ + cur->bc_ops->set_root(cur, newroot, -1); + + error = cur->bc_ops->free_block(cur, bp); + if (error) { + XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR); + return error; + } + + XFS_BTREE_STATS_INC(cur, free); + + cur->bc_bufs[level] = NULL; + cur->bc_ra[level] = 0; + cur->bc_nlevels--; + + XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); + return 0; +} + STATIC int xfs_btree_dec_cursor( struct xfs_btree_cur *cur, @@ -3195,7 +3229,7 @@ xfs_btree_delrec( * Make it the new root of the btree. */ pp = xfs_btree_ptr_addr(cur, 1, block); - error = cur->bc_ops->kill_root(cur, bp, level, pp); + error = xfs_btree_kill_root(cur, bp, level, pp); if (error) goto error0; } else if (level > 0) { diff --git a/fs/xfs/xfs_btree.h b/fs/xfs/xfs_btree.h index 7fa07062bdd..82fafc66bd1 100644 --- a/fs/xfs/xfs_btree.h +++ b/fs/xfs/xfs_btree.h @@ -152,9 +152,7 @@ struct xfs_btree_ops { /* update btree root pointer */ void (*set_root)(struct xfs_btree_cur *cur, - union xfs_btree_ptr *nptr, int level_change); - int (*kill_root)(struct xfs_btree_cur *cur, struct xfs_buf *bp, - int level, union xfs_btree_ptr *newroot); + union xfs_btree_ptr *nptr, int level_change); /* block allocation / freeing */ int (*alloc_block)(struct xfs_btree_cur *cur, @@ -399,16 +397,6 @@ xfs_btree_reada_bufs( xfs_agblock_t agbno, /* allocation group block number */ xfs_extlen_t count); /* count of filesystem blocks */ -/* - * Set the buffer for level "lev" in the cursor to bp, releasing - * any previous buffer. - */ -void -xfs_btree_setbuf( - xfs_btree_cur_t *cur, /* btree cursor */ - int lev, /* level in btree */ - struct xfs_buf *bp); /* new buffer to set */ - /* * Common btree core entry points. diff --git a/fs/xfs/xfs_ialloc_btree.c b/fs/xfs/xfs_ialloc_btree.c index d352862cefa..16921f55c54 100644 --- a/fs/xfs/xfs_ialloc_btree.c +++ b/fs/xfs/xfs_ialloc_btree.c @@ -183,38 +183,6 @@ xfs_inobt_key_diff( cur->bc_rec.i.ir_startino; } -STATIC int -xfs_inobt_kill_root( - struct xfs_btree_cur *cur, - struct xfs_buf *bp, - int level, - union xfs_btree_ptr *newroot) -{ - int error; - - XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY); - XFS_BTREE_STATS_INC(cur, killroot); - - /* - * Update the root pointer, decreasing the level by 1 and then - * free the old root. - */ - xfs_inobt_set_root(cur, newroot, -1); - error = xfs_inobt_free_block(cur, bp); - if (error) { - XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR); - return error; - } - - XFS_BTREE_STATS_INC(cur, free); - - cur->bc_bufs[level] = NULL; - cur->bc_nlevels--; - - XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT); - return 0; -} - #ifdef DEBUG STATIC int xfs_inobt_keys_inorder( @@ -309,7 +277,6 @@ static const struct xfs_btree_ops xfs_inobt_ops = { .dup_cursor = xfs_inobt_dup_cursor, .set_root = xfs_inobt_set_root, - .kill_root = xfs_inobt_kill_root, .alloc_block = xfs_inobt_alloc_block, .free_block = xfs_inobt_free_block, .get_minrecs = xfs_inobt_get_minrecs, -- cgit v1.2.3-18-g5258 From 9c169915ad374cd9efb1556943b2074ec07e1749 Mon Sep 17 00:00:00 2001 From: Poyo VL Date: Thu, 2 Sep 2010 07:41:55 +0000 Subject: xfs: eliminate some newly-reported gcc warnings Ionut Gabriel Popescu submitted a simple change to eliminate some "may be used uninitialized" warnings when building XFS. The reported condition seems to be something that GCC did not used to recognize or report. The warnings were produced by: gcc version 4.5.0 20100604 [gcc-4_5-branch revision 160292] (SUSE Linux) Signed-off-by: Ionut Gabriel Popescu Signed-off-by: Alex Elder --- fs/xfs/xfs_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index af168faccc7..112abc439ca 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c @@ -675,7 +675,7 @@ xfs_alloc_ag_vextent_near( xfs_agblock_t gtbnoa; /* aligned ... */ xfs_extlen_t gtdiff; /* difference to right side entry */ xfs_extlen_t gtlen; /* length of right side entry */ - xfs_extlen_t gtlena; /* aligned ... */ + xfs_extlen_t gtlena = 0; /* aligned ... */ xfs_agblock_t gtnew; /* useful start bno of right side */ int error; /* error code */ int i; /* result code, temporary */ @@ -684,7 +684,7 @@ xfs_alloc_ag_vextent_near( xfs_agblock_t ltbnoa; /* aligned ... */ xfs_extlen_t ltdiff; /* difference to left side entry */ xfs_extlen_t ltlen; /* length of left side entry */ - xfs_extlen_t ltlena; /* aligned ... */ + xfs_extlen_t ltlena = 0; /* aligned ... */ xfs_agblock_t ltnew; /* useful start bno of left side */ xfs_extlen_t rlen; /* length of returned extent */ #if defined(DEBUG) && defined(__KERNEL__) -- cgit v1.2.3-18-g5258 From d1583a3833290ab9f8b13a064acbb5e508c59f60 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 24 Sep 2010 18:14:13 +1000 Subject: xfs: reduce the number of CIL lock round trips during commit When commiting a transaction, we do a lock CIL state lock round trip on every single log vector we insert into the CIL. This is resulting in the lock being as hot as the inode and dcache locks on 8-way create workloads. Rework the insertion loops to bring the number of lock round trips to one per transaction for log vectors, and one more do the busy extents. Also change the allocation of the log vector buffer not to zero it as we copy over the entire allocated buffer anyway. This patch also includes a structural cleanup to the CIL item insertion provided by Christoph Hellwig. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/xfs_log_cil.c | 232 ++++++++++++++++++++++++++++----------------------- 1 file changed, 127 insertions(+), 105 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index 7e206fc1fa3..23d6ceb5e97 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -145,102 +145,6 @@ xlog_cil_init_post_recovery( log->l_curr_block); } -/* - * Insert the log item into the CIL and calculate the difference in space - * consumed by the item. Add the space to the checkpoint ticket and calculate - * if the change requires additional log metadata. If it does, take that space - * as well. Remove the amount of space we addded to the checkpoint ticket from - * the current transaction ticket so that the accounting works out correctly. - * - * If this is the first time the item is being placed into the CIL in this - * context, pin it so it can't be written to disk until the CIL is flushed to - * the iclog and the iclog written to disk. - */ -static void -xlog_cil_insert( - struct log *log, - struct xlog_ticket *ticket, - struct xfs_log_item *item, - struct xfs_log_vec *lv) -{ - struct xfs_cil *cil = log->l_cilp; - struct xfs_log_vec *old = lv->lv_item->li_lv; - struct xfs_cil_ctx *ctx = cil->xc_ctx; - int len; - int diff_iovecs; - int iclog_space; - - if (old) { - /* existing lv on log item, space used is a delta */ - ASSERT(!list_empty(&item->li_cil)); - ASSERT(old->lv_buf && old->lv_buf_len && old->lv_niovecs); - - len = lv->lv_buf_len - old->lv_buf_len; - diff_iovecs = lv->lv_niovecs - old->lv_niovecs; - kmem_free(old->lv_buf); - kmem_free(old); - } else { - /* new lv, must pin the log item */ - ASSERT(!lv->lv_item->li_lv); - ASSERT(list_empty(&item->li_cil)); - - len = lv->lv_buf_len; - diff_iovecs = lv->lv_niovecs; - IOP_PIN(lv->lv_item); - - } - len += diff_iovecs * sizeof(xlog_op_header_t); - - /* attach new log vector to log item */ - lv->lv_item->li_lv = lv; - - spin_lock(&cil->xc_cil_lock); - list_move_tail(&item->li_cil, &cil->xc_cil); - ctx->nvecs += diff_iovecs; - - /* - * If this is the first time the item is being committed to the CIL, - * store the sequence number on the log item so we can tell - * in future commits whether this is the first checkpoint the item is - * being committed into. - */ - if (!item->li_seq) - item->li_seq = ctx->sequence; - - /* - * Now transfer enough transaction reservation to the context ticket - * for the checkpoint. The context ticket is special - the unit - * reservation has to grow as well as the current reservation as we - * steal from tickets so we can correctly determine the space used - * during the transaction commit. - */ - if (ctx->ticket->t_curr_res == 0) { - /* first commit in checkpoint, steal the header reservation */ - ASSERT(ticket->t_curr_res >= ctx->ticket->t_unit_res + len); - ctx->ticket->t_curr_res = ctx->ticket->t_unit_res; - ticket->t_curr_res -= ctx->ticket->t_unit_res; - } - - /* do we need space for more log record headers? */ - iclog_space = log->l_iclog_size - log->l_iclog_hsize; - if (len > 0 && (ctx->space_used / iclog_space != - (ctx->space_used + len) / iclog_space)) { - int hdrs; - - hdrs = (len + iclog_space - 1) / iclog_space; - /* need to take into account split region headers, too */ - hdrs *= log->l_iclog_hsize + sizeof(struct xlog_op_header); - ctx->ticket->t_unit_res += hdrs; - ctx->ticket->t_curr_res += hdrs; - ticket->t_curr_res -= hdrs; - ASSERT(ticket->t_curr_res >= len); - } - ticket->t_curr_res -= len; - ctx->space_used += len; - - spin_unlock(&cil->xc_cil_lock); -} - /* * Format log item into a flat buffers * @@ -286,7 +190,7 @@ xlog_cil_format_items( len += lv->lv_iovecp[index].i_len; lv->lv_buf_len = len; - lv->lv_buf = kmem_zalloc(lv->lv_buf_len, KM_SLEEP|KM_NOFS); + lv->lv_buf = kmem_alloc(lv->lv_buf_len, KM_SLEEP|KM_NOFS); ptr = lv->lv_buf; for (index = 0; index < lv->lv_niovecs; index++) { @@ -300,21 +204,136 @@ xlog_cil_format_items( } } +/* + * Prepare the log item for insertion into the CIL. Calculate the difference in + * log space and vectors it will consume, and if it is a new item pin it as + * well. + */ +STATIC void +xfs_cil_prepare_item( + struct log *log, + struct xfs_log_vec *lv, + int *len, + int *diff_iovecs) +{ + struct xfs_log_vec *old = lv->lv_item->li_lv; + + if (old) { + /* existing lv on log item, space used is a delta */ + ASSERT(!list_empty(&lv->lv_item->li_cil)); + ASSERT(old->lv_buf && old->lv_buf_len && old->lv_niovecs); + + *len += lv->lv_buf_len - old->lv_buf_len; + *diff_iovecs += lv->lv_niovecs - old->lv_niovecs; + kmem_free(old->lv_buf); + kmem_free(old); + } else { + /* new lv, must pin the log item */ + ASSERT(!lv->lv_item->li_lv); + ASSERT(list_empty(&lv->lv_item->li_cil)); + + *len += lv->lv_buf_len; + *diff_iovecs += lv->lv_niovecs; + IOP_PIN(lv->lv_item); + + } + + /* attach new log vector to log item */ + lv->lv_item->li_lv = lv; + + /* + * If this is the first time the item is being committed to the + * CIL, store the sequence number on the log item so we can + * tell in future commits whether this is the first checkpoint + * the item is being committed into. + */ + if (!lv->lv_item->li_seq) + lv->lv_item->li_seq = log->l_cilp->xc_ctx->sequence; +} + +/* + * Insert the log items into the CIL and calculate the difference in space + * consumed by the item. Add the space to the checkpoint ticket and calculate + * if the change requires additional log metadata. If it does, take that space + * as well. Remove the amount of space we addded to the checkpoint ticket from + * the current transaction ticket so that the accounting works out correctly. + */ static void xlog_cil_insert_items( struct log *log, struct xfs_log_vec *log_vector, - struct xlog_ticket *ticket, - xfs_lsn_t *start_lsn) + struct xlog_ticket *ticket) { - struct xfs_log_vec *lv; - - if (start_lsn) - *start_lsn = log->l_cilp->xc_ctx->sequence; + struct xfs_cil *cil = log->l_cilp; + struct xfs_cil_ctx *ctx = cil->xc_ctx; + struct xfs_log_vec *lv; + int len = 0; + int diff_iovecs = 0; + int iclog_space; ASSERT(log_vector); + + /* + * Do all the accounting aggregation and switching of log vectors + * around in a separate loop to the insertion of items into the CIL. + * Then we can do a separate loop to update the CIL within a single + * lock/unlock pair. This reduces the number of round trips on the CIL + * lock from O(nr_logvectors) to O(1) and greatly reduces the overall + * hold time for the transaction commit. + * + * If this is the first time the item is being placed into the CIL in + * this context, pin it so it can't be written to disk until the CIL is + * flushed to the iclog and the iclog written to disk. + * + * We can do this safely because the context can't checkpoint until we + * are done so it doesn't matter exactly how we update the CIL. + */ + for (lv = log_vector; lv; lv = lv->lv_next) + xfs_cil_prepare_item(log, lv, &len, &diff_iovecs); + + /* account for space used by new iovec headers */ + len += diff_iovecs * sizeof(xlog_op_header_t); + + spin_lock(&cil->xc_cil_lock); + + /* move the items to the tail of the CIL */ for (lv = log_vector; lv; lv = lv->lv_next) - xlog_cil_insert(log, ticket, lv->lv_item, lv); + list_move_tail(&lv->lv_item->li_cil, &cil->xc_cil); + + ctx->nvecs += diff_iovecs; + + /* + * Now transfer enough transaction reservation to the context ticket + * for the checkpoint. The context ticket is special - the unit + * reservation has to grow as well as the current reservation as we + * steal from tickets so we can correctly determine the space used + * during the transaction commit. + */ + if (ctx->ticket->t_curr_res == 0) { + /* first commit in checkpoint, steal the header reservation */ + ASSERT(ticket->t_curr_res >= ctx->ticket->t_unit_res + len); + ctx->ticket->t_curr_res = ctx->ticket->t_unit_res; + ticket->t_curr_res -= ctx->ticket->t_unit_res; + } + + /* do we need space for more log record headers? */ + iclog_space = log->l_iclog_size - log->l_iclog_hsize; + if (len > 0 && (ctx->space_used / iclog_space != + (ctx->space_used + len) / iclog_space)) { + int hdrs; + + hdrs = (len + iclog_space - 1) / iclog_space; + /* need to take into account split region headers, too */ + hdrs *= log->l_iclog_hsize + sizeof(struct xlog_op_header); + ctx->ticket->t_unit_res += hdrs; + ctx->ticket->t_curr_res += hdrs; + ticket->t_curr_res -= hdrs; + ASSERT(ticket->t_curr_res >= len); + } + ticket->t_curr_res -= len; + ctx->space_used += len; + + spin_unlock(&cil->xc_cil_lock); } static void @@ -638,7 +657,10 @@ xfs_log_commit_cil( /* lock out background commit */ down_read(&log->l_cilp->xc_ctx_lock); - xlog_cil_insert_items(log, log_vector, tp->t_ticket, commit_lsn); + if (commit_lsn) + *commit_lsn = log->l_cilp->xc_ctx->sequence; + + xlog_cil_insert_items(log, log_vector, tp->t_ticket); /* check we didn't blow the reservation */ if (tp->t_ticket->t_curr_res < 0) -- cgit v1.2.3-18-g5258 From bd32d25a7cf7242512e77e70bab63df4402ab91c Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 22 Sep 2010 10:47:20 +1000 Subject: xfs: remove debug assert for per-ag reference counting When we start taking references per cached buffer to the the perag it is cached on, it will blow the current debug maximum reference count assert out of the water. The assert has never caught a bug, and we have tracing to track changes if there ever is a problem, so just remove it. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/xfs_mount.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index aeb9d72ebf6..00c7a876807 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -210,8 +210,6 @@ xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno) pag = radix_tree_lookup(&mp->m_perag_tree, agno); if (pag) { ASSERT(atomic_read(&pag->pag_ref) >= 0); - /* catch leaks in the positive direction during testing */ - ASSERT(atomic_read(&pag->pag_ref) < 1000); ref = atomic_inc_return(&pag->pag_ref); } spin_unlock(&mp->m_perag_lock); -- cgit v1.2.3-18-g5258 From e176579e70118ed7cfdb60f963628fe0ca771f3d Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 22 Sep 2010 10:47:20 +1000 Subject: xfs: lockless per-ag lookups When we start taking a reference to the per-ag for every cached buffer in the system, kernel lockstat profiling on an 8-way create workload shows the mp->m_perag_lock has higher acquisition rates than the inode lock and has significantly more contention. That is, it becomes the highest contended lock in the system. The perag lookup is trivial to convert to lock-less RCU lookups because perag structures never go away. Hence the only thing we need to protect against is tree structure changes during a grow. This can be done simply by replacing the locking in xfs_perag_get() with RCU read locking. This removes the mp->m_perag_lock completely from this path. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_sync.c | 6 +++--- fs/xfs/xfs_ag.h | 3 +++ fs/xfs/xfs_mount.c | 25 +++++++++++++++++-------- 3 files changed, 23 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 81976ffed7d..3a1d229b478 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -150,17 +150,17 @@ xfs_inode_ag_iter_next_pag( int found; int ref; - spin_lock(&mp->m_perag_lock); + rcu_read_lock(); found = radix_tree_gang_lookup_tag(&mp->m_perag_tree, (void **)&pag, *first, 1, tag); if (found <= 0) { - spin_unlock(&mp->m_perag_lock); + rcu_read_unlock(); return NULL; } *first = pag->pag_agno + 1; /* open coded pag reference increment */ ref = atomic_inc_return(&pag->pag_ref); - spin_unlock(&mp->m_perag_lock); + rcu_read_unlock(); trace_xfs_perag_get_reclaim(mp, pag->pag_agno, ref, _RET_IP_); } else { pag = xfs_perag_get(mp, *first); diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h index 4917d4eed4e..51c42c202bf 100644 --- a/fs/xfs/xfs_ag.h +++ b/fs/xfs/xfs_ag.h @@ -230,6 +230,9 @@ typedef struct xfs_perag { rwlock_t pag_ici_lock; /* incore inode lock */ struct radix_tree_root pag_ici_root; /* incore inode cache root */ int pag_ici_reclaimable; /* reclaimable inodes */ + + /* for rcu-safe freeing */ + struct rcu_head rcu_head; #endif int pagb_count; /* pagb slots in use */ } xfs_perag_t; diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 00c7a876807..14fc6e9e181 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -199,6 +199,8 @@ xfs_uuid_unmount( /* * Reference counting access wrappers to the perag structures. + * Because we never free per-ag structures, the only thing we + * have to protect against changes is the tree structure itself. */ struct xfs_perag * xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno) @@ -206,13 +208,13 @@ xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno) struct xfs_perag *pag; int ref = 0; - spin_lock(&mp->m_perag_lock); + rcu_read_lock(); pag = radix_tree_lookup(&mp->m_perag_tree, agno); if (pag) { ASSERT(atomic_read(&pag->pag_ref) >= 0); ref = atomic_inc_return(&pag->pag_ref); } - spin_unlock(&mp->m_perag_lock); + rcu_read_unlock(); trace_xfs_perag_get(mp, agno, ref, _RET_IP_); return pag; } @@ -227,10 +229,18 @@ xfs_perag_put(struct xfs_perag *pag) trace_xfs_perag_put(pag->pag_mount, pag->pag_agno, ref, _RET_IP_); } +STATIC void +__xfs_free_perag( + struct rcu_head *head) +{ + struct xfs_perag *pag = container_of(head, struct xfs_perag, rcu_head); + + ASSERT(atomic_read(&pag->pag_ref) == 0); + kmem_free(pag); +} + /* - * Free up the resources associated with a mount structure. Assume that - * the structure was initially zeroed, so we can tell which fields got - * initialized. + * Free up the per-ag resources associated with the mount structure. */ STATIC void xfs_free_perag( @@ -242,10 +252,9 @@ xfs_free_perag( for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { spin_lock(&mp->m_perag_lock); pag = radix_tree_delete(&mp->m_perag_tree, agno); - ASSERT(pag); - ASSERT(atomic_read(&pag->pag_ref) == 0); spin_unlock(&mp->m_perag_lock); - kmem_free(pag); + ASSERT(pag); + call_rcu(&pag->rcu_head, __xfs_free_perag); } } -- cgit v1.2.3-18-g5258 From dcd79a1423f64ee0184629874805c3ac40f3a2c5 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 28 Sep 2010 12:27:25 +1000 Subject: xfs: don't use vfs writeback for pure metadata modifications Under heavy multi-way parallel create workloads, the VFS struggles to write back all the inodes that have been changed in age order. The bdi flusher thread becomes CPU bound, spending 85% of it's time in the VFS code, mostly traversing the superblock dirty inode list to separate dirty inodes old enough to flush. We already keep an index of all metadata changes in age order - in the AIL - and continued log pressure will do age ordered writeback without any extra overhead at all. If there is no pressure on the log, the xfssyncd will periodically write back metadata in ascending disk address offset order so will be very efficient. Hence we can stop marking VFS inodes dirty during transaction commit or when changing timestamps during transactions. This will keep the inodes in the superblock dirty list to those containing data or unlogged metadata changes. However, the timstamp changes are slightly more complex than this - there are a couple of places that do unlogged updates of the timestamps, and the VFS need to be informed of these. Hence add a new function xfs_trans_ichgtime() for transactional changes, and leave xfs_ichgtime() for the non-transactional changes. Signed-off-by: Dave Chinner Reviewed-by: Alex Elder Reviewed-by: Christoph Hellwig --- fs/xfs/linux-2.6/xfs_ioctl.c | 2 +- fs/xfs/linux-2.6/xfs_iops.c | 35 ----------------------------------- fs/xfs/linux-2.6/xfs_super.c | 7 +------ fs/xfs/quota/xfs_qm_syscalls.c | 2 +- fs/xfs/xfs_attr.c | 31 +++++++++++-------------------- fs/xfs/xfs_inode.h | 1 - fs/xfs/xfs_inode_item.c | 9 --------- fs/xfs/xfs_rename.c | 12 ++++++++---- fs/xfs/xfs_trans.h | 1 + fs/xfs/xfs_trans_inode.c | 30 ++++++++++++++++++++++++++++++ fs/xfs/xfs_utils.c | 4 ++-- fs/xfs/xfs_vnodeops.c | 17 ++++++++++------- 12 files changed, 65 insertions(+), 86 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index 03aa908a9cb..10206be7a07 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -1088,8 +1088,8 @@ xfs_ioctl_setattr( xfs_diflags_to_linux(ip); } + xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); - xfs_ichgtime(ip, XFS_ICHGTIME_CHG); XFS_STATS_INC(xs_ig_attrchg); diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index b1fc2a6bfe8..a788f016d1f 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -94,41 +94,6 @@ xfs_mark_inode_dirty( mark_inode_dirty(inode); } -/* - * Change the requested timestamp in the given inode. - * We don't lock across timestamp updates, and we don't log them but - * we do record the fact that there is dirty information in core. - */ -void -xfs_ichgtime( - xfs_inode_t *ip, - int flags) -{ - struct inode *inode = VFS_I(ip); - timespec_t tv; - int sync_it = 0; - - tv = current_fs_time(inode->i_sb); - - if ((flags & XFS_ICHGTIME_MOD) && - !timespec_equal(&inode->i_mtime, &tv)) { - inode->i_mtime = tv; - sync_it = 1; - } - if ((flags & XFS_ICHGTIME_CHG) && - !timespec_equal(&inode->i_ctime, &tv)) { - inode->i_ctime = tv; - sync_it = 1; - } - - /* - * Update complete - now make sure everyone knows that the inode - * is dirty. - */ - if (sync_it) - xfs_mark_inode_dirty_sync(ip); -} - /* * Hook in SELinux. This is not quite correct yet, what we really need * here (as we do for default ACLs) is a mechanism by which creation of diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index a4e07974955..83154c0a317 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -972,12 +972,7 @@ xfs_fs_inode_init_once( /* * Dirty the XFS inode when mark_inode_dirty_sync() is called so that - * we catch unlogged VFS level updates to the inode. Care must be taken - * here - the transaction code calls mark_inode_dirty_sync() to mark the - * VFS inode dirty in a transaction and clears the i_update_core field; - * it must clear the field after calling mark_inode_dirty_sync() to - * correctly indicate that the dirty state has been propagated into the - * inode log item. + * we catch unlogged VFS level updates to the inode. * * We need the barrier() to maintain correct ordering between unlogged * updates and the transaction commit code that clears the i_update_core diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index 45e5849df23..7a71336f792 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -276,7 +276,7 @@ xfs_qm_scall_trunc_qfile( goto out_unlock; } - xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); out_unlock: diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c index c2568242a90..905d390c1e5 100644 --- a/fs/xfs/xfs_attr.c +++ b/fs/xfs/xfs_attr.c @@ -355,16 +355,15 @@ xfs_attr_set_int( if (mp->m_flags & XFS_MOUNT_WSYNC) { xfs_trans_set_sync(args.trans); } + + if (!error && (flags & ATTR_KERNOTIME) == 0) { + xfs_trans_ichgtime(args.trans, dp, + XFS_ICHGTIME_CHG); + } err2 = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES); xfs_iunlock(dp, XFS_ILOCK_EXCL); - /* - * Hit the inode change time. - */ - if (!error && (flags & ATTR_KERNOTIME) == 0) { - xfs_ichgtime(dp, XFS_ICHGTIME_CHG); - } return(error == 0 ? err2 : error); } @@ -420,6 +419,9 @@ xfs_attr_set_int( xfs_trans_set_sync(args.trans); } + if ((flags & ATTR_KERNOTIME) == 0) + xfs_trans_ichgtime(args.trans, dp, XFS_ICHGTIME_CHG); + /* * Commit the last in the sequence of transactions. */ @@ -427,13 +429,6 @@ xfs_attr_set_int( error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES); xfs_iunlock(dp, XFS_ILOCK_EXCL); - /* - * Hit the inode change time. - */ - if (!error && (flags & ATTR_KERNOTIME) == 0) { - xfs_ichgtime(dp, XFS_ICHGTIME_CHG); - } - return(error); out: @@ -567,6 +562,9 @@ xfs_attr_remove_int(xfs_inode_t *dp, struct xfs_name *name, int flags) xfs_trans_set_sync(args.trans); } + if ((flags & ATTR_KERNOTIME) == 0) + xfs_trans_ichgtime(args.trans, dp, XFS_ICHGTIME_CHG); + /* * Commit the last in the sequence of transactions. */ @@ -574,13 +572,6 @@ xfs_attr_remove_int(xfs_inode_t *dp, struct xfs_name *name, int flags) error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES); xfs_iunlock(dp, XFS_ILOCK_EXCL); - /* - * Hit the inode change time. - */ - if (!error && (flags & ATTR_KERNOTIME) == 0) { - xfs_ichgtime(dp, XFS_ICHGTIME_CHG); - } - return(error); out: diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 0898c5417d1..54781fcd322 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -471,7 +471,6 @@ int xfs_iunlink(struct xfs_trans *, xfs_inode_t *); void xfs_iext_realloc(xfs_inode_t *, int, int); void xfs_iunpin_wait(xfs_inode_t *); int xfs_iflush(xfs_inode_t *, uint); -void xfs_ichgtime(xfs_inode_t *, int); void xfs_lock_inodes(xfs_inode_t **, int, uint); void xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index fe00777e279..c7ac020705d 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -222,15 +222,6 @@ xfs_inode_item_format( vecp++; nvecs = 1; - /* - * Make sure the linux inode is dirty. We do this before - * clearing i_update_core as the VFS will call back into - * XFS here and set i_update_core, so we need to dirty the - * inode first so that the ordering of i_update_core and - * unlogged modifications still works as described below. - */ - xfs_mark_inode_dirty_sync(ip); - /* * Clear i_update_core if the timestamps (or any other * non-transactional modification) need flushing/logging diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c index 8fca957200d..9028733f7ed 100644 --- a/fs/xfs/xfs_rename.c +++ b/fs/xfs/xfs_rename.c @@ -211,7 +211,9 @@ xfs_rename( goto error_return; if (error) goto abort_return; - xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + + xfs_trans_ichgtime(tp, target_dp, + XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); if (new_parent && src_is_directory) { error = xfs_bumplink(tp, target_dp); @@ -249,7 +251,9 @@ xfs_rename( &first_block, &free_list, spaceres); if (error) goto abort_return; - xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + + xfs_trans_ichgtime(tp, target_dp, + XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); /* * Decrement the link count on the target since the target @@ -292,7 +296,7 @@ xfs_rename( * inode isn't really being changed, but old unix file systems did * it and some incremental backup programs won't work without it. */ - xfs_ichgtime(src_ip, XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, src_ip, XFS_ICHGTIME_CHG); /* * Adjust the link count on src_dp. This is necessary when @@ -315,7 +319,7 @@ xfs_rename( if (error) goto abort_return; - xfs_ichgtime(src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE); if (new_parent) xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index c13c0f97b49..e2cbe4da7d8 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -473,6 +473,7 @@ void xfs_trans_dquot_buf(xfs_trans_t *, struct xfs_buf *, uint); void xfs_trans_inode_alloc_buf(xfs_trans_t *, struct xfs_buf *); int xfs_trans_iget(struct xfs_mount *, xfs_trans_t *, xfs_ino_t , uint, uint, struct xfs_inode **); +void xfs_trans_ichgtime(struct xfs_trans *, struct xfs_inode *, int); void xfs_trans_ijoin_ref(struct xfs_trans *, struct xfs_inode *, uint); void xfs_trans_ijoin(struct xfs_trans *, struct xfs_inode *); void xfs_trans_log_buf(xfs_trans_t *, struct xfs_buf *, uint, uint); diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c index cdc53a1050c..ccb34532768 100644 --- a/fs/xfs/xfs_trans_inode.c +++ b/fs/xfs/xfs_trans_inode.c @@ -117,6 +117,36 @@ xfs_trans_ijoin_ref( ip->i_itemp->ili_lock_flags = lock_flags; } +/* + * Transactional inode timestamp update. Requires the inode to be locked and + * joined to the transaction supplied. Relies on the transaction subsystem to + * track dirty state and update/writeback the inode accordingly. + */ +void +xfs_trans_ichgtime( + struct xfs_trans *tp, + struct xfs_inode *ip, + int flags) +{ + struct inode *inode = VFS_I(ip); + timespec_t tv; + + ASSERT(tp); + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); + ASSERT(ip->i_transp == tp); + + tv = current_fs_time(inode->i_sb); + + if ((flags & XFS_ICHGTIME_MOD) && + !timespec_equal(&inode->i_mtime, &tv)) { + inode->i_mtime = tv; + } + if ((flags & XFS_ICHGTIME_CHG) && + !timespec_equal(&inode->i_ctime, &tv)) { + inode->i_ctime = tv; + } +} + /* * This is called to mark the fields indicated in fieldmask as needing * to be logged when the transaction is committed. The inode must diff --git a/fs/xfs/xfs_utils.c b/fs/xfs/xfs_utils.c index b7d5769d2df..4c2ba6f6adc 100644 --- a/fs/xfs/xfs_utils.c +++ b/fs/xfs/xfs_utils.c @@ -235,7 +235,7 @@ xfs_droplink( { int error; - xfs_ichgtime(ip, XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); ASSERT (ip->i_d.di_nlink > 0); ip->i_d.di_nlink--; @@ -299,7 +299,7 @@ xfs_bumplink( { if (ip->i_d.di_nlink >= XFS_MAXLINK) return XFS_ERROR(EMLINK); - xfs_ichgtime(ip, XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); ASSERT(ip->i_d.di_nlink > 0); ip->i_d.di_nlink++; diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index dc6e4fb8bbc..a230cd4d077 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -184,8 +184,11 @@ xfs_setattr( ip->i_size == 0 && ip->i_d.di_nextents == 0) { xfs_iunlock(ip, XFS_ILOCK_EXCL); lock_flags &= ~XFS_ILOCK_EXCL; - if (mask & ATTR_CTIME) - xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + if (mask & ATTR_CTIME) { + inode->i_mtime = inode->i_ctime = + current_fs_time(inode->i_sb); + xfs_mark_inode_dirty_sync(ip); + } code = 0; goto error_return; } @@ -1391,7 +1394,7 @@ xfs_create( ASSERT(error != ENOSPC); goto out_trans_abort; } - xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); if (is_dir) { @@ -1742,7 +1745,7 @@ xfs_remove( ASSERT(error != ENOENT); goto out_bmap_cancel; } - xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); if (is_dir) { /* @@ -1895,7 +1898,7 @@ xfs_link( &first_block, &free_list, resblks); if (error) goto abort_return; - xfs_ichgtime(tdp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, tdp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE); error = xfs_bumplink(tp, sip); @@ -2129,7 +2132,7 @@ xfs_symlink( &first_block, &free_list, resblks); if (error) goto error1; - xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); /* @@ -2833,7 +2836,7 @@ xfs_change_file_space( if (ip->i_d.di_mode & S_IXGRP) ip->i_d.di_mode &= ~S_ISGID; - xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); } if (setprealloc) ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC; -- cgit v1.2.3-18-g5258 From 686865f76e35b28ba7aa6afa19209426f0da6201 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 24 Sep 2010 20:07:47 +1000 Subject: xfs: rename xfs_buf_get_nodaddr to be more appropriate xfs_buf_get_nodaddr() is really used to allocate a buffer that is uncached. While it is not directly assigned a disk address, the fact that they are not cached is a more important distinction. With the upcoming uncached buffer read primitive, we should be consistent with this disctinction. While there, make page allocation in xfs_buf_get_nodaddr() safe against memory reclaim re-entrancy into the filesystem by allowing a flags parameter to be passed. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_buf.c | 9 +++++---- fs/xfs/linux-2.6/xfs_buf.h | 2 +- fs/xfs/linux-2.6/xfs_trace.h | 2 +- fs/xfs/xfs_log.c | 3 ++- fs/xfs/xfs_log_recover.c | 3 ++- fs/xfs/xfs_vnodeops.c | 6 +++--- 6 files changed, 14 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 286e36e21da..eca945b0f88 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -707,9 +707,10 @@ xfs_buf_associate_memory( } xfs_buf_t * -xfs_buf_get_noaddr( +xfs_buf_get_uncached( + struct xfs_buftarg *target, size_t len, - xfs_buftarg_t *target) + int flags) { unsigned long page_count = PAGE_ALIGN(len) >> PAGE_SHIFT; int error, i; @@ -725,7 +726,7 @@ xfs_buf_get_noaddr( goto fail_free_buf; for (i = 0; i < page_count; i++) { - bp->b_pages[i] = alloc_page(GFP_KERNEL); + bp->b_pages[i] = alloc_page(xb_to_gfp(flags)); if (!bp->b_pages[i]) goto fail_free_mem; } @@ -740,7 +741,7 @@ xfs_buf_get_noaddr( xfs_buf_unlock(bp); - trace_xfs_buf_get_noaddr(bp, _RET_IP_); + trace_xfs_buf_get_uncached(bp, _RET_IP_); return bp; fail_free_mem: diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index 2a05614f0b9..fb30447091d 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -213,7 +213,7 @@ extern xfs_buf_t *xfs_buf_read(xfs_buftarg_t *, xfs_off_t, size_t, xfs_buf_flags_t); extern xfs_buf_t *xfs_buf_get_empty(size_t, xfs_buftarg_t *); -extern xfs_buf_t *xfs_buf_get_noaddr(size_t, xfs_buftarg_t *); +extern xfs_buf_t *xfs_buf_get_uncached(struct xfs_buftarg *, size_t, int); extern int xfs_buf_associate_memory(xfs_buf_t *, void *, size_t); extern void xfs_buf_hold(xfs_buf_t *); extern void xfs_buf_readahead(xfs_buftarg_t *, xfs_off_t, size_t, diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index be5dffd282a..2a1d4fbd9ed 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -331,7 +331,7 @@ DEFINE_BUF_EVENT(xfs_buf_iowait_done); DEFINE_BUF_EVENT(xfs_buf_delwri_queue); DEFINE_BUF_EVENT(xfs_buf_delwri_dequeue); DEFINE_BUF_EVENT(xfs_buf_delwri_split); -DEFINE_BUF_EVENT(xfs_buf_get_noaddr); +DEFINE_BUF_EVENT(xfs_buf_get_uncached); DEFINE_BUF_EVENT(xfs_bdstrat_shut); DEFINE_BUF_EVENT(xfs_buf_item_relse); DEFINE_BUF_EVENT(xfs_buf_item_iodone); diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 33f718f92a4..c8a30942430 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1131,7 +1131,8 @@ xlog_alloc_log(xfs_mount_t *mp, iclog->ic_prev = prev_iclog; prev_iclog = iclog; - bp = xfs_buf_get_noaddr(log->l_iclog_size, mp->m_logdev_targp); + bp = xfs_buf_get_uncached(mp->m_logdev_targp, + log->l_iclog_size, 0); if (!bp) goto out_free_iclog; if (!XFS_BUF_CPSEMA(bp)) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 6f3f5fa37ac..3d887542b03 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -107,7 +107,8 @@ xlog_get_bp( nbblks += log->l_sectBBsize; nbblks = round_up(nbblks, log->l_sectBBsize); - return xfs_buf_get_noaddr(BBTOB(nbblks), log->l_mp->m_logdev_targp); + return xfs_buf_get_uncached(log->l_mp->m_logdev_targp, + BBTOB(nbblks), 0); } STATIC void diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index a230cd4d077..b7bdc43308e 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -2434,9 +2434,9 @@ xfs_zero_remaining_bytes( if (endoff > ip->i_size) endoff = ip->i_size; - bp = xfs_buf_get_noaddr(mp->m_sb.sb_blocksize, - XFS_IS_REALTIME_INODE(ip) ? - mp->m_rtdev_targp : mp->m_ddev_targp); + bp = xfs_buf_get_uncached(XFS_IS_REALTIME_INODE(ip) ? + mp->m_rtdev_targp : mp->m_ddev_targp, + mp->m_sb.sb_blocksize, XBF_DONT_BLOCK); if (!bp) return XFS_ERROR(ENOMEM); -- cgit v1.2.3-18-g5258 From 5adc94c247c3779782c7b0b8b5e28cf50596eb37 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 24 Sep 2010 21:58:31 +1000 Subject: xfs: introduced uncached buffer read primitve To avoid the need to use cached buffers for single-shot or buffers cached at the filesystem level, introduce a new buffer read primitive that bypasses the cache an reads directly from disk. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_buf.c | 34 ++++++++++++++++++++++++++++++++++ fs/xfs/linux-2.6/xfs_buf.h | 3 +++ 2 files changed, 37 insertions(+) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index eca945b0f88..22c7bff77ad 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -638,6 +638,40 @@ xfs_buf_readahead( xfs_buf_read(target, ioff, isize, flags); } +/* + * Read an uncached buffer from disk. Allocates and returns a locked + * buffer containing the disk contents or nothing. + */ +struct xfs_buf * +xfs_buf_read_uncached( + struct xfs_mount *mp, + struct xfs_buftarg *target, + xfs_daddr_t daddr, + size_t length, + int flags) +{ + xfs_buf_t *bp; + int error; + + bp = xfs_buf_get_uncached(target, length, flags); + if (!bp) + return NULL; + + /* set up the buffer for a read IO */ + xfs_buf_lock(bp); + XFS_BUF_SET_ADDR(bp, daddr); + XFS_BUF_READ(bp); + XFS_BUF_BUSY(bp); + + xfsbdstrat(mp, bp); + error = xfs_iowait(bp); + if (error || bp->b_error) { + xfs_buf_relse(bp); + return NULL; + } + return bp; +} + xfs_buf_t * xfs_buf_get_empty( size_t len, diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index fb30447091d..57eedc750ee 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -218,6 +218,9 @@ extern int xfs_buf_associate_memory(xfs_buf_t *, void *, size_t); extern void xfs_buf_hold(xfs_buf_t *); extern void xfs_buf_readahead(xfs_buftarg_t *, xfs_off_t, size_t, xfs_buf_flags_t); +struct xfs_buf *xfs_buf_read_uncached(struct xfs_mount *mp, + struct xfs_buftarg *target, + xfs_daddr_t daddr, size_t length, int flags); /* Releasing Buffers */ extern void xfs_buf_free(xfs_buf_t *); -- cgit v1.2.3-18-g5258 From ebad861b5702c3e2332a3e906978f47144d22f70 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 22 Sep 2010 10:47:20 +1000 Subject: xfs: store xfs_mount in the buftarg instead of in the xfs_buf Each buffer contains both a buftarg pointer and a mount pointer. If we add a mount pointer into the buftarg, we can avoid needing the b_mount field in every buffer and grab it from the buftarg when needed instead. This shrinks the xfs_buf by 8 bytes. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_buf.c | 9 ++++----- fs/xfs/linux-2.6/xfs_buf.h | 5 +++-- fs/xfs/linux-2.6/xfs_super.c | 8 +++++--- fs/xfs/xfs_buf_item.c | 3 +-- fs/xfs/xfs_log_recover.c | 16 +++++++--------- 5 files changed, 20 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 22c7bff77ad..d6928970097 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -894,7 +894,7 @@ xfs_buf_lock( trace_xfs_buf_lock(bp, _RET_IP_); if (atomic_read(&bp->b_pin_count) && (bp->b_flags & XBF_STALE)) - xfs_log_force(bp->b_mount, 0); + xfs_log_force(bp->b_target->bt_mount, 0); if (atomic_read(&bp->b_io_remaining)) blk_run_address_space(bp->b_target->bt_mapping); down(&bp->b_sema); @@ -1017,7 +1017,6 @@ xfs_bwrite( { int error; - bp->b_mount = mp; bp->b_flags |= XBF_WRITE; bp->b_flags &= ~(XBF_ASYNC | XBF_READ); @@ -1038,8 +1037,6 @@ xfs_bdwrite( { trace_xfs_buf_bdwrite(bp, _RET_IP_); - bp->b_mount = mp; - bp->b_flags &= ~XBF_READ; bp->b_flags |= (XBF_DELWRI | XBF_ASYNC); @@ -1128,7 +1125,7 @@ int xfs_bdstrat_cb( struct xfs_buf *bp) { - if (XFS_FORCED_SHUTDOWN(bp->b_mount)) { + if (XFS_FORCED_SHUTDOWN(bp->b_target->bt_mount)) { trace_xfs_bdstrat_shut(bp, _RET_IP_); /* * Metadata write that didn't get logged but @@ -1644,6 +1641,7 @@ out_error: xfs_buftarg_t * xfs_alloc_buftarg( + struct xfs_mount *mp, struct block_device *bdev, int external, const char *fsname) @@ -1652,6 +1650,7 @@ xfs_alloc_buftarg( btp = kmem_zalloc(sizeof(*btp), KM_SLEEP); + btp->bt_mount = mp; btp->bt_dev = bdev->bd_dev; btp->bt_bdev = bdev; if (xfs_setsize_buftarg_early(btp, bdev)) diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index 57eedc750ee..def2cea2b4d 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -132,6 +132,7 @@ typedef struct xfs_buftarg { dev_t bt_dev; struct block_device *bt_bdev; struct address_space *bt_mapping; + struct xfs_mount *bt_mount; unsigned int bt_bsize; unsigned int bt_sshift; size_t bt_smask; @@ -189,7 +190,6 @@ typedef struct xfs_buf { struct completion b_iowait; /* queue for I/O waiters */ void *b_fspriv; void *b_fspriv2; - struct xfs_mount *b_mount; unsigned short b_error; /* error code on I/O */ unsigned int b_page_count; /* size of page array */ unsigned int b_offset; /* page offset in first page */ @@ -377,7 +377,8 @@ static inline void xfs_buf_relse(xfs_buf_t *bp) /* * Handling of buftargs. */ -extern xfs_buftarg_t *xfs_alloc_buftarg(struct block_device *, int, const char *); +extern xfs_buftarg_t *xfs_alloc_buftarg(struct xfs_mount *, + struct block_device *, int, const char *); extern void xfs_free_buftarg(struct xfs_mount *, struct xfs_buftarg *); extern void xfs_wait_buftarg(xfs_buftarg_t *); extern int xfs_setsize_buftarg(xfs_buftarg_t *, unsigned int, unsigned int); diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 83154c0a317..4759be4daa2 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -758,18 +758,20 @@ xfs_open_devices( * Setup xfs_mount buffer target pointers */ error = ENOMEM; - mp->m_ddev_targp = xfs_alloc_buftarg(ddev, 0, mp->m_fsname); + mp->m_ddev_targp = xfs_alloc_buftarg(mp, ddev, 0, mp->m_fsname); if (!mp->m_ddev_targp) goto out_close_rtdev; if (rtdev) { - mp->m_rtdev_targp = xfs_alloc_buftarg(rtdev, 1, mp->m_fsname); + mp->m_rtdev_targp = xfs_alloc_buftarg(mp, rtdev, 1, + mp->m_fsname); if (!mp->m_rtdev_targp) goto out_free_ddev_targ; } if (logdev && logdev != ddev) { - mp->m_logdev_targp = xfs_alloc_buftarg(logdev, 1, mp->m_fsname); + mp->m_logdev_targp = xfs_alloc_buftarg(mp, logdev, 1, + mp->m_fsname); if (!mp->m_logdev_targp) goto out_free_rtdev_targ; } else { diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 1b09d7a280d..ee7557611b6 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -692,8 +692,7 @@ xfs_buf_item_init( * the first. If we do already have one, there is * nothing to do here so return. */ - if (bp->b_mount != mp) - bp->b_mount = mp; + ASSERT(bp->b_target->bt_mount == mp); if (XFS_BUF_FSPRIVATE(bp, void *) != NULL) { lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *); if (lip->li_type == XFS_LI_BUF) { diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 3d887542b03..351d71117f1 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -322,10 +322,11 @@ xlog_recover_iodone( * this during recovery. One strike! */ xfs_ioerror_alert("xlog_recover_iodone", - bp->b_mount, bp, XFS_BUF_ADDR(bp)); - xfs_force_shutdown(bp->b_mount, SHUTDOWN_META_IO_ERROR); + bp->b_target->bt_mount, bp, + XFS_BUF_ADDR(bp)); + xfs_force_shutdown(bp->b_target->bt_mount, + SHUTDOWN_META_IO_ERROR); } - bp->b_mount = NULL; XFS_BUF_CLR_IODONE_FUNC(bp); xfs_biodone(bp); } @@ -2276,8 +2277,7 @@ xlog_recover_do_buffer_trans( XFS_BUF_STALE(bp); error = xfs_bwrite(mp, bp); } else { - ASSERT(bp->b_mount == NULL || bp->b_mount == mp); - bp->b_mount = mp; + ASSERT(bp->b_target->bt_mount == mp); XFS_BUF_SET_IODONE_FUNC(bp, xlog_recover_iodone); xfs_bdwrite(mp, bp); } @@ -2541,8 +2541,7 @@ xlog_recover_do_inode_trans( } write_inode_buffer: - ASSERT(bp->b_mount == NULL || bp->b_mount == mp); - bp->b_mount = mp; + ASSERT(bp->b_target->bt_mount == mp); XFS_BUF_SET_IODONE_FUNC(bp, xlog_recover_iodone); xfs_bdwrite(mp, bp); error: @@ -2679,8 +2678,7 @@ xlog_recover_do_dquot_trans( memcpy(ddq, recddq, item->ri_buf[1].i_len); ASSERT(dq_f->qlf_size == 2); - ASSERT(bp->b_mount == NULL || bp->b_mount == mp); - bp->b_mount = mp; + ASSERT(bp->b_target->bt_mount == mp); XFS_BUF_SET_IODONE_FUNC(bp, xlog_recover_iodone); xfs_bdwrite(mp, bp); -- cgit v1.2.3-18-g5258 From 26af655233dd486659235f3049959d2f7dafc5a1 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 22 Sep 2010 10:47:20 +1000 Subject: xfs: kill XBF_FS_MANAGED buffers Filesystem level managed buffers are buffers that have their lifecycle controlled by the filesystem layer, not the buffer cache. We currently cache these buffers, which makes cleanup and cache walking somewhat troublesome. Convert the fs managed buffers to uncached buffers obtained by via xfs_buf_get_uncached(), and remove the XBF_FS_MANAGED special cases from the buffer cache. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_buf.c | 20 ++++------------- fs/xfs/linux-2.6/xfs_buf.h | 4 ---- fs/xfs/xfs_mount.c | 55 ++++++++++++++-------------------------------- 3 files changed, 20 insertions(+), 59 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index d6928970097..975d6589394 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -826,8 +826,6 @@ xfs_buf_rele( atomic_inc(&bp->b_hold); spin_unlock(&hash->bh_lock); (*(bp->b_relse)) (bp); - } else if (bp->b_flags & XBF_FS_MANAGED) { - spin_unlock(&hash->bh_lock); } else { ASSERT(!(bp->b_flags & (XBF_DELWRI|_XBF_DELWRI_Q))); list_del_init(&bp->b_hash_list); @@ -1433,26 +1431,16 @@ void xfs_wait_buftarg( xfs_buftarg_t *btp) { - xfs_buf_t *bp, *n; xfs_bufhash_t *hash; uint i; for (i = 0; i < (1 << btp->bt_hashshift); i++) { hash = &btp->bt_hash[i]; -again: spin_lock(&hash->bh_lock); - list_for_each_entry_safe(bp, n, &hash->bh_list, b_hash_list) { - ASSERT(btp == bp->b_target); - if (!(bp->b_flags & XBF_FS_MANAGED)) { - spin_unlock(&hash->bh_lock); - /* - * Catch superblock reference count leaks - * immediately - */ - BUG_ON(bp->b_bn == 0); - delay(100); - goto again; - } + while (!list_empty(&hash->bh_list)) { + spin_unlock(&hash->bh_lock); + delay(100); + spin_lock(&hash->bh_lock); } spin_unlock(&hash->bh_lock); } diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index def2cea2b4d..1f109cee136 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -51,7 +51,6 @@ typedef enum { #define XBF_DONE (1 << 5) /* all pages in the buffer uptodate */ #define XBF_DELWRI (1 << 6) /* buffer has dirty pages */ #define XBF_STALE (1 << 7) /* buffer has been staled, do not find it */ -#define XBF_FS_MANAGED (1 << 8) /* filesystem controls freeing memory */ #define XBF_ORDERED (1 << 11)/* use ordered writes */ #define XBF_READ_AHEAD (1 << 12)/* asynchronous read-ahead */ #define XBF_LOG_BUFFER (1 << 13)/* this is a buffer used for the log */ @@ -104,7 +103,6 @@ typedef unsigned int xfs_buf_flags_t; { XBF_DONE, "DONE" }, \ { XBF_DELWRI, "DELWRI" }, \ { XBF_STALE, "STALE" }, \ - { XBF_FS_MANAGED, "FS_MANAGED" }, \ { XBF_ORDERED, "ORDERED" }, \ { XBF_READ_AHEAD, "READ_AHEAD" }, \ { XBF_LOCK, "LOCK" }, /* should never be set */\ @@ -279,8 +277,6 @@ extern void xfs_buf_terminate(void); XFS_BUF_DONE(bp); \ } while (0) -#define XFS_BUF_UNMANAGE(bp) ((bp)->b_flags &= ~XBF_FS_MANAGED) - #define XFS_BUF_DELAYWRITE(bp) ((bp)->b_flags |= XBF_DELWRI) #define XFS_BUF_UNDELAYWRITE(bp) xfs_buf_delwri_dequeue(bp) #define XFS_BUF_ISDELAYWRITE(bp) ((bp)->b_flags & XBF_DELWRI) diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 14fc6e9e181..fbca293326e 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -646,7 +646,6 @@ int xfs_readsb(xfs_mount_t *mp, int flags) { unsigned int sector_size; - unsigned int extra_flags; xfs_buf_t *bp; int error; @@ -659,28 +658,24 @@ xfs_readsb(xfs_mount_t *mp, int flags) * access to the superblock. */ sector_size = xfs_getsize_buftarg(mp->m_ddev_targp); - extra_flags = XBF_LOCK | XBF_FS_MANAGED | XBF_MAPPED; - bp = xfs_buf_read(mp->m_ddev_targp, XFS_SB_DADDR, BTOBB(sector_size), - extra_flags); - if (!bp || XFS_BUF_ISERROR(bp)) { - xfs_fs_mount_cmn_err(flags, "SB read failed"); - error = bp ? XFS_BUF_GETERROR(bp) : ENOMEM; - goto fail; +reread: + bp = xfs_buf_read_uncached(mp, mp->m_ddev_targp, + XFS_SB_DADDR, sector_size, 0); + if (!bp) { + xfs_fs_mount_cmn_err(flags, "SB buffer read failed"); + return EIO; } - ASSERT(XFS_BUF_ISBUSY(bp)); - ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); /* * Initialize the mount structure from the superblock. * But first do some basic consistency checking. */ xfs_sb_from_disk(&mp->m_sb, XFS_BUF_TO_SBP(bp)); - error = xfs_mount_validate_sb(mp, &(mp->m_sb), flags); if (error) { xfs_fs_mount_cmn_err(flags, "SB validate failed"); - goto fail; + goto release_buf; } /* @@ -691,7 +686,7 @@ xfs_readsb(xfs_mount_t *mp, int flags) "device supports only %u byte sectors (not %u)", sector_size, mp->m_sb.sb_sectsize); error = ENOSYS; - goto fail; + goto release_buf; } /* @@ -699,33 +694,20 @@ xfs_readsb(xfs_mount_t *mp, int flags) * re-read the superblock so the buffer is correctly sized. */ if (sector_size < mp->m_sb.sb_sectsize) { - XFS_BUF_UNMANAGE(bp); xfs_buf_relse(bp); sector_size = mp->m_sb.sb_sectsize; - bp = xfs_buf_read(mp->m_ddev_targp, XFS_SB_DADDR, - BTOBB(sector_size), extra_flags); - if (!bp || XFS_BUF_ISERROR(bp)) { - xfs_fs_mount_cmn_err(flags, "SB re-read failed"); - error = bp ? XFS_BUF_GETERROR(bp) : ENOMEM; - goto fail; - } - ASSERT(XFS_BUF_ISBUSY(bp)); - ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); + goto reread; } /* Initialize per-cpu counters */ xfs_icsb_reinit_counters(mp); mp->m_sb_bp = bp; - xfs_buf_relse(bp); - ASSERT(XFS_BUF_VALUSEMA(bp) > 0); + xfs_buf_unlock(bp); return 0; - fail: - if (bp) { - XFS_BUF_UNMANAGE(bp); - xfs_buf_relse(bp); - } +release_buf: + xfs_buf_relse(bp); return error; } @@ -2005,18 +1987,13 @@ xfs_getsb( */ void xfs_freesb( - xfs_mount_t *mp) + struct xfs_mount *mp) { - xfs_buf_t *bp; + struct xfs_buf *bp = mp->m_sb_bp; - /* - * Use xfs_getsb() so that the buffer will be locked - * when we call xfs_buf_relse(). - */ - bp = xfs_getsb(mp, 0); - XFS_BUF_UNMANAGE(bp); - xfs_buf_relse(bp); + xfs_buf_lock(bp); mp->m_sb_bp = NULL; + xfs_buf_relse(bp); } /* -- cgit v1.2.3-18-g5258 From 1922c949c59f93beb560d59874bcc6d5c00115ac Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 22 Sep 2010 10:47:20 +1000 Subject: xfs: use unhashed buffers for size checks When we are checking we can access the last block of each device, we do not need to use cached buffers as they will be tossed away immediately. Use uncached buffers for size checks so that all IO prior to full in-memory structure initialisation does not use the buffer cache. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/xfs_fsops.c | 11 +++++------ fs/xfs/xfs_mount.c | 39 ++++++++++++++++----------------------- fs/xfs/xfs_rtalloc.c | 29 +++++++++++++---------------- 3 files changed, 34 insertions(+), 45 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index 43b1d569933..6a1edb1348f 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -144,12 +144,11 @@ xfs_growfs_data_private( if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb))) return error; dpct = pct - mp->m_sb.sb_imax_pct; - error = xfs_read_buf(mp, mp->m_ddev_targp, - XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1), - XFS_FSS_TO_BB(mp, 1), 0, &bp); - if (error) - return error; - ASSERT(bp); + bp = xfs_buf_read_uncached(mp, mp->m_ddev_targp, + XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1), + BBTOB(XFS_FSS_TO_BB(mp, 1)), 0); + if (!bp) + return EIO; xfs_buf_relse(bp); new = nb; /* use new as a temporary here */ diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index fbca293326e..912101d280b 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -980,42 +980,35 @@ xfs_check_sizes(xfs_mount_t *mp) { xfs_buf_t *bp; xfs_daddr_t d; - int error; d = (xfs_daddr_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks); if (XFS_BB_TO_FSB(mp, d) != mp->m_sb.sb_dblocks) { - cmn_err(CE_WARN, "XFS: size check 1 failed"); + cmn_err(CE_WARN, "XFS: filesystem size mismatch detected"); return XFS_ERROR(EFBIG); } - error = xfs_read_buf(mp, mp->m_ddev_targp, - d - XFS_FSS_TO_BB(mp, 1), - XFS_FSS_TO_BB(mp, 1), 0, &bp); - if (!error) { - xfs_buf_relse(bp); - } else { - cmn_err(CE_WARN, "XFS: size check 2 failed"); - if (error == ENOSPC) - error = XFS_ERROR(EFBIG); - return error; + bp = xfs_buf_read_uncached(mp, mp->m_ddev_targp, + d - XFS_FSS_TO_BB(mp, 1), + BBTOB(XFS_FSS_TO_BB(mp, 1)), 0); + if (!bp) { + cmn_err(CE_WARN, "XFS: last sector read failed"); + return EIO; } + xfs_buf_relse(bp); if (mp->m_logdev_targp != mp->m_ddev_targp) { d = (xfs_daddr_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks); if (XFS_BB_TO_FSB(mp, d) != mp->m_sb.sb_logblocks) { - cmn_err(CE_WARN, "XFS: size check 3 failed"); + cmn_err(CE_WARN, "XFS: log size mismatch detected"); return XFS_ERROR(EFBIG); } - error = xfs_read_buf(mp, mp->m_logdev_targp, - d - XFS_FSB_TO_BB(mp, 1), - XFS_FSB_TO_BB(mp, 1), 0, &bp); - if (!error) { - xfs_buf_relse(bp); - } else { - cmn_err(CE_WARN, "XFS: size check 3 failed"); - if (error == ENOSPC) - error = XFS_ERROR(EFBIG); - return error; + bp = xfs_buf_read_uncached(mp, mp->m_logdev_targp, + d - XFS_FSB_TO_BB(mp, 1), + XFS_FSB_TO_B(mp, 1), 0); + if (!bp) { + cmn_err(CE_WARN, "XFS: log device read failed"); + return EIO; } + xfs_buf_relse(bp); } return 0; } diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 891260fea11..12a19138531 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -39,6 +39,7 @@ #include "xfs_trans_space.h" #include "xfs_utils.h" #include "xfs_trace.h" +#include "xfs_buf.h" /* @@ -1883,13 +1884,13 @@ xfs_growfs_rt( /* * Read in the last block of the device, make sure it exists. */ - error = xfs_read_buf(mp, mp->m_rtdev_targp, - XFS_FSB_TO_BB(mp, nrblocks - 1), - XFS_FSB_TO_BB(mp, 1), 0, &bp); - if (error) - return error; - ASSERT(bp); + bp = xfs_buf_read_uncached(mp, mp->m_rtdev_targp, + XFS_FSB_TO_BB(mp, nrblocks - 1), + XFS_FSB_TO_B(mp, 1), 0); + if (!bp) + return EIO; xfs_buf_relse(bp); + /* * Calculate new parameters. These are the final values to be reached. */ @@ -2215,7 +2216,6 @@ xfs_rtmount_init( { xfs_buf_t *bp; /* buffer for last block of subvolume */ xfs_daddr_t d; /* address of last block of subvolume */ - int error; /* error return value */ xfs_sb_t *sbp; /* filesystem superblock copy in mount */ sbp = &mp->m_sb; @@ -2242,15 +2242,12 @@ xfs_rtmount_init( (unsigned long long) mp->m_sb.sb_rblocks); return XFS_ERROR(EFBIG); } - error = xfs_read_buf(mp, mp->m_rtdev_targp, - d - XFS_FSB_TO_BB(mp, 1), - XFS_FSB_TO_BB(mp, 1), 0, &bp); - if (error) { - cmn_err(CE_WARN, - "XFS: realtime mount -- xfs_read_buf failed, returned %d", error); - if (error == ENOSPC) - return XFS_ERROR(EFBIG); - return error; + bp = xfs_buf_read_uncached(mp, mp->m_rtdev_targp, + d - XFS_FSB_TO_BB(mp, 1), + XFS_FSB_TO_B(mp, 1), 0); + if (!bp) { + cmn_err(CE_WARN, "XFS: realtime device size check failed"); + return EIO; } xfs_buf_relse(bp); return 0; -- cgit v1.2.3-18-g5258 From 69d6cc76cff3573ceefda178b75e20878866fdc3 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 22 Sep 2010 10:47:20 +1000 Subject: xfs: remove buftarg hash for external devices For RT and external log devices, we never use hashed buffers on them now. Remove the buftarg hash tables that are set up for them. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_buf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 975d6589394..251bcdc6352 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -1458,7 +1458,11 @@ xfs_alloc_bufhash( { unsigned int i; - btp->bt_hashshift = external ? 3 : 12; /* 8 or 4096 buckets */ + if (external) { + btp->bt_hash = NULL; + return; + } + btp->bt_hashshift = 12; /* 4096 buckets */ btp->bt_hash = kmem_zalloc_large((1 << btp->bt_hashshift) * sizeof(xfs_bufhash_t)); for (i = 0; i < (1 << btp->bt_hashshift); i++) { -- cgit v1.2.3-18-g5258 From 65d0f20533c503b50bd5e7e86434512af7761eea Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 24 Sep 2010 18:40:15 +1000 Subject: xfs: split inode AG walking into separate code for reclaim The reclaim walk requires different locking and has a slightly different walk algorithm, so separate it out so that it can be optimised separately. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_sync.c | 202 +++++++++++++++++++---------------------- fs/xfs/linux-2.6/xfs_sync.h | 2 +- fs/xfs/linux-2.6/xfs_trace.h | 2 +- fs/xfs/quota/xfs_qm_syscalls.c | 3 +- fs/xfs/xfs_mount.c | 26 ++++++ fs/xfs/xfs_mount.h | 2 + 6 files changed, 122 insertions(+), 115 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 3a1d229b478..b5cdf0ef39e 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -40,78 +40,46 @@ #include -STATIC xfs_inode_t * -xfs_inode_ag_lookup( - struct xfs_mount *mp, - struct xfs_perag *pag, - uint32_t *first_index, - int tag) -{ - int nr_found; - struct xfs_inode *ip; - - /* - * use a gang lookup to find the next inode in the tree - * as the tree is sparse and a gang lookup walks to find - * the number of objects requested. - */ - if (tag == XFS_ICI_NO_TAG) { - nr_found = radix_tree_gang_lookup(&pag->pag_ici_root, - (void **)&ip, *first_index, 1); - } else { - nr_found = radix_tree_gang_lookup_tag(&pag->pag_ici_root, - (void **)&ip, *first_index, 1, tag); - } - if (!nr_found) - return NULL; - - /* - * Update the index for the next lookup. Catch overflows - * into the next AG range which can occur if we have inodes - * in the last block of the AG and we are currently - * pointing to the last inode. - */ - *first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1); - if (*first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) - return NULL; - return ip; -} - STATIC int xfs_inode_ag_walk( struct xfs_mount *mp, struct xfs_perag *pag, int (*execute)(struct xfs_inode *ip, struct xfs_perag *pag, int flags), - int flags, - int tag, - int exclusive, - int *nr_to_scan) + int flags) { uint32_t first_index; int last_error = 0; int skipped; + int done; restart: + done = 0; skipped = 0; first_index = 0; do { int error = 0; + int nr_found; xfs_inode_t *ip; - if (exclusive) - write_lock(&pag->pag_ici_lock); - else - read_lock(&pag->pag_ici_lock); - ip = xfs_inode_ag_lookup(mp, pag, &first_index, tag); - if (!ip) { - if (exclusive) - write_unlock(&pag->pag_ici_lock); - else - read_unlock(&pag->pag_ici_lock); + read_lock(&pag->pag_ici_lock); + nr_found = radix_tree_gang_lookup(&pag->pag_ici_root, + (void **)&ip, first_index, 1); + if (!nr_found) { + read_unlock(&pag->pag_ici_lock); break; } + /* + * Update the index for the next lookup. Catch overflows + * into the next AG range which can occur if we have inodes + * in the last block of the AG and we are currently + * pointing to the last inode. + */ + first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1); + if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) + done = 1; + /* execute releases pag->pag_ici_lock */ error = execute(ip, pag, flags); if (error == EAGAIN) { @@ -125,7 +93,7 @@ restart: if (error == EFSCORRUPTED) break; - } while ((*nr_to_scan)--); + } while (!done); if (skipped) { delay(1); @@ -134,73 +102,29 @@ restart: return last_error; } -/* - * Select the next per-ag structure to iterate during the walk. The reclaim - * walk is optimised only to walk AGs with reclaimable inodes in them. - */ -static struct xfs_perag * -xfs_inode_ag_iter_next_pag( - struct xfs_mount *mp, - xfs_agnumber_t *first, - int tag) -{ - struct xfs_perag *pag = NULL; - - if (tag == XFS_ICI_RECLAIM_TAG) { - int found; - int ref; - - rcu_read_lock(); - found = radix_tree_gang_lookup_tag(&mp->m_perag_tree, - (void **)&pag, *first, 1, tag); - if (found <= 0) { - rcu_read_unlock(); - return NULL; - } - *first = pag->pag_agno + 1; - /* open coded pag reference increment */ - ref = atomic_inc_return(&pag->pag_ref); - rcu_read_unlock(); - trace_xfs_perag_get_reclaim(mp, pag->pag_agno, ref, _RET_IP_); - } else { - pag = xfs_perag_get(mp, *first); - (*first)++; - } - return pag; -} - int xfs_inode_ag_iterator( struct xfs_mount *mp, int (*execute)(struct xfs_inode *ip, struct xfs_perag *pag, int flags), - int flags, - int tag, - int exclusive, - int *nr_to_scan) + int flags) { struct xfs_perag *pag; int error = 0; int last_error = 0; xfs_agnumber_t ag; - int nr; - nr = nr_to_scan ? *nr_to_scan : INT_MAX; ag = 0; - while ((pag = xfs_inode_ag_iter_next_pag(mp, &ag, tag))) { - error = xfs_inode_ag_walk(mp, pag, execute, flags, tag, - exclusive, &nr); + while ((pag = xfs_perag_get(mp, ag))) { + ag = pag->pag_agno + 1; + error = xfs_inode_ag_walk(mp, pag, execute, flags); xfs_perag_put(pag); if (error) { last_error = error; if (error == EFSCORRUPTED) break; } - if (nr <= 0) - break; } - if (nr_to_scan) - *nr_to_scan = nr; return XFS_ERROR(last_error); } @@ -318,8 +242,7 @@ xfs_sync_data( ASSERT((flags & ~(SYNC_TRYLOCK|SYNC_WAIT)) == 0); - error = xfs_inode_ag_iterator(mp, xfs_sync_inode_data, flags, - XFS_ICI_NO_TAG, 0, NULL); + error = xfs_inode_ag_iterator(mp, xfs_sync_inode_data, flags); if (error) return XFS_ERROR(error); @@ -337,8 +260,7 @@ xfs_sync_attr( { ASSERT((flags & ~SYNC_WAIT) == 0); - return xfs_inode_ag_iterator(mp, xfs_sync_inode_attr, flags, - XFS_ICI_NO_TAG, 0, NULL); + return xfs_inode_ag_iterator(mp, xfs_sync_inode_attr, flags); } STATIC int @@ -868,13 +790,72 @@ reclaim: } +/* + * Walk the AGs and reclaim the inodes in them. Even if the filesystem is + * corrupted, we still want to try to reclaim all the inodes. If we don't, + * then a shut down during filesystem unmount reclaim walk leak all the + * unreclaimed inodes. + */ +int +xfs_reclaim_inodes_ag( + struct xfs_mount *mp, + int flags, + int *nr_to_scan) +{ + struct xfs_perag *pag; + int error = 0; + int last_error = 0; + xfs_agnumber_t ag; + + ag = 0; + while ((pag = xfs_perag_get_tag(mp, ag, XFS_ICI_RECLAIM_TAG))) { + unsigned long first_index = 0; + int done = 0; + + ag = pag->pag_agno + 1; + + do { + struct xfs_inode *ip; + int nr_found; + + write_lock(&pag->pag_ici_lock); + nr_found = radix_tree_gang_lookup_tag(&pag->pag_ici_root, + (void **)&ip, first_index, 1, + XFS_ICI_RECLAIM_TAG); + if (!nr_found) { + write_unlock(&pag->pag_ici_lock); + break; + } + + /* + * Update the index for the next lookup. Catch overflows + * into the next AG range which can occur if we have inodes + * in the last block of the AG and we are currently + * pointing to the last inode. + */ + first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1); + if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) + done = 1; + + error = xfs_reclaim_inode(ip, pag, flags); + if (error && last_error != EFSCORRUPTED) + last_error = error; + + } while (!done && (*nr_to_scan)--); + + xfs_perag_put(pag); + } + return XFS_ERROR(last_error); +} + int xfs_reclaim_inodes( xfs_mount_t *mp, int mode) { - return xfs_inode_ag_iterator(mp, xfs_reclaim_inode, mode, - XFS_ICI_RECLAIM_TAG, 1, NULL); + int nr_to_scan = INT_MAX; + + return xfs_reclaim_inodes_ag(mp, mode, &nr_to_scan); } /* @@ -896,17 +877,16 @@ xfs_reclaim_inode_shrink( if (!(gfp_mask & __GFP_FS)) return -1; - xfs_inode_ag_iterator(mp, xfs_reclaim_inode, 0, - XFS_ICI_RECLAIM_TAG, 1, &nr_to_scan); - /* if we don't exhaust the scan, don't bother coming back */ + xfs_reclaim_inodes_ag(mp, 0, &nr_to_scan); + /* terminate if we don't exhaust the scan */ if (nr_to_scan > 0) return -1; } reclaimable = 0; ag = 0; - while ((pag = xfs_inode_ag_iter_next_pag(mp, &ag, - XFS_ICI_RECLAIM_TAG))) { + while ((pag = xfs_perag_get_tag(mp, ag, XFS_ICI_RECLAIM_TAG))) { + ag = pag->pag_agno + 1; reclaimable += pag->pag_ici_reclaimable; xfs_perag_put(pag); } diff --git a/fs/xfs/linux-2.6/xfs_sync.h b/fs/xfs/linux-2.6/xfs_sync.h index fe78726196f..e8a352896d2 100644 --- a/fs/xfs/linux-2.6/xfs_sync.h +++ b/fs/xfs/linux-2.6/xfs_sync.h @@ -50,7 +50,7 @@ void __xfs_inode_clear_reclaim_tag(struct xfs_mount *mp, struct xfs_perag *pag, int xfs_sync_inode_valid(struct xfs_inode *ip, struct xfs_perag *pag); int xfs_inode_ag_iterator(struct xfs_mount *mp, int (*execute)(struct xfs_inode *ip, struct xfs_perag *pag, int flags), - int flags, int tag, int write_lock, int *nr_to_scan); + int flags); void xfs_inode_shrinker_register(struct xfs_mount *mp); void xfs_inode_shrinker_unregister(struct xfs_mount *mp); diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index 2a1d4fbd9ed..286dc201c5b 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -124,7 +124,7 @@ DEFINE_EVENT(xfs_perag_class, name, \ unsigned long caller_ip), \ TP_ARGS(mp, agno, refcount, caller_ip)) DEFINE_PERAG_REF_EVENT(xfs_perag_get); -DEFINE_PERAG_REF_EVENT(xfs_perag_get_reclaim); +DEFINE_PERAG_REF_EVENT(xfs_perag_get_tag); DEFINE_PERAG_REF_EVENT(xfs_perag_put); DEFINE_PERAG_REF_EVENT(xfs_perag_set_reclaim); DEFINE_PERAG_REF_EVENT(xfs_perag_clear_reclaim); diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index 7a71336f792..ac11fbef37f 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -918,8 +918,7 @@ xfs_qm_dqrele_all_inodes( uint flags) { ASSERT(mp->m_quotainfo); - xfs_inode_ag_iterator(mp, xfs_dqrele_inode, flags, - XFS_ICI_NO_TAG, 0, NULL); + xfs_inode_ag_iterator(mp, xfs_dqrele_inode, flags); } /*------------------------------------------------------------------------*/ diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 912101d280b..d66e87c7c3a 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -219,6 +219,32 @@ xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno) return pag; } +/* + * search from @first to find the next perag with the given tag set. + */ +struct xfs_perag * +xfs_perag_get_tag( + struct xfs_mount *mp, + xfs_agnumber_t first, + int tag) +{ + struct xfs_perag *pag; + int found; + int ref; + + rcu_read_lock(); + found = radix_tree_gang_lookup_tag(&mp->m_perag_tree, + (void **)&pag, first, 1, tag); + if (found <= 0) { + rcu_read_unlock(); + return NULL; + } + ref = atomic_inc_return(&pag->pag_ref); + rcu_read_unlock(); + trace_xfs_perag_get_tag(mp, pag->pag_agno, ref, _RET_IP_); + return pag; +} + void xfs_perag_put(struct xfs_perag *pag) { diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 622da2179a5..7ab240930ba 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -327,6 +327,8 @@ xfs_daddr_to_agbno(struct xfs_mount *mp, xfs_daddr_t d) * perag get/put wrappers for ref counting */ struct xfs_perag *xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno); +struct xfs_perag *xfs_perag_get_tag(struct xfs_mount *mp, xfs_agnumber_t agno, + int tag); void xfs_perag_put(struct xfs_perag *pag); /* -- cgit v1.2.3-18-g5258 From e13de955ca67b0bd1cec9a2f9352a3053065bf7f Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 28 Sep 2010 12:28:06 +1000 Subject: xfs: split out inode walk inode grabbing When doing read side inode cache walks, the code to validate and grab an inode is common to all callers. Split it out of the execute callbacks in preparation for batching lookups. Similarly, split out the inode reference dropping from the execute callbacks into the main lookup look to be symmetric with the grab. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_sync.c | 79 ++++++++++++++++++------------------------ fs/xfs/quota/xfs_qm_syscalls.c | 9 ----- 2 files changed, 34 insertions(+), 54 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index b5cdf0ef39e..37fc2c0a4d2 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -39,6 +39,33 @@ #include #include +STATIC int +xfs_inode_ag_walk_grab( + struct xfs_inode *ip) +{ + struct inode *inode = VFS_I(ip); + + /* nothing to sync during shutdown */ + if (XFS_FORCED_SHUTDOWN(ip->i_mount)) + return EFSCORRUPTED; + + /* avoid new or reclaimable inodes. Leave for reclaim code to flush */ + if (xfs_iflags_test(ip, XFS_INEW | XFS_IRECLAIMABLE | XFS_IRECLAIM)) + return ENOENT; + + /* If we can't grab the inode, it must on it's way to reclaim. */ + if (!igrab(inode)) + return ENOENT; + + if (is_bad_inode(inode)) { + IRELE(ip); + return ENOENT; + } + + /* inode is valid */ + return 0; +} + STATIC int xfs_inode_ag_walk( @@ -80,8 +107,14 @@ restart: if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) done = 1; - /* execute releases pag->pag_ici_lock */ + if (xfs_inode_ag_walk_grab(ip)) { + read_unlock(&pag->pag_ici_lock); + continue; + } + read_unlock(&pag->pag_ici_lock); + error = execute(ip, pag, flags); + IRELE(ip); if (error == EAGAIN) { skipped++; continue; @@ -128,40 +161,6 @@ xfs_inode_ag_iterator( return XFS_ERROR(last_error); } -/* must be called with pag_ici_lock held and releases it */ -int -xfs_sync_inode_valid( - struct xfs_inode *ip, - struct xfs_perag *pag) -{ - struct inode *inode = VFS_I(ip); - int error = EFSCORRUPTED; - - /* nothing to sync during shutdown */ - if (XFS_FORCED_SHUTDOWN(ip->i_mount)) - goto out_unlock; - - /* avoid new or reclaimable inodes. Leave for reclaim code to flush */ - error = ENOENT; - if (xfs_iflags_test(ip, XFS_INEW | XFS_IRECLAIMABLE | XFS_IRECLAIM)) - goto out_unlock; - - /* If we can't grab the inode, it must on it's way to reclaim. */ - if (!igrab(inode)) - goto out_unlock; - - if (is_bad_inode(inode)) { - IRELE(ip); - goto out_unlock; - } - - /* inode is valid */ - error = 0; -out_unlock: - read_unlock(&pag->pag_ici_lock); - return error; -} - STATIC int xfs_sync_inode_data( struct xfs_inode *ip, @@ -172,10 +171,6 @@ xfs_sync_inode_data( struct address_space *mapping = inode->i_mapping; int error = 0; - error = xfs_sync_inode_valid(ip, pag); - if (error) - return error; - if (!mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) goto out_wait; @@ -192,7 +187,6 @@ xfs_sync_inode_data( out_wait: if (flags & SYNC_WAIT) xfs_ioend_wait(ip); - IRELE(ip); return error; } @@ -204,10 +198,6 @@ xfs_sync_inode_attr( { int error = 0; - error = xfs_sync_inode_valid(ip, pag); - if (error) - return error; - xfs_ilock(ip, XFS_ILOCK_SHARED); if (xfs_inode_clean(ip)) goto out_unlock; @@ -226,7 +216,6 @@ xfs_sync_inode_attr( out_unlock: xfs_iunlock(ip, XFS_ILOCK_SHARED); - IRELE(ip); return error; } diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index ac11fbef37f..57847434fa5 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -875,21 +875,14 @@ xfs_dqrele_inode( struct xfs_perag *pag, int flags) { - int error; - /* skip quota inodes */ if (ip == ip->i_mount->m_quotainfo->qi_uquotaip || ip == ip->i_mount->m_quotainfo->qi_gquotaip) { ASSERT(ip->i_udquot == NULL); ASSERT(ip->i_gdquot == NULL); - read_unlock(&pag->pag_ici_lock); return 0; } - error = xfs_sync_inode_valid(ip, pag); - if (error) - return error; - xfs_ilock(ip, XFS_ILOCK_EXCL); if ((flags & XFS_UQUOTA_ACCT) && ip->i_udquot) { xfs_qm_dqrele(ip->i_udquot); @@ -900,8 +893,6 @@ xfs_dqrele_inode( ip->i_gdquot = NULL; } xfs_iunlock(ip, XFS_ILOCK_EXCL); - - IRELE(ip); return 0; } -- cgit v1.2.3-18-g5258 From 78ae5256768b91f25ce7a4eb9f56d563e302cc10 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 28 Sep 2010 12:28:19 +1000 Subject: xfs: implement batched inode lookups for AG walking With the reclaim code separated from the generic walking code, it is simple to implement batched lookups for the generic walk code. Separate out the inode validation from the execute operations and modify the tree lookups to get a batch of inodes at a time. Reclaim operations will be optimised separately. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_sync.c | 66 ++++++++++++++++++++++++++++++--------------- fs/xfs/linux-2.6/xfs_sync.h | 2 +- 2 files changed, 45 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 37fc2c0a4d2..0ed3d0ae3c2 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -39,6 +39,14 @@ #include #include +/* + * The inode lookup is done in batches to keep the amount of lock traffic and + * radix tree lookups to a minimum. The batch size is a trade off between + * lookup reduction and stack usage. This is in the reclaim path, so we can't + * be too greedy. + */ +#define XFS_LOOKUP_BATCH 32 + STATIC int xfs_inode_ag_walk_grab( struct xfs_inode *ip) @@ -66,7 +74,6 @@ xfs_inode_ag_walk_grab( return 0; } - STATIC int xfs_inode_ag_walk( struct xfs_mount *mp, @@ -79,54 +86,69 @@ xfs_inode_ag_walk( int last_error = 0; int skipped; int done; + int nr_found; restart: done = 0; skipped = 0; first_index = 0; + nr_found = 0; do { + struct xfs_inode *batch[XFS_LOOKUP_BATCH]; int error = 0; - int nr_found; - xfs_inode_t *ip; + int i; read_lock(&pag->pag_ici_lock); nr_found = radix_tree_gang_lookup(&pag->pag_ici_root, - (void **)&ip, first_index, 1); + (void **)batch, first_index, + XFS_LOOKUP_BATCH); if (!nr_found) { read_unlock(&pag->pag_ici_lock); break; } /* - * Update the index for the next lookup. Catch overflows - * into the next AG range which can occur if we have inodes - * in the last block of the AG and we are currently - * pointing to the last inode. + * Grab the inodes before we drop the lock. if we found + * nothing, nr == 0 and the loop will be skipped. */ - first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1); - if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) - done = 1; + for (i = 0; i < nr_found; i++) { + struct xfs_inode *ip = batch[i]; - if (xfs_inode_ag_walk_grab(ip)) { - read_unlock(&pag->pag_ici_lock); - continue; + if (done || xfs_inode_ag_walk_grab(ip)) + batch[i] = NULL; + + /* + * Update the index for the next lookup. Catch overflows + * into the next AG range which can occur if we have inodes + * in the last block of the AG and we are currently + * pointing to the last inode. + */ + first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1); + if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) + done = 1; } + + /* unlock now we've grabbed the inodes. */ read_unlock(&pag->pag_ici_lock); - error = execute(ip, pag, flags); - IRELE(ip); - if (error == EAGAIN) { - skipped++; - continue; + for (i = 0; i < nr_found; i++) { + if (!batch[i]) + continue; + error = execute(batch[i], pag, flags); + IRELE(batch[i]); + if (error == EAGAIN) { + skipped++; + continue; + } + if (error && last_error != EFSCORRUPTED) + last_error = error; } - if (error) - last_error = error; /* bail out if the filesystem is corrupted. */ if (error == EFSCORRUPTED) break; - } while (!done); + } while (nr_found && !done); if (skipped) { delay(1); diff --git a/fs/xfs/linux-2.6/xfs_sync.h b/fs/xfs/linux-2.6/xfs_sync.h index e8a352896d2..32ba6628290 100644 --- a/fs/xfs/linux-2.6/xfs_sync.h +++ b/fs/xfs/linux-2.6/xfs_sync.h @@ -47,7 +47,7 @@ void __xfs_inode_set_reclaim_tag(struct xfs_perag *pag, struct xfs_inode *ip); void __xfs_inode_clear_reclaim_tag(struct xfs_mount *mp, struct xfs_perag *pag, struct xfs_inode *ip); -int xfs_sync_inode_valid(struct xfs_inode *ip, struct xfs_perag *pag); +int xfs_sync_inode_grab(struct xfs_inode *ip); int xfs_inode_ag_iterator(struct xfs_mount *mp, int (*execute)(struct xfs_inode *ip, struct xfs_perag *pag, int flags), int flags); -- cgit v1.2.3-18-g5258 From e3a20c0b02e1704ab115dfa9d012caf0fbc45ed0 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 24 Sep 2010 19:51:50 +1000 Subject: xfs: batch inode reclaim lookup Batch and optimise the per-ag inode lookup for reclaim to minimise scanning overhead. This involves gang lookups on the radix trees to get multiple inodes during each tree walk, and tighter validation of what inodes can be reclaimed without blocking befor we take any locks. This is based on ideas suggested in a proof-of-concept patch posted by Nick Piggin. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_sync.c | 110 +++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 33 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 0ed3d0ae3c2..754bc591a24 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -630,6 +630,43 @@ __xfs_inode_clear_reclaim_tag( __xfs_inode_clear_reclaim(pag, ip); } +/* + * Grab the inode for reclaim exclusively. + * Return 0 if we grabbed it, non-zero otherwise. + */ +STATIC int +xfs_reclaim_inode_grab( + struct xfs_inode *ip, + int flags) +{ + + /* + * do some unlocked checks first to avoid unnecceary lock traffic. + * The first is a flush lock check, the second is a already in reclaim + * check. Only do these checks if we are not going to block on locks. + */ + if ((flags & SYNC_TRYLOCK) && + (!ip->i_flush.done || __xfs_iflags_test(ip, XFS_IRECLAIM))) { + return 1; + } + + /* + * The radix tree lock here protects a thread in xfs_iget from racing + * with us starting reclaim on the inode. Once we have the + * XFS_IRECLAIM flag set it will not touch us. + */ + spin_lock(&ip->i_flags_lock); + ASSERT_ALWAYS(__xfs_iflags_test(ip, XFS_IRECLAIMABLE)); + if (__xfs_iflags_test(ip, XFS_IRECLAIM)) { + /* ignore as it is already under reclaim */ + spin_unlock(&ip->i_flags_lock); + return 1; + } + __xfs_iflags_set(ip, XFS_IRECLAIM); + spin_unlock(&ip->i_flags_lock); + return 0; +} + /* * Inodes in different states need to be treated differently, and the return * value of xfs_iflush is not sufficient to get this right. The following table @@ -688,23 +725,6 @@ xfs_reclaim_inode( { int error = 0; - /* - * The radix tree lock here protects a thread in xfs_iget from racing - * with us starting reclaim on the inode. Once we have the - * XFS_IRECLAIM flag set it will not touch us. - */ - spin_lock(&ip->i_flags_lock); - ASSERT_ALWAYS(__xfs_iflags_test(ip, XFS_IRECLAIMABLE)); - if (__xfs_iflags_test(ip, XFS_IRECLAIM)) { - /* ignore as it is already under reclaim */ - spin_unlock(&ip->i_flags_lock); - write_unlock(&pag->pag_ici_lock); - return 0; - } - __xfs_iflags_set(ip, XFS_IRECLAIM); - spin_unlock(&ip->i_flags_lock); - write_unlock(&pag->pag_ici_lock); - xfs_ilock(ip, XFS_ILOCK_EXCL); if (!xfs_iflock_nowait(ip)) { if (!(sync_mode & SYNC_WAIT)) @@ -822,16 +842,19 @@ xfs_reclaim_inodes_ag( while ((pag = xfs_perag_get_tag(mp, ag, XFS_ICI_RECLAIM_TAG))) { unsigned long first_index = 0; int done = 0; + int nr_found = 0; ag = pag->pag_agno + 1; do { - struct xfs_inode *ip; - int nr_found; + struct xfs_inode *batch[XFS_LOOKUP_BATCH]; + int i; write_lock(&pag->pag_ici_lock); - nr_found = radix_tree_gang_lookup_tag(&pag->pag_ici_root, - (void **)&ip, first_index, 1, + nr_found = radix_tree_gang_lookup_tag( + &pag->pag_ici_root, + (void **)batch, first_index, + XFS_LOOKUP_BATCH, XFS_ICI_RECLAIM_TAG); if (!nr_found) { write_unlock(&pag->pag_ici_lock); @@ -839,20 +862,41 @@ xfs_reclaim_inodes_ag( } /* - * Update the index for the next lookup. Catch overflows - * into the next AG range which can occur if we have inodes - * in the last block of the AG and we are currently - * pointing to the last inode. + * Grab the inodes before we drop the lock. if we found + * nothing, nr == 0 and the loop will be skipped. */ - first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1); - if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) - done = 1; + for (i = 0; i < nr_found; i++) { + struct xfs_inode *ip = batch[i]; + + if (done || xfs_reclaim_inode_grab(ip, flags)) + batch[i] = NULL; + + /* + * Update the index for the next lookup. Catch + * overflows into the next AG range which can + * occur if we have inodes in the last block of + * the AG and we are currently pointing to the + * last inode. + */ + first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1); + if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) + done = 1; + } - error = xfs_reclaim_inode(ip, pag, flags); - if (error && last_error != EFSCORRUPTED) - last_error = error; + /* unlock now we've grabbed the inodes. */ + write_unlock(&pag->pag_ici_lock); + + for (i = 0; i < nr_found; i++) { + if (!batch[i]) + continue; + error = xfs_reclaim_inode(batch[i], pag, flags); + if (error && last_error != EFSCORRUPTED) + last_error = error; + } + + *nr_to_scan -= XFS_LOOKUP_BATCH; - } while (!done && (*nr_to_scan)--); + } while (nr_found && !done && *nr_to_scan > 0); xfs_perag_put(pag); } @@ -888,7 +932,7 @@ xfs_reclaim_inode_shrink( if (!(gfp_mask & __GFP_FS)) return -1; - xfs_reclaim_inodes_ag(mp, 0, &nr_to_scan); + xfs_reclaim_inodes_ag(mp, SYNC_TRYLOCK, &nr_to_scan); /* terminate if we don't exhaust the scan */ if (nr_to_scan > 0) return -1; -- cgit v1.2.3-18-g5258 From 69b491c214d7fd4d4df972ae5377be99ca3753db Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 27 Sep 2010 11:09:51 +1000 Subject: xfs: serialise inode reclaim within an AG Memory reclaim via shrinkers has a terrible habit of having N+M concurrent shrinker executions (N = num CPUs, M = num kswapds) all trying to shrink the same cache. When the cache they are all working on is protected by a single spinlock, massive contention an slowdowns occur. Wrap the per-ag inode caches with a reclaim mutex to serialise reclaim access to the AG. This will block concurrent reclaim in each AG but still allow reclaim to scan multiple AGs concurrently. Allow shrinkers to move on to the next AG if it can't get the lock, and if we can't get any AG, then start blocking on locks. To prevent reclaimers from continually scanning the same inodes in each AG, add a cursor that tracks where the last reclaim got up to and start from that point on the next reclaim. This should avoid only ever scanning a small number of inodes at the satart of each AG and not making progress. If we have a non-shrinker based reclaim pass, ignore the cursor and reset it to zero once we are done. Signed-off-by: Dave Chinner Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_sync.c | 30 ++++++++++++++++++++++++++++++ fs/xfs/xfs_ag.h | 2 ++ fs/xfs/xfs_mount.c | 1 + 3 files changed, 33 insertions(+) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 754bc591a24..37d33254981 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -837,8 +837,12 @@ xfs_reclaim_inodes_ag( int error = 0; int last_error = 0; xfs_agnumber_t ag; + int trylock = flags & SYNC_TRYLOCK; + int skipped; +restart: ag = 0; + skipped = 0; while ((pag = xfs_perag_get_tag(mp, ag, XFS_ICI_RECLAIM_TAG))) { unsigned long first_index = 0; int done = 0; @@ -846,6 +850,15 @@ xfs_reclaim_inodes_ag( ag = pag->pag_agno + 1; + if (trylock) { + if (!mutex_trylock(&pag->pag_ici_reclaim_lock)) { + skipped++; + continue; + } + first_index = pag->pag_ici_reclaim_cursor; + } else + mutex_lock(&pag->pag_ici_reclaim_lock); + do { struct xfs_inode *batch[XFS_LOOKUP_BATCH]; int i; @@ -898,8 +911,25 @@ xfs_reclaim_inodes_ag( } while (nr_found && !done && *nr_to_scan > 0); + if (trylock && !done) + pag->pag_ici_reclaim_cursor = first_index; + else + pag->pag_ici_reclaim_cursor = 0; + mutex_unlock(&pag->pag_ici_reclaim_lock); xfs_perag_put(pag); } + + /* + * if we skipped any AG, and we still have scan count remaining, do + * another pass this time using blocking reclaim semantics (i.e + * waiting on the reclaim locks and ignoring the reclaim cursors). This + * ensure that when we get more reclaimers than AGs we block rather + * than spin trying to execute reclaim. + */ + if (trylock && skipped && *nr_to_scan > 0) { + trylock = 0; + goto restart; + } return XFS_ERROR(last_error); } diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h index 51c42c202bf..baeec83d01f 100644 --- a/fs/xfs/xfs_ag.h +++ b/fs/xfs/xfs_ag.h @@ -230,6 +230,8 @@ typedef struct xfs_perag { rwlock_t pag_ici_lock; /* incore inode lock */ struct radix_tree_root pag_ici_root; /* incore inode cache root */ int pag_ici_reclaimable; /* reclaimable inodes */ + struct mutex pag_ici_reclaim_lock; /* serialisation point */ + unsigned long pag_ici_reclaim_cursor; /* reclaim restart point */ /* for rcu-safe freeing */ struct rcu_head rcu_head; diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index d66e87c7c3a..59859c343e0 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -477,6 +477,7 @@ xfs_initialize_perag( pag->pag_agno = index; pag->pag_mount = mp; rwlock_init(&pag->pag_ici_lock); + mutex_init(&pag->pag_ici_reclaim_lock); INIT_RADIX_TREE(&pag->pag_ici_root, GFP_ATOMIC); if (radix_tree_preload(GFP_NOFS)) -- cgit v1.2.3-18-g5258 From 74f75a0cb7033918eb0fa4a50df25091ac75c16e Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 24 Sep 2010 19:59:04 +1000 Subject: xfs: convert buffer cache hash to rbtree The buffer cache hash is showing typical hash scalability problems. In large scale testing the number of cached items growing far larger than the hash can efficiently handle. Hence we need to move to a self-scaling cache indexing mechanism. I have selected rbtrees for indexing becuse they can have O(log n) search scalability, and insert and remove cost is not excessive, even on large trees. Hence we should be able to cache large numbers of buffers without incurring the excessive cache miss search penalties that the hash is imposing on us. To ensure we still have parallel access to the cache, we need multiple trees. Rather than hashing the buffers by disk address to select a tree, it seems more sensible to separate trees by typical access patterns. Most operations use buffers from within a single AG at a time, so rather than searching lots of different lists, separate the buffer indexes out into per-AG rbtrees. This means that searches during metadata operation have a much higher chance of hitting cache resident nodes, and that updates of the tree are less likely to disturb trees being accessed on other CPUs doing independent operations. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_buf.c | 136 ++++++++++++++++++++++----------------------- fs/xfs/linux-2.6/xfs_buf.h | 8 +-- fs/xfs/xfs_ag.h | 4 ++ fs/xfs/xfs_mount.c | 2 + 4 files changed, 74 insertions(+), 76 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 251bcdc6352..749d7d39d65 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -188,7 +188,7 @@ _xfs_buf_initialize( atomic_set(&bp->b_hold, 1); init_completion(&bp->b_iowait); INIT_LIST_HEAD(&bp->b_list); - INIT_LIST_HEAD(&bp->b_hash_list); + RB_CLEAR_NODE(&bp->b_rbnode); init_MUTEX_LOCKED(&bp->b_sema); /* held, no waiters */ XB_SET_OWNER(bp); bp->b_target = target; @@ -262,8 +262,6 @@ xfs_buf_free( { trace_xfs_buf_free(bp, _RET_IP_); - ASSERT(list_empty(&bp->b_hash_list)); - if (bp->b_flags & (_XBF_PAGE_CACHE|_XBF_PAGES)) { uint i; @@ -422,8 +420,10 @@ _xfs_buf_find( { xfs_off_t range_base; size_t range_length; - xfs_bufhash_t *hash; - xfs_buf_t *bp, *n; + struct xfs_perag *pag; + struct rb_node **rbp; + struct rb_node *parent; + xfs_buf_t *bp; range_base = (ioff << BBSHIFT); range_length = (isize << BBSHIFT); @@ -432,14 +432,37 @@ _xfs_buf_find( ASSERT(!(range_length < (1 << btp->bt_sshift))); ASSERT(!(range_base & (xfs_off_t)btp->bt_smask)); - hash = &btp->bt_hash[hash_long((unsigned long)ioff, btp->bt_hashshift)]; - - spin_lock(&hash->bh_lock); - - list_for_each_entry_safe(bp, n, &hash->bh_list, b_hash_list) { - ASSERT(btp == bp->b_target); - if (bp->b_file_offset == range_base && - bp->b_buffer_length == range_length) { + /* get tree root */ + pag = xfs_perag_get(btp->bt_mount, + xfs_daddr_to_agno(btp->bt_mount, ioff)); + + /* walk tree */ + spin_lock(&pag->pag_buf_lock); + rbp = &pag->pag_buf_tree.rb_node; + parent = NULL; + bp = NULL; + while (*rbp) { + parent = *rbp; + bp = rb_entry(parent, struct xfs_buf, b_rbnode); + + if (range_base < bp->b_file_offset) + rbp = &(*rbp)->rb_left; + else if (range_base > bp->b_file_offset) + rbp = &(*rbp)->rb_right; + else { + /* + * found a block offset match. If the range doesn't + * match, the only way this is allowed is if the buffer + * in the cache is stale and the transaction that made + * it stale has not yet committed. i.e. we are + * reallocating a busy extent. Skip this buffer and + * continue searching to the right for an exact match. + */ + if (bp->b_buffer_length != range_length) { + ASSERT(bp->b_flags & XBF_STALE); + rbp = &(*rbp)->rb_right; + continue; + } atomic_inc(&bp->b_hold); goto found; } @@ -449,17 +472,21 @@ _xfs_buf_find( if (new_bp) { _xfs_buf_initialize(new_bp, btp, range_base, range_length, flags); - new_bp->b_hash = hash; - list_add(&new_bp->b_hash_list, &hash->bh_list); + rb_link_node(&new_bp->b_rbnode, parent, rbp); + rb_insert_color(&new_bp->b_rbnode, &pag->pag_buf_tree); + /* the buffer keeps the perag reference until it is freed */ + new_bp->b_pag = pag; + spin_unlock(&pag->pag_buf_lock); } else { XFS_STATS_INC(xb_miss_locked); + spin_unlock(&pag->pag_buf_lock); + xfs_perag_put(pag); } - - spin_unlock(&hash->bh_lock); return new_bp; found: - spin_unlock(&hash->bh_lock); + spin_unlock(&pag->pag_buf_lock); + xfs_perag_put(pag); /* Attempt to get the semaphore without sleeping, * if this does not work then we need to drop the @@ -809,27 +836,30 @@ void xfs_buf_rele( xfs_buf_t *bp) { - xfs_bufhash_t *hash = bp->b_hash; + struct xfs_perag *pag = bp->b_pag; trace_xfs_buf_rele(bp, _RET_IP_); - if (unlikely(!hash)) { + if (!pag) { ASSERT(!bp->b_relse); + ASSERT(RB_EMPTY_NODE(&bp->b_rbnode)); if (atomic_dec_and_test(&bp->b_hold)) xfs_buf_free(bp); return; } + ASSERT(!RB_EMPTY_NODE(&bp->b_rbnode)); ASSERT(atomic_read(&bp->b_hold) > 0); - if (atomic_dec_and_lock(&bp->b_hold, &hash->bh_lock)) { + if (atomic_dec_and_lock(&bp->b_hold, &pag->pag_buf_lock)) { if (bp->b_relse) { atomic_inc(&bp->b_hold); - spin_unlock(&hash->bh_lock); - (*(bp->b_relse)) (bp); + spin_unlock(&pag->pag_buf_lock); + bp->b_relse(bp); } else { ASSERT(!(bp->b_flags & (XBF_DELWRI|_XBF_DELWRI_Q))); - list_del_init(&bp->b_hash_list); - spin_unlock(&hash->bh_lock); + rb_erase(&bp->b_rbnode, &pag->pag_buf_tree); + spin_unlock(&pag->pag_buf_lock); + xfs_perag_put(pag); xfs_buf_free(bp); } } @@ -1429,56 +1459,24 @@ xfs_buf_iomove( */ void xfs_wait_buftarg( - xfs_buftarg_t *btp) + struct xfs_buftarg *btp) { - xfs_bufhash_t *hash; - uint i; + struct xfs_perag *pag; + uint i; - for (i = 0; i < (1 << btp->bt_hashshift); i++) { - hash = &btp->bt_hash[i]; - spin_lock(&hash->bh_lock); - while (!list_empty(&hash->bh_list)) { - spin_unlock(&hash->bh_lock); + for (i = 0; i < btp->bt_mount->m_sb.sb_agcount; i++) { + pag = xfs_perag_get(btp->bt_mount, i); + spin_lock(&pag->pag_buf_lock); + while (rb_first(&pag->pag_buf_tree)) { + spin_unlock(&pag->pag_buf_lock); delay(100); - spin_lock(&hash->bh_lock); + spin_lock(&pag->pag_buf_lock); } - spin_unlock(&hash->bh_lock); - } -} - -/* - * Allocate buffer hash table for a given target. - * For devices containing metadata (i.e. not the log/realtime devices) - * we need to allocate a much larger hash table. - */ -STATIC void -xfs_alloc_bufhash( - xfs_buftarg_t *btp, - int external) -{ - unsigned int i; - - if (external) { - btp->bt_hash = NULL; - return; - } - btp->bt_hashshift = 12; /* 4096 buckets */ - btp->bt_hash = kmem_zalloc_large((1 << btp->bt_hashshift) * - sizeof(xfs_bufhash_t)); - for (i = 0; i < (1 << btp->bt_hashshift); i++) { - spin_lock_init(&btp->bt_hash[i].bh_lock); - INIT_LIST_HEAD(&btp->bt_hash[i].bh_list); + spin_unlock(&pag->pag_buf_lock); + xfs_perag_put(pag); } } -STATIC void -xfs_free_bufhash( - xfs_buftarg_t *btp) -{ - kmem_free_large(btp->bt_hash); - btp->bt_hash = NULL; -} - /* * buftarg list for delwrite queue processing */ @@ -1511,7 +1509,6 @@ xfs_free_buftarg( xfs_flush_buftarg(btp, 1); if (mp->m_flags & XFS_MOUNT_BARRIER) xfs_blkdev_issue_flush(btp); - xfs_free_bufhash(btp); iput(btp->bt_mapping->host); /* Unregister the buftarg first so that we don't get a @@ -1651,7 +1648,6 @@ xfs_alloc_buftarg( goto error; if (xfs_alloc_delwrite_queue(btp, fsname)) goto error; - xfs_alloc_bufhash(btp, external); return btp; error: diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index 1f109cee136..c79882ecb51 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -135,10 +135,6 @@ typedef struct xfs_buftarg { unsigned int bt_sshift; size_t bt_smask; - /* per device buffer hash table */ - uint bt_hashshift; - xfs_bufhash_t *bt_hash; - /* per device delwri queue */ struct task_struct *bt_task; struct list_head bt_list; @@ -172,8 +168,8 @@ typedef struct xfs_buf { wait_queue_head_t b_waiters; /* unpin waiters */ struct list_head b_list; xfs_buf_flags_t b_flags; /* status flags */ - struct list_head b_hash_list; /* hash table list */ - xfs_bufhash_t *b_hash; /* hash table list start */ + struct rb_node b_rbnode; /* rbtree node */ + struct xfs_perag *b_pag; /* contains rbtree root */ xfs_buftarg_t *b_target; /* buffer target (device) */ atomic_t b_hold; /* reference count */ xfs_daddr_t b_bn; /* block number for I/O */ diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h index baeec83d01f..63c7a1a6c02 100644 --- a/fs/xfs/xfs_ag.h +++ b/fs/xfs/xfs_ag.h @@ -233,6 +233,10 @@ typedef struct xfs_perag { struct mutex pag_ici_reclaim_lock; /* serialisation point */ unsigned long pag_ici_reclaim_cursor; /* reclaim restart point */ + /* buffer cache index */ + spinlock_t pag_buf_lock; /* lock for pag_buf_tree */ + struct rb_root pag_buf_tree; /* ordered tree of active buffers */ + /* for rcu-safe freeing */ struct rcu_head rcu_head; #endif diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 59859c343e0..cfa2fb4e7f9 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -479,6 +479,8 @@ xfs_initialize_perag( rwlock_init(&pag->pag_ici_lock); mutex_init(&pag->pag_ici_reclaim_lock); INIT_RADIX_TREE(&pag->pag_ici_root, GFP_ATOMIC); + spin_lock_init(&pag->pag_buf_lock); + pag->pag_buf_tree = RB_ROOT; if (radix_tree_preload(GFP_NOFS)) goto out_unwind; -- cgit v1.2.3-18-g5258 From 50f59e8eed85ec4c79bc2454ed50c7886f6c5ebf Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 24 Sep 2010 19:59:15 +1000 Subject: xfs: pack xfs_buf structure more tightly pahole reports the struct xfs_buf has quite a few holes in it, so packing the structure better will reduce the size of it by 16 bytes. Also, move all the fields used in cache lookups into the first cacheline. Before on x86_64: /* size: 320, cachelines: 5 */ /* sum members: 298, holes: 6, sum holes: 22 */ After on x86_64: /* size: 304, cachelines: 5 */ /* padding: 6 */ /* last cacheline: 48 bytes */ Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder --- fs/xfs/linux-2.6/xfs_buf.h | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index c79882ecb51..161333785f6 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -162,33 +162,41 @@ typedef int (*xfs_buf_bdstrat_t)(struct xfs_buf *); #define XB_PAGES 2 typedef struct xfs_buf { + /* + * first cacheline holds all the fields needed for an uncontended cache + * hit to be fully processed. The semaphore straddles the cacheline + * boundary, but the counter and lock sits on the first cacheline, + * which is the only bit that is touched if we hit the semaphore + * fast-path on locking. + */ + struct rb_node b_rbnode; /* rbtree node */ + xfs_off_t b_file_offset; /* offset in file */ + size_t b_buffer_length;/* size of buffer in bytes */ + atomic_t b_hold; /* reference count */ + xfs_buf_flags_t b_flags; /* status flags */ struct semaphore b_sema; /* semaphore for lockables */ - unsigned long b_queuetime; /* time buffer was queued */ - atomic_t b_pin_count; /* pin count */ + wait_queue_head_t b_waiters; /* unpin waiters */ struct list_head b_list; - xfs_buf_flags_t b_flags; /* status flags */ - struct rb_node b_rbnode; /* rbtree node */ struct xfs_perag *b_pag; /* contains rbtree root */ xfs_buftarg_t *b_target; /* buffer target (device) */ - atomic_t b_hold; /* reference count */ xfs_daddr_t b_bn; /* block number for I/O */ - xfs_off_t b_file_offset; /* offset in file */ - size_t b_buffer_length;/* size of buffer in bytes */ size_t b_count_desired;/* desired transfer size */ void *b_addr; /* virtual address of buffer */ struct work_struct b_iodone_work; - atomic_t b_io_remaining; /* #outstanding I/O requests */ xfs_buf_iodone_t b_iodone; /* I/O completion function */ xfs_buf_relse_t b_relse; /* releasing function */ struct completion b_iowait; /* queue for I/O waiters */ void *b_fspriv; void *b_fspriv2; - unsigned short b_error; /* error code on I/O */ - unsigned int b_page_count; /* size of page array */ - unsigned int b_offset; /* page offset in first page */ struct page **b_pages; /* array of page pointers */ struct page *b_page_array[XB_PAGES]; /* inline pages */ + unsigned long b_queuetime; /* time buffer was queued */ + atomic_t b_pin_count; /* pin count */ + atomic_t b_io_remaining; /* #outstanding I/O requests */ + unsigned int b_page_count; /* size of page array */ + unsigned int b_offset; /* page offset in first page */ + unsigned short b_error; /* error code on I/O */ #ifdef XFS_BUF_LOCK_TRACKING int b_last_holder; #endif -- cgit v1.2.3-18-g5258 From 61ba35dea0593fbc8d062cab3e4c4c3da5ce7104 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 30 Sep 2010 02:25:54 +0000 Subject: xfs: remove XFS_MOUNT_NO_PERCPU_SB Fail the mount if we can't allocate memory for the per-CPU counters. This is consistent with how we handle everything else in the mount path and makes the superblock counter modification a lot simpler. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_super.c | 6 ++++-- fs/xfs/xfs_mount.c | 40 +++++++++++++++------------------------- fs/xfs/xfs_mount.h | 2 -- 3 files changed, 19 insertions(+), 29 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 4759be4daa2..a316c731670 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1518,8 +1518,9 @@ xfs_fs_fill_super( if (error) goto out_free_fsname; - if (xfs_icsb_init_counters(mp)) - mp->m_flags |= XFS_MOUNT_NO_PERCPU_SB; + error = xfs_icsb_init_counters(mp); + if (error) + goto out_close_devices; error = xfs_readsb(mp, flags); if (error) @@ -1580,6 +1581,7 @@ xfs_fs_fill_super( xfs_freesb(mp); out_destroy_counters: xfs_icsb_destroy_counters(mp); + out_close_devices: xfs_close_devices(mp); out_free_fsname: xfs_free_fsname(mp); diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index cfa2fb4e7f9..55de83585e0 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1856,12 +1856,8 @@ xfs_mod_incore_sb( case XFS_SBS_ICOUNT: case XFS_SBS_IFREE: case XFS_SBS_FDBLOCKS: - if (!(mp->m_flags & XFS_MOUNT_NO_PERCPU_SB)) { - status = xfs_icsb_modify_counters(mp, field, - delta, rsvd); - break; - } - /* FALLTHROUGH */ + status = xfs_icsb_modify_counters(mp, field, delta, rsvd); + break; #endif default: spin_lock(&mp->m_sb_lock); @@ -1910,15 +1906,12 @@ xfs_mod_incore_sb_batch(xfs_mount_t *mp, xfs_mod_sb_t *msb, uint nmsb, int rsvd) case XFS_SBS_ICOUNT: case XFS_SBS_IFREE: case XFS_SBS_FDBLOCKS: - if (!(mp->m_flags & XFS_MOUNT_NO_PERCPU_SB)) { - spin_unlock(&mp->m_sb_lock); - status = xfs_icsb_modify_counters(mp, - msbp->msb_field, - msbp->msb_delta, rsvd); - spin_lock(&mp->m_sb_lock); - break; - } - /* FALLTHROUGH */ + spin_unlock(&mp->m_sb_lock); + status = xfs_icsb_modify_counters(mp, + msbp->msb_field, + msbp->msb_delta, rsvd); + spin_lock(&mp->m_sb_lock); + break; #endif default: status = xfs_mod_incore_sb_unlocked(mp, @@ -1948,16 +1941,13 @@ xfs_mod_incore_sb_batch(xfs_mount_t *mp, xfs_mod_sb_t *msb, uint nmsb, int rsvd) case XFS_SBS_ICOUNT: case XFS_SBS_IFREE: case XFS_SBS_FDBLOCKS: - if (!(mp->m_flags & XFS_MOUNT_NO_PERCPU_SB)) { - spin_unlock(&mp->m_sb_lock); - status = xfs_icsb_modify_counters(mp, - msbp->msb_field, - -(msbp->msb_delta), - rsvd); - spin_lock(&mp->m_sb_lock); - break; - } - /* FALLTHROUGH */ + spin_unlock(&mp->m_sb_lock); + status = xfs_icsb_modify_counters(mp, + msbp->msb_field, + -(msbp->msb_delta), + rsvd); + spin_lock(&mp->m_sb_lock); + break; #endif default: status = xfs_mod_incore_sb_unlocked(mp, diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 7ab240930ba..a9d366e7656 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -232,8 +232,6 @@ typedef struct xfs_mount { #define XFS_MOUNT_DIRSYNC (1ULL << 21) /* synchronous directory ops */ #define XFS_MOUNT_COMPAT_IOSIZE (1ULL << 22) /* don't report large preferred * I/O size in stat() */ -#define XFS_MOUNT_NO_PERCPU_SB (1ULL << 23) /* don't use per-cpu superblock - counters */ #define XFS_MOUNT_FILESTREAMS (1ULL << 24) /* enable the filestreams allocator */ #define XFS_MOUNT_NOATTR2 (1ULL << 25) /* disable use of attr2 format */ -- cgit v1.2.3-18-g5258 From 96540c78583a417113df4d027e6b68a595ab9a09 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 30 Sep 2010 02:25:55 +0000 Subject: xfs: do not use xfs_mod_incore_sb for per-cpu counters Export xfs_icsb_modify_counters and always use it for modifying the per-cpu counters. Remove support for per-cpu counters from xfs_mod_incore_sb to simplify it. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_bmap.c | 32 ++++++++++++++++++-------------- fs/xfs/xfs_fsops.c | 3 ++- fs/xfs/xfs_mount.c | 32 +++++++++----------------------- fs/xfs/xfs_mount.h | 4 ++++ fs/xfs/xfs_trans.c | 4 ++-- 5 files changed, 35 insertions(+), 40 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index 5e33b7862d4..8abd12e32e1 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -614,7 +614,7 @@ xfs_bmap_add_extent( nblks += cur->bc_private.b.allocated; ASSERT(nblks <= da_old); if (nblks < da_old) - xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS, + xfs_icsb_modify_counters(ip->i_mount, XFS_SBS_FDBLOCKS, (int64_t)(da_old - nblks), rsvd); } /* @@ -1079,7 +1079,8 @@ xfs_bmap_add_extent_delay_real( diff = (int)(temp + temp2 - startblockval(PREV.br_startblock) - (cur ? cur->bc_private.b.allocated : 0)); if (diff > 0 && - xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS, -((int64_t)diff), rsvd)) { + xfs_icsb_modify_counters(ip->i_mount, XFS_SBS_FDBLOCKS, + -((int64_t)diff), rsvd)) { /* * Ick gross gag me with a spoon. */ @@ -1089,16 +1090,18 @@ xfs_bmap_add_extent_delay_real( temp--; diff--; if (!diff || - !xfs_mod_incore_sb(ip->i_mount, - XFS_SBS_FDBLOCKS, -((int64_t)diff), rsvd)) + !xfs_icsb_modify_counters(ip->i_mount, + XFS_SBS_FDBLOCKS, + -((int64_t)diff), rsvd)) break; } if (temp2) { temp2--; diff--; if (!diff || - !xfs_mod_incore_sb(ip->i_mount, - XFS_SBS_FDBLOCKS, -((int64_t)diff), rsvd)) + !xfs_icsb_modify_counters(ip->i_mount, + XFS_SBS_FDBLOCKS, + -((int64_t)diff), rsvd)) break; } } @@ -1766,7 +1769,7 @@ xfs_bmap_add_extent_hole_delay( } if (oldlen != newlen) { ASSERT(oldlen > newlen); - xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS, + xfs_icsb_modify_counters(ip->i_mount, XFS_SBS_FDBLOCKS, (int64_t)(oldlen - newlen), rsvd); /* * Nothing to do for disk quota accounting here. @@ -3111,9 +3114,10 @@ xfs_bmap_del_extent( * Nothing to do for disk quota accounting here. */ ASSERT(da_old >= da_new); - if (da_old > da_new) - xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, (int64_t)(da_old - da_new), - rsvd); + if (da_old > da_new) { + xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, + (int64_t)(da_old - da_new), rsvd); + } done: *logflagsp = flags; return error; @@ -4526,13 +4530,13 @@ xfs_bmapi( -((int64_t)extsz), (flags & XFS_BMAPI_RSVBLOCKS)); } else { - error = xfs_mod_incore_sb(mp, + error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, -((int64_t)alen), (flags & XFS_BMAPI_RSVBLOCKS)); } if (!error) { - error = xfs_mod_incore_sb(mp, + error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, -((int64_t)indlen), (flags & XFS_BMAPI_RSVBLOCKS)); @@ -4542,7 +4546,7 @@ xfs_bmapi( (int64_t)extsz, (flags & XFS_BMAPI_RSVBLOCKS)); else if (error) - xfs_mod_incore_sb(mp, + xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, (int64_t)alen, (flags & XFS_BMAPI_RSVBLOCKS)); @@ -5206,7 +5210,7 @@ xfs_bunmapi( ip, -((long)del.br_blockcount), 0, XFS_QMOPT_RES_RTBLKS); } else { - xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, + xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, (int64_t)del.br_blockcount, rsvd); (void)xfs_trans_reserve_quota_nblks(NULL, ip, -((long)del.br_blockcount), 0, diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index 6a1edb1348f..a7c116e814a 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -596,7 +596,8 @@ out: * the extra reserve blocks from the reserve..... */ int error; - error = xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, fdblks_delta, 0); + error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, + fdblks_delta, 0); if (error == ENOSPC) goto retry; } diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 55de83585e0..20c67ff5753 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -52,16 +52,11 @@ STATIC void xfs_icsb_balance_counter(xfs_mount_t *, xfs_sb_field_t, int); STATIC void xfs_icsb_balance_counter_locked(xfs_mount_t *, xfs_sb_field_t, int); -STATIC int xfs_icsb_modify_counters(xfs_mount_t *, xfs_sb_field_t, - int64_t, int); STATIC void xfs_icsb_disable_counter(xfs_mount_t *, xfs_sb_field_t); - #else #define xfs_icsb_balance_counter(mp, a, b) do { } while (0) #define xfs_icsb_balance_counter_locked(mp, a, b) do { } while (0) -#define xfs_icsb_modify_counters(mp, a, b, c) do { } while (0) - #endif static const struct { @@ -1843,28 +1838,19 @@ xfs_mod_incore_sb_unlocked( */ int xfs_mod_incore_sb( - xfs_mount_t *mp, - xfs_sb_field_t field, - int64_t delta, - int rsvd) + struct xfs_mount *mp, + xfs_sb_field_t field, + int64_t delta, + int rsvd) { - int status; + int status; - /* check for per-cpu counters */ - switch (field) { #ifdef HAVE_PERCPU_SB - case XFS_SBS_ICOUNT: - case XFS_SBS_IFREE: - case XFS_SBS_FDBLOCKS: - status = xfs_icsb_modify_counters(mp, field, delta, rsvd); - break; + ASSERT(field < XFS_SBS_ICOUNT || field > XFS_SBS_FDBLOCKS); #endif - default: - spin_lock(&mp->m_sb_lock); - status = xfs_mod_incore_sb_unlocked(mp, field, delta, rsvd); - spin_unlock(&mp->m_sb_lock); - break; - } + spin_lock(&mp->m_sb_lock); + status = xfs_mod_incore_sb_unlocked(mp, field, delta, rsvd); + spin_unlock(&mp->m_sb_lock); return status; } diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index a9d366e7656..6509c074395 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -91,6 +91,8 @@ extern void xfs_icsb_reinit_counters(struct xfs_mount *); extern void xfs_icsb_destroy_counters(struct xfs_mount *); extern void xfs_icsb_sync_counters(struct xfs_mount *, int); extern void xfs_icsb_sync_counters_locked(struct xfs_mount *, int); +extern int xfs_icsb_modify_counters(struct xfs_mount *, xfs_sb_field_t, + int64_t, int); #else #define xfs_icsb_init_counters(mp) (0) @@ -98,6 +100,8 @@ extern void xfs_icsb_sync_counters_locked(struct xfs_mount *, int); #define xfs_icsb_reinit_counters(mp) do { } while (0) #define xfs_icsb_sync_counters(mp, flags) do { } while (0) #define xfs_icsb_sync_counters_locked(mp, flags) do { } while (0) +#define xfs_icsb_modify_counters(mp, field, delta, rsvd) \ + xfs_mod_incore_sb(mp, field, delta, rsvd) #endif typedef struct xfs_mount { diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 1c47edaea0d..73cf8f45725 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -696,7 +696,7 @@ xfs_trans_reserve( * fail if the count would go below zero. */ if (blocks > 0) { - error = xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FDBLOCKS, + error = xfs_icsb_modify_counters(tp->t_mountp, XFS_SBS_FDBLOCKS, -((int64_t)blocks), rsvd); if (error != 0) { current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); @@ -767,7 +767,7 @@ undo_log: undo_blocks: if (blocks > 0) { - (void) xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FDBLOCKS, + xfs_icsb_modify_counters(tp->t_mountp, XFS_SBS_FDBLOCKS, (int64_t)blocks, rsvd); tp->t_blk_res = 0; } -- cgit v1.2.3-18-g5258 From 1b0407125f9a5be63e861eb27c8af9e32f20619c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 30 Sep 2010 02:25:56 +0000 Subject: xfs: do not use xfs_mod_incore_sb_batch for per-cpu counters Update the per-cpu counters manually in xfs_trans_unreserve_and_mod_sb and remove support for per-cpu counters from xfs_mod_incore_sb_batch to simplify it. And added benefit is that we don't have to take m_sb_lock for transactions that only modify per-cpu counters. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_mount.c | 114 ++++++++++++++++------------------------------------- fs/xfs/xfs_trans.c | 78 +++++++++++++++++++++++------------- 2 files changed, 85 insertions(+), 107 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 20c67ff5753..00538b7e694 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1856,98 +1856,54 @@ xfs_mod_incore_sb( } /* - * xfs_mod_incore_sb_batch() is used to change more than one field - * in the in-core superblock structure at a time. This modification - * is protected by a lock internal to this module. The fields and - * changes to those fields are specified in the array of xfs_mod_sb - * structures passed in. + * Change more than one field in the in-core superblock structure at a time. * - * Either all of the specified deltas will be applied or none of - * them will. If any modified field dips below 0, then all modifications - * will be backed out and EINVAL will be returned. + * The fields and changes to those fields are specified in the array of + * xfs_mod_sb structures passed in. Either all of the specified deltas + * will be applied or none of them will. If any modified field dips below 0, + * then all modifications will be backed out and EINVAL will be returned. + * + * Note that this function may not be used for the superblock values that + * are tracked with the in-memory per-cpu counters - a direct call to + * xfs_icsb_modify_counters is required for these. */ int -xfs_mod_incore_sb_batch(xfs_mount_t *mp, xfs_mod_sb_t *msb, uint nmsb, int rsvd) +xfs_mod_incore_sb_batch( + struct xfs_mount *mp, + xfs_mod_sb_t *msb, + uint nmsb, + int rsvd) { - int status=0; - xfs_mod_sb_t *msbp; + xfs_mod_sb_t *msbp = &msb[0]; + int error = 0; /* - * Loop through the array of mod structures and apply each - * individually. If any fail, then back out all those - * which have already been applied. Do all of this within - * the scope of the m_sb_lock so that all of the changes will - * be atomic. + * Loop through the array of mod structures and apply each individually. + * If any fail, then back out all those which have already been applied. + * Do all of this within the scope of the m_sb_lock so that all of the + * changes will be atomic. */ spin_lock(&mp->m_sb_lock); - msbp = &msb[0]; for (msbp = &msbp[0]; msbp < (msb + nmsb); msbp++) { - /* - * Apply the delta at index n. If it fails, break - * from the loop so we'll fall into the undo loop - * below. - */ - switch (msbp->msb_field) { -#ifdef HAVE_PERCPU_SB - case XFS_SBS_ICOUNT: - case XFS_SBS_IFREE: - case XFS_SBS_FDBLOCKS: - spin_unlock(&mp->m_sb_lock); - status = xfs_icsb_modify_counters(mp, - msbp->msb_field, - msbp->msb_delta, rsvd); - spin_lock(&mp->m_sb_lock); - break; -#endif - default: - status = xfs_mod_incore_sb_unlocked(mp, - msbp->msb_field, - msbp->msb_delta, rsvd); - break; - } + ASSERT(msbp->msb_field < XFS_SBS_ICOUNT || + msbp->msb_field > XFS_SBS_FDBLOCKS); - if (status != 0) { - break; - } + error = xfs_mod_incore_sb_unlocked(mp, msbp->msb_field, + msbp->msb_delta, rsvd); + if (error) + goto unwind; } + spin_unlock(&mp->m_sb_lock); + return 0; - /* - * If we didn't complete the loop above, then back out - * any changes made to the superblock. If you add code - * between the loop above and here, make sure that you - * preserve the value of status. Loop back until - * we step below the beginning of the array. Make sure - * we don't touch anything back there. - */ - if (status != 0) { - msbp--; - while (msbp >= msb) { - switch (msbp->msb_field) { -#ifdef HAVE_PERCPU_SB - case XFS_SBS_ICOUNT: - case XFS_SBS_IFREE: - case XFS_SBS_FDBLOCKS: - spin_unlock(&mp->m_sb_lock); - status = xfs_icsb_modify_counters(mp, - msbp->msb_field, - -(msbp->msb_delta), - rsvd); - spin_lock(&mp->m_sb_lock); - break; -#endif - default: - status = xfs_mod_incore_sb_unlocked(mp, - msbp->msb_field, - -(msbp->msb_delta), - rsvd); - break; - } - ASSERT(status == 0); - msbp--; - } +unwind: + while (--msbp >= msb) { + error = xfs_mod_incore_sb_unlocked(mp, msbp->msb_field, + -msbp->msb_delta, rsvd); + ASSERT(error == 0); } spin_unlock(&mp->m_sb_lock); - return status; + return error; } /* @@ -2478,7 +2434,7 @@ xfs_icsb_balance_counter( spin_unlock(&mp->m_sb_lock); } -STATIC int +int xfs_icsb_modify_counters( xfs_mount_t *mp, xfs_sb_field_t field, diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 73cf8f45725..5fab0e6bf86 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1009,7 +1009,7 @@ void xfs_trans_unreserve_and_mod_sb( xfs_trans_t *tp) { - xfs_mod_sb_t msb[14]; /* If you add cases, add entries */ + xfs_mod_sb_t msb[9]; /* If you add cases, add entries */ xfs_mod_sb_t *msbp; xfs_mount_t *mp = tp->t_mountp; /* REFERENCED */ @@ -1017,55 +1017,61 @@ xfs_trans_unreserve_and_mod_sb( int rsvd; int64_t blkdelta = 0; int64_t rtxdelta = 0; + int64_t idelta = 0; + int64_t ifreedelta = 0; msbp = msb; rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0; - /* calculate free blocks delta */ + /* calculate deltas */ if (tp->t_blk_res > 0) blkdelta = tp->t_blk_res; - if ((tp->t_fdblocks_delta != 0) && (xfs_sb_version_haslazysbcount(&mp->m_sb) || (tp->t_flags & XFS_TRANS_SB_DIRTY))) blkdelta += tp->t_fdblocks_delta; - if (blkdelta != 0) { - msbp->msb_field = XFS_SBS_FDBLOCKS; - msbp->msb_delta = blkdelta; - msbp++; - } - - /* calculate free realtime extents delta */ if (tp->t_rtx_res > 0) rtxdelta = tp->t_rtx_res; - if ((tp->t_frextents_delta != 0) && (tp->t_flags & XFS_TRANS_SB_DIRTY)) rtxdelta += tp->t_frextents_delta; + if (xfs_sb_version_haslazysbcount(&mp->m_sb) || + (tp->t_flags & XFS_TRANS_SB_DIRTY)) { + idelta = tp->t_icount_delta; + ifreedelta = tp->t_ifree_delta; + } + + /* apply the per-cpu counters */ + if (blkdelta) { + error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, + blkdelta, rsvd); + if (error) + goto out; + } + + if (idelta) { + error = xfs_icsb_modify_counters(mp, XFS_SBS_ICOUNT, + idelta, rsvd); + if (error) + goto out_undo_fdblocks; + } + + if (ifreedelta) { + error = xfs_icsb_modify_counters(mp, XFS_SBS_IFREE, + ifreedelta, rsvd); + if (error) + goto out_undo_icount; + } + + /* apply remaining deltas */ if (rtxdelta != 0) { msbp->msb_field = XFS_SBS_FREXTENTS; msbp->msb_delta = rtxdelta; msbp++; } - /* apply remaining deltas */ - - if (xfs_sb_version_haslazysbcount(&mp->m_sb) || - (tp->t_flags & XFS_TRANS_SB_DIRTY)) { - if (tp->t_icount_delta != 0) { - msbp->msb_field = XFS_SBS_ICOUNT; - msbp->msb_delta = tp->t_icount_delta; - msbp++; - } - if (tp->t_ifree_delta != 0) { - msbp->msb_field = XFS_SBS_IFREE; - msbp->msb_delta = tp->t_ifree_delta; - msbp++; - } - } - if (tp->t_flags & XFS_TRANS_SB_DIRTY) { if (tp->t_dblocks_delta != 0) { msbp->msb_field = XFS_SBS_DBLOCKS; @@ -1115,8 +1121,24 @@ xfs_trans_unreserve_and_mod_sb( if (msbp > msb) { error = xfs_mod_incore_sb_batch(tp->t_mountp, msb, (uint)(msbp - msb), rsvd); - ASSERT(error == 0); + if (error) + goto out_undo_ifreecount; } + + return; + +out_undo_ifreecount: + if (ifreedelta) + xfs_icsb_modify_counters(mp, XFS_SBS_IFREE, -ifreedelta, rsvd); +out_undo_icount: + if (idelta) + xfs_icsb_modify_counters(mp, XFS_SBS_ICOUNT, -idelta, rsvd); +out_undo_fdblocks: + if (blkdelta) + xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, -blkdelta, rsvd); +out: + ASSERT(error = 0); + return; } /* -- cgit v1.2.3-18-g5258 From d276734d937a649ff43fd197d0df7a747bd55b7e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 6 Oct 2010 18:31:23 +0000 Subject: xfs: fix bogus m_maxagi check in xfs_iget These days inode64 should only control which AGs we allocate new inodes from, while we still try to support reading all existing inodes. To make this actually work the check ontop of xfs_iget needs to be relaxed to allow inodes in all allocation groups instead of just those that we allow allocating inodes from. Note that we can't simply remove the check - it prevents us from accessing invalid data when fed invalid inode numbers from NFS or bulkstat. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_iget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index b1ecc6f97ad..0cdd26932d8 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -365,8 +365,8 @@ xfs_iget( xfs_perag_t *pag; xfs_agino_t agino; - /* the radix tree exists only in inode capable AGs */ - if (XFS_INO_TO_AGNO(mp, ino) >= mp->m_maxagi) + /* reject inode numbers outside existing AGs */ + if (XFS_INO_TO_AGNO(mp, ino) >= mp->m_sb.sb_agcount) return EINVAL; /* get the perag structure and ensure that it's inode capable */ -- cgit v1.2.3-18-g5258 From dfe188d4283752086d48380cde40d9801c318667 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 6 Oct 2010 18:41:12 +0000 Subject: xfs: remove unused t_callback field in struct xfs_trans Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_trans.c | 4 ---- fs/xfs/xfs_trans.h | 2 -- 2 files changed, 6 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 5fab0e6bf86..50bd4acefc0 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1416,10 +1416,6 @@ xfs_trans_committed( { struct xfs_log_item_desc *lidp, *next; - /* Call the transaction's completion callback if there is one. */ - if (tp->t_callback != NULL) - tp->t_callback(tp, tp->t_callarg); - list_for_each_entry_safe(lidp, next, &tp->t_items, lid_trans) { xfs_trans_item_committed(lidp->lid_item, tp->t_lsn, abortflag); xfs_trans_free_item_desc(lidp); diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index e2cbe4da7d8..246286b77a8 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -399,8 +399,6 @@ typedef struct xfs_trans { * transaction. */ struct xfs_mount *t_mountp; /* ptr to fs mount struct */ struct xfs_dquot_acct *t_dqinfo; /* acctg info for dquots */ - xfs_trans_callback_t t_callback; /* transaction callback */ - void *t_callarg; /* callback arg */ unsigned int t_flags; /* misc flags */ int64_t t_icount_delta; /* superblock icount change */ int64_t t_ifree_delta; /* superblock ifree change */ -- cgit v1.2.3-18-g5258 From 4957a449a1bce2f5095f57f84114dc038a8f08d5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 6 Oct 2010 18:41:13 +0000 Subject: xfs: fix the xfs_trans_committed Use the correct prototype for xfs_trans_committed instead of casting it. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_trans.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 50bd4acefc0..f6d956b7711 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1411,9 +1411,10 @@ xfs_trans_item_committed( */ STATIC void xfs_trans_committed( - struct xfs_trans *tp, + void *arg, int abortflag) { + struct xfs_trans *tp = arg; struct xfs_log_item_desc *lidp, *next; list_for_each_entry_safe(lidp, next, &tp->t_items, lid_trans) { @@ -1543,7 +1544,7 @@ xfs_trans_commit_iclog( * running in simulation mode (the log is explicitly turned * off). */ - tp->t_logcb.cb_func = (void(*)(void*, int))xfs_trans_committed; + tp->t_logcb.cb_func = xfs_trans_committed; tp->t_logcb.cb_arg = tp; /* -- cgit v1.2.3-18-g5258 From 1ae4fe6dba24ebabcd12cd0fa45cc5955394cbd8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 6 Oct 2010 18:41:14 +0000 Subject: xfs: remove xfs_refcache.h This header has been completely unused for a couple of years. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_refcache.h | 52 --------------------------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 fs/xfs/xfs_refcache.h (limited to 'fs') diff --git a/fs/xfs/xfs_refcache.h b/fs/xfs/xfs_refcache.h deleted file mode 100644 index 2dec79edb51..00000000000 --- a/fs/xfs/xfs_refcache.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * 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 the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_REFCACHE_H__ -#define __XFS_REFCACHE_H__ - -#ifdef HAVE_REFCACHE -/* - * Maximum size (in inodes) for the NFS reference cache - */ -#define XFS_REFCACHE_SIZE_MAX 512 - -struct xfs_inode; -struct xfs_mount; - -extern void xfs_refcache_insert(struct xfs_inode *); -extern void xfs_refcache_purge_ip(struct xfs_inode *); -extern void xfs_refcache_purge_mp(struct xfs_mount *); -extern void xfs_refcache_purge_some(struct xfs_mount *); -extern void xfs_refcache_resize(int); -extern void xfs_refcache_destroy(void); - -extern void xfs_refcache_iunlock(struct xfs_inode *, uint); - -#else - -#define xfs_refcache_insert(ip) do { } while (0) -#define xfs_refcache_purge_ip(ip) do { } while (0) -#define xfs_refcache_purge_mp(mp) do { } while (0) -#define xfs_refcache_purge_some(mp) do { } while (0) -#define xfs_refcache_resize(size) do { } while (0) -#define xfs_refcache_destroy() do { } while (0) - -#define xfs_refcache_iunlock(ip, flags) xfs_iunlock(ip, flags) - -#endif - -#endif /* __XFS_REFCACHE_H__ */ -- cgit v1.2.3-18-g5258 From 668332e5fec809bb100da619fda80e033b12b4a7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 6 Oct 2010 18:41:15 +0000 Subject: xfs: remove xfs_version.h It used to have a place when it contained an automatically generated CVS version, but these days it's entirely superflous. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_super.c | 1 - fs/xfs/linux-2.6/xfs_super.h | 1 + fs/xfs/linux-2.6/xfs_version.h | 29 ----------------------------- 3 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 fs/xfs/linux-2.6/xfs_version.h (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index a316c731670..48d5f820654 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -44,7 +44,6 @@ #include "xfs_buf_item.h" #include "xfs_utils.h" #include "xfs_vnodeops.h" -#include "xfs_version.h" #include "xfs_log_priv.h" #include "xfs_trans_priv.h" #include "xfs_filestream.h" diff --git a/fs/xfs/linux-2.6/xfs_super.h b/fs/xfs/linux-2.6/xfs_super.h index 1ef4a4d2d99..50a3266c999 100644 --- a/fs/xfs/linux-2.6/xfs_super.h +++ b/fs/xfs/linux-2.6/xfs_super.h @@ -62,6 +62,7 @@ extern void xfs_qm_exit(void); # define XFS_DBG_STRING "no debug" #endif +#define XFS_VERSION_STRING "SGI XFS" #define XFS_BUILD_OPTIONS XFS_ACL_STRING \ XFS_SECURITY_STRING \ XFS_REALTIME_STRING \ diff --git a/fs/xfs/linux-2.6/xfs_version.h b/fs/xfs/linux-2.6/xfs_version.h deleted file mode 100644 index f8d279d7563..00000000000 --- a/fs/xfs/linux-2.6/xfs_version.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2001-2002,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * 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 the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_VERSION_H__ -#define __XFS_VERSION_H__ - -/* - * Dummy file that can contain a timestamp to put into the - * XFS init string, to help users keep track of what they're - * running - */ - -#define XFS_VERSION_STRING "SGI XFS" - -#endif /* __XFS_VERSION_H__ */ -- cgit v1.2.3-18-g5258 From 78a4b0961ff241c6f23b16329db0d67e97cb86a7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 6 Oct 2010 18:41:16 +0000 Subject: xfs: remove xfs_globals.h This header only provides one extern that isn't actually declared anywhere, and shadowed by a macro. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_globals.h | 23 ----------------------- fs/xfs/linux-2.6/xfs_linux.h | 1 - 2 files changed, 24 deletions(-) delete mode 100644 fs/xfs/linux-2.6/xfs_globals.h (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_globals.h b/fs/xfs/linux-2.6/xfs_globals.h deleted file mode 100644 index 69f71caf061..00000000000 --- a/fs/xfs/linux-2.6/xfs_globals.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * 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 the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_GLOBALS_H__ -#define __XFS_GLOBALS_H__ - -extern uint64_t xfs_panic_mask; /* set to cause more panics */ - -#endif /* __XFS_GLOBALS_H__ */ diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h index 2fa0bd9ebc7..1a2afcd87ef 100644 --- a/fs/xfs/linux-2.6/xfs_linux.h +++ b/fs/xfs/linux-2.6/xfs_linux.h @@ -86,7 +86,6 @@ #include #include #include -#include #include /* -- cgit v1.2.3-18-g5258 From 6c77b0ea1bdf85dfd48c20ceb10fd215a95c66e2 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 6 Oct 2010 18:41:17 +0000 Subject: xfs: remove xfs_cred.h We're not actually passing around credentials inside XFS for a while now, so remove all xfs_cred.h with it's cred_t typedef and all instances of it. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_cred.h | 28 ---------------------------- fs/xfs/linux-2.6/xfs_globals.c | 1 - fs/xfs/linux-2.6/xfs_iops.c | 4 ++-- fs/xfs/linux-2.6/xfs_linux.h | 2 +- fs/xfs/quota/xfs_qm.c | 8 +++----- fs/xfs/xfs_inode.c | 1 - fs/xfs/xfs_inode.h | 5 ++--- fs/xfs/xfs_mount.h | 1 - fs/xfs/xfs_utils.c | 5 ++--- fs/xfs/xfs_utils.h | 3 +-- fs/xfs/xfs_vnodeops.c | 12 +++++------- fs/xfs/xfs_vnodeops.h | 6 ++---- 12 files changed, 18 insertions(+), 58 deletions(-) delete mode 100644 fs/xfs/linux-2.6/xfs_cred.h (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_cred.h b/fs/xfs/linux-2.6/xfs_cred.h deleted file mode 100644 index 55bddf3b609..00000000000 --- a/fs/xfs/linux-2.6/xfs_cred.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * 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 the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_CRED_H__ -#define __XFS_CRED_H__ - -#include - -/* - * Credentials - */ -typedef const struct cred cred_t; - -#endif /* __XFS_CRED_H__ */ diff --git a/fs/xfs/linux-2.6/xfs_globals.c b/fs/xfs/linux-2.6/xfs_globals.c index 2ae8b1ccb02..76e81cff70b 100644 --- a/fs/xfs/linux-2.6/xfs_globals.c +++ b/fs/xfs/linux-2.6/xfs_globals.c @@ -16,7 +16,6 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xfs.h" -#include "xfs_cred.h" #include "xfs_sysctl.h" /* diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index a788f016d1f..ec858e09d54 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -189,7 +189,7 @@ xfs_vn_mknod( } xfs_dentry_to_name(&name, dentry); - error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip, NULL); + error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip); if (unlikely(error)) goto out_free_acl; @@ -362,7 +362,7 @@ xfs_vn_symlink( (irix_symlink_mode ? 0777 & ~current_umask() : S_IRWXUGO); xfs_dentry_to_name(&name, dentry); - error = xfs_symlink(XFS_I(dir), &name, symname, mode, &cip, NULL); + error = xfs_symlink(XFS_I(dir), &name, symname, mode, &cip); if (unlikely(error)) goto out; diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h index 1a2afcd87ef..76ebc582dba 100644 --- a/fs/xfs/linux-2.6/xfs_linux.h +++ b/fs/xfs/linux-2.6/xfs_linux.h @@ -71,6 +71,7 @@ #include #include #include +#include #include #include @@ -79,7 +80,6 @@ #include #include -#include #include #include #include diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 121f632213a..a3f8f95d33e 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -55,8 +55,6 @@ uint ndquot; kmem_zone_t *qm_dqzone; kmem_zone_t *qm_dqtrxzone; -static cred_t xfs_zerocr; - STATIC void xfs_qm_list_init(xfs_dqlist_t *, char *, int); STATIC void xfs_qm_list_destroy(xfs_dqlist_t *); @@ -1224,8 +1222,8 @@ xfs_qm_qino_alloc( return error; } - if ((error = xfs_dir_ialloc(&tp, NULL, S_IFREG, 1, 0, - &xfs_zerocr, 0, 1, ip, &committed))) { + error = xfs_dir_ialloc(&tp, NULL, S_IFREG, 1, 0, 0, 1, ip, &committed); + if (error) { xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); return error; @@ -2143,7 +2141,7 @@ xfs_qm_write_sb_changes( /* - * Given an inode, a uid and gid (from cred_t) make sure that we have + * Given an inode, a uid, gid and prid make sure that we have * allocated relevant dquot(s) on disk, and that we won't exceed inode * quotas by creating this file. * This also attaches dquot(s) to the given inode after locking it, diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 34798f391c4..4fc72c9e372 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -982,7 +982,6 @@ xfs_ialloc( mode_t mode, xfs_nlink_t nlink, xfs_dev_t rdev, - cred_t *cr, xfs_prid_t prid, int okalloc, xfs_buf_t **ialloc_context, diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 54781fcd322..ac82327ec9d 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -212,7 +212,6 @@ typedef struct xfs_icdinode { #ifdef __KERNEL__ struct bhv_desc; -struct cred; struct xfs_buf; struct xfs_bmap_free; struct xfs_bmbt_irec; @@ -456,8 +455,8 @@ void xfs_inode_free(struct xfs_inode *ip); * xfs_inode.c prototypes. */ int xfs_ialloc(struct xfs_trans *, xfs_inode_t *, mode_t, - xfs_nlink_t, xfs_dev_t, cred_t *, xfs_prid_t, - int, struct xfs_buf **, boolean_t *, xfs_inode_t **); + xfs_nlink_t, xfs_dev_t, xfs_prid_t, int, + struct xfs_buf **, boolean_t *, xfs_inode_t **); uint xfs_ip2xflags(struct xfs_inode *); uint xfs_dic2xflags(struct xfs_dinode *); diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 6509c074395..5861b498074 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -53,7 +53,6 @@ typedef struct xfs_trans_reservations { #include "xfs_sync.h" -struct cred; struct log; struct xfs_mount_args; struct xfs_inode; diff --git a/fs/xfs/xfs_utils.c b/fs/xfs/xfs_utils.c index 4c2ba6f6adc..8b32d1a4c5a 100644 --- a/fs/xfs/xfs_utils.c +++ b/fs/xfs/xfs_utils.c @@ -56,7 +56,6 @@ xfs_dir_ialloc( mode_t mode, xfs_nlink_t nlink, xfs_dev_t rdev, - cred_t *credp, prid_t prid, /* project id */ int okalloc, /* ok to allocate new space */ xfs_inode_t **ipp, /* pointer to inode; it will be @@ -93,7 +92,7 @@ xfs_dir_ialloc( * transaction commit so that no other process can steal * the inode(s) that we've just allocated. */ - code = xfs_ialloc(tp, dp, mode, nlink, rdev, credp, prid, okalloc, + code = xfs_ialloc(tp, dp, mode, nlink, rdev, prid, okalloc, &ialloc_context, &call_again, &ip); /* @@ -197,7 +196,7 @@ xfs_dir_ialloc( * other allocations in this allocation group, * this call should always succeed. */ - code = xfs_ialloc(tp, dp, mode, nlink, rdev, credp, prid, + code = xfs_ialloc(tp, dp, mode, nlink, rdev, prid, okalloc, &ialloc_context, &call_again, &ip); /* diff --git a/fs/xfs/xfs_utils.h b/fs/xfs/xfs_utils.h index f55b9678264..456fca31493 100644 --- a/fs/xfs/xfs_utils.h +++ b/fs/xfs/xfs_utils.h @@ -19,8 +19,7 @@ #define __XFS_UTILS_H__ extern int xfs_dir_ialloc(xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t, - xfs_dev_t, cred_t *, prid_t, int, - xfs_inode_t **, int *); + xfs_dev_t, prid_t, int, xfs_inode_t **, int *); extern int xfs_droplink(xfs_trans_t *, xfs_inode_t *); extern int xfs_bumplink(xfs_trans_t *, xfs_inode_t *); extern void xfs_bump_ino_vers2(xfs_trans_t *, xfs_inode_t *); diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index b7bdc43308e..f82c8032db5 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -1256,8 +1256,7 @@ xfs_create( struct xfs_name *name, mode_t mode, xfs_dev_t rdev, - xfs_inode_t **ipp, - cred_t *credp) + xfs_inode_t **ipp) { int is_dir = S_ISDIR(mode); struct xfs_mount *mp = dp->i_mount; @@ -1363,7 +1362,7 @@ xfs_create( * entry pointing to them, but a directory also the "." entry * pointing to itself. */ - error = xfs_dir_ialloc(&tp, dp, mode, is_dir ? 2 : 1, rdev, credp, + error = xfs_dir_ialloc(&tp, dp, mode, is_dir ? 2 : 1, rdev, prid, resblks > 0, &ip, &committed); if (error) { if (error == ENOSPC) @@ -1936,8 +1935,7 @@ xfs_symlink( struct xfs_name *link_name, const char *target_path, mode_t mode, - xfs_inode_t **ipp, - cred_t *credp) + xfs_inode_t **ipp) { xfs_mount_t *mp = dp->i_mount; xfs_trans_t *tp; @@ -2049,8 +2047,8 @@ xfs_symlink( /* * Allocate an inode for the symlink. */ - error = xfs_dir_ialloc(&tp, dp, S_IFLNK | (mode & ~S_IFMT), - 1, 0, credp, prid, resblks > 0, &ip, NULL); + error = xfs_dir_ialloc(&tp, dp, S_IFLNK | (mode & ~S_IFMT), 1, 0, + prid, resblks > 0, &ip, NULL); if (error) { if (error == ENOSPC) goto error_return; diff --git a/fs/xfs/xfs_vnodeops.h b/fs/xfs/xfs_vnodeops.h index d8dfa8d0dad..f6702927eee 100644 --- a/fs/xfs/xfs_vnodeops.h +++ b/fs/xfs/xfs_vnodeops.h @@ -2,7 +2,6 @@ #define _XFS_VNODEOPS_H 1 struct attrlist_cursor_kern; -struct cred; struct file; struct iattr; struct inode; @@ -26,7 +25,7 @@ int xfs_inactive(struct xfs_inode *ip); int xfs_lookup(struct xfs_inode *dp, struct xfs_name *name, struct xfs_inode **ipp, struct xfs_name *ci_name); int xfs_create(struct xfs_inode *dp, struct xfs_name *name, mode_t mode, - xfs_dev_t rdev, struct xfs_inode **ipp, cred_t *credp); + xfs_dev_t rdev, struct xfs_inode **ipp); int xfs_remove(struct xfs_inode *dp, struct xfs_name *name, struct xfs_inode *ip); int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip, @@ -34,8 +33,7 @@ int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip, int xfs_readdir(struct xfs_inode *dp, void *dirent, size_t bufsize, xfs_off_t *offset, filldir_t filldir); int xfs_symlink(struct xfs_inode *dp, struct xfs_name *link_name, - const char *target_path, mode_t mode, struct xfs_inode **ipp, - cred_t *credp); + const char *target_path, mode_t mode, struct xfs_inode **ipp); int xfs_set_dmattrs(struct xfs_inode *ip, u_int evmask, u_int16_t state); int xfs_change_file_space(struct xfs_inode *ip, int cmd, xfs_flock64_t *bf, xfs_off_t offset, int attr_flags); -- cgit v1.2.3-18-g5258 From 1a1a3e97bad42e92cd2f32e81c396c8ee0bddb28 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 6 Oct 2010 18:41:18 +0000 Subject: xfs: remove xfs_buf wrappers Stop having two different names for many buffer functions and use the more descriptive xfs_buf_* names directly. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_buf.c | 19 +++++++++---------- fs/xfs/linux-2.6/xfs_buf.h | 20 +++----------------- fs/xfs/linux-2.6/xfs_super.c | 2 +- fs/xfs/quota/xfs_qm.c | 2 +- fs/xfs/xfs_attr.c | 6 +++--- fs/xfs/xfs_btree.c | 4 ++-- fs/xfs/xfs_buf_item.c | 4 ++-- fs/xfs/xfs_da_btree.c | 2 +- fs/xfs/xfs_dir2_leaf.c | 2 +- fs/xfs/xfs_ialloc.c | 2 +- fs/xfs/xfs_inode.c | 2 +- fs/xfs/xfs_log.c | 2 +- fs/xfs/xfs_log_recover.c | 6 +++--- fs/xfs/xfs_mount.c | 2 +- fs/xfs/xfs_trans_buf.c | 2 +- fs/xfs/xfs_vnodeops.c | 4 ++-- 16 files changed, 33 insertions(+), 48 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 749d7d39d65..47ef97f46fe 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -652,8 +652,7 @@ void xfs_buf_readahead( xfs_buftarg_t *target, xfs_off_t ioff, - size_t isize, - xfs_buf_flags_t flags) + size_t isize) { struct backing_dev_info *bdi; @@ -661,8 +660,8 @@ xfs_buf_readahead( if (bdi_read_congested(bdi)) return; - flags |= (XBF_TRYLOCK|XBF_ASYNC|XBF_READ_AHEAD); - xfs_buf_read(target, ioff, isize, flags); + xfs_buf_read(target, ioff, isize, + XBF_TRYLOCK|XBF_ASYNC|XBF_READ_AHEAD|XBF_DONT_BLOCK); } /* @@ -691,7 +690,7 @@ xfs_buf_read_uncached( XFS_BUF_BUSY(bp); xfsbdstrat(mp, bp); - error = xfs_iowait(bp); + error = xfs_buf_iowait(bp); if (error || bp->b_error) { xfs_buf_relse(bp); return NULL; @@ -1073,7 +1072,7 @@ xfs_bdwrite( /* * Called when we want to stop a buffer from getting written or read. - * We attach the EIO error, muck with its flags, and call biodone + * We attach the EIO error, muck with its flags, and call xfs_buf_ioend * so that the proper iodone callbacks get called. */ STATIC int @@ -1090,21 +1089,21 @@ xfs_bioerror( XFS_BUF_ERROR(bp, EIO); /* - * We're calling biodone, so delete XBF_DONE flag. + * We're calling xfs_buf_ioend, so delete XBF_DONE flag. */ XFS_BUF_UNREAD(bp); XFS_BUF_UNDELAYWRITE(bp); XFS_BUF_UNDONE(bp); XFS_BUF_STALE(bp); - xfs_biodone(bp); + xfs_buf_ioend(bp, 0); return EIO; } /* * Same as xfs_bioerror, except that we are releasing the buffer - * here ourselves, and avoiding the biodone call. + * here ourselves, and avoiding the xfs_buf_ioend call. * This is meant for userdata errors; metadata bufs come with * iodone functions attached, so that we can track down errors. */ @@ -1938,7 +1937,7 @@ xfs_flush_buftarg( bp = list_first_entry(&wait_list, struct xfs_buf, b_list); list_del_init(&bp->b_list); - xfs_iowait(bp); + xfs_buf_iowait(bp); xfs_buf_relse(bp); } } diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index 161333785f6..131c0ebf2c0 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -218,8 +218,7 @@ extern xfs_buf_t *xfs_buf_get_empty(size_t, xfs_buftarg_t *); extern xfs_buf_t *xfs_buf_get_uncached(struct xfs_buftarg *, size_t, int); extern int xfs_buf_associate_memory(xfs_buf_t *, void *, size_t); extern void xfs_buf_hold(xfs_buf_t *); -extern void xfs_buf_readahead(xfs_buftarg_t *, xfs_off_t, size_t, - xfs_buf_flags_t); +extern void xfs_buf_readahead(xfs_buftarg_t *, xfs_off_t, size_t); struct xfs_buf *xfs_buf_read_uncached(struct xfs_mount *mp, struct xfs_buftarg *target, xfs_daddr_t daddr, size_t length, int flags); @@ -247,6 +246,8 @@ extern int xfs_buf_iorequest(xfs_buf_t *); extern int xfs_buf_iowait(xfs_buf_t *); extern void xfs_buf_iomove(xfs_buf_t *, size_t, size_t, void *, xfs_buf_rw_t); +#define xfs_buf_zero(bp, off, len) \ + xfs_buf_iomove((bp), (off), (len), NULL, XBRW_ZERO) static inline int xfs_buf_geterror(xfs_buf_t *bp) { @@ -359,21 +360,6 @@ static inline void xfs_buf_relse(xfs_buf_t *bp) xfs_buf_rele(bp); } -#define xfs_biodone(bp) xfs_buf_ioend(bp, 0) - -#define xfs_biomove(bp, off, len, data, rw) \ - xfs_buf_iomove((bp), (off), (len), (data), \ - ((rw) == XBF_WRITE) ? XBRW_WRITE : XBRW_READ) - -#define xfs_biozero(bp, off, len) \ - xfs_buf_iomove((bp), (off), (len), NULL, XBRW_ZERO) - -#define xfs_iowait(bp) xfs_buf_iowait(bp) - -#define xfs_baread(target, rablkno, ralen) \ - xfs_buf_readahead((target), (rablkno), (ralen), XBF_DONT_BLOCK) - - /* * Handling of buftargs. */ diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 48d5f820654..fa1e40ac4b3 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -644,7 +644,7 @@ xfs_barrier_test( XFS_BUF_ORDERED(sbp); xfsbdstrat(mp, sbp); - error = xfs_iowait(sbp); + error = xfs_buf_iowait(sbp); /* * Clear all the flags we set and possible error state in the diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index a3f8f95d33e..d109cc557be 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -1433,7 +1433,7 @@ xfs_qm_dqiterate( rablkcnt = map[i+1].br_blockcount; rablkno = map[i+1].br_startblock; while (rablkcnt--) { - xfs_baread(mp->m_ddev_targp, + xfs_buf_readahead(mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, rablkno), mp->m_quotainfo->qi_dqchunklen); rablkno++; diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c index 905d390c1e5..c8637537881 100644 --- a/fs/xfs/xfs_attr.c +++ b/fs/xfs/xfs_attr.c @@ -1986,7 +1986,7 @@ xfs_attr_rmtval_get(xfs_da_args_t *args) tmp = (valuelen < XFS_BUF_SIZE(bp)) ? valuelen : XFS_BUF_SIZE(bp); - xfs_biomove(bp, 0, tmp, dst, XBF_READ); + xfs_buf_iomove(bp, 0, tmp, dst, XBRW_READ); xfs_buf_relse(bp); dst += tmp; valuelen -= tmp; @@ -2116,9 +2116,9 @@ xfs_attr_rmtval_set(xfs_da_args_t *args) tmp = (valuelen < XFS_BUF_SIZE(bp)) ? valuelen : XFS_BUF_SIZE(bp); - xfs_biomove(bp, 0, tmp, src, XBF_WRITE); + xfs_buf_iomove(bp, 0, tmp, src, XBRW_WRITE); if (tmp < XFS_BUF_SIZE(bp)) - xfs_biozero(bp, tmp, XFS_BUF_SIZE(bp) - tmp); + xfs_buf_zero(bp, tmp, XFS_BUF_SIZE(bp) - tmp); if ((error = xfs_bwrite(mp, bp))) {/* GROT: NOTE: synchronous write */ return (error); } diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c index 735dc2e671b..04f9cca8da7 100644 --- a/fs/xfs/xfs_btree.c +++ b/fs/xfs/xfs_btree.c @@ -656,7 +656,7 @@ xfs_btree_reada_bufl( ASSERT(fsbno != NULLFSBLOCK); d = XFS_FSB_TO_DADDR(mp, fsbno); - xfs_baread(mp->m_ddev_targp, d, mp->m_bsize * count); + xfs_buf_readahead(mp->m_ddev_targp, d, mp->m_bsize * count); } /* @@ -676,7 +676,7 @@ xfs_btree_reada_bufs( ASSERT(agno != NULLAGNUMBER); ASSERT(agbno != NULLAGBLOCK); d = XFS_AGB_TO_DADDR(mp, agno, agbno); - xfs_baread(mp->m_ddev_targp, d, mp->m_bsize * count); + xfs_buf_readahead(mp->m_ddev_targp, d, mp->m_bsize * count); } STATIC int diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index ee7557611b6..2686d0d54c5 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -973,7 +973,7 @@ xfs_buf_iodone_callbacks( xfs_buf_do_callbacks(bp, lip); XFS_BUF_SET_FSPRIVATE(bp, NULL); XFS_BUF_CLR_IODONE_FUNC(bp); - xfs_biodone(bp); + xfs_buf_ioend(bp, 0); return; } @@ -1032,7 +1032,7 @@ xfs_buf_iodone_callbacks( xfs_buf_do_callbacks(bp, lip); XFS_BUF_SET_FSPRIVATE(bp, NULL); XFS_BUF_CLR_IODONE_FUNC(bp); - xfs_biodone(bp); + xfs_buf_ioend(bp, 0); } /* diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c index 30fa0e206fb..1c00bedb317 100644 --- a/fs/xfs/xfs_da_btree.c +++ b/fs/xfs/xfs_da_btree.c @@ -2042,7 +2042,7 @@ xfs_da_do_buf( mappedbno, nmapped, 0, &bp); break; case 3: - xfs_baread(mp->m_ddev_targp, mappedbno, nmapped); + xfs_buf_readahead(mp->m_ddev_targp, mappedbno, nmapped); error = 0; bp = NULL; break; diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c index 504be8640e9..ae891223be9 100644 --- a/fs/xfs/xfs_dir2_leaf.c +++ b/fs/xfs/xfs_dir2_leaf.c @@ -961,7 +961,7 @@ xfs_dir2_leaf_getdents( if (i > ra_current && map[ra_index].br_blockcount >= mp->m_dirblkfsbs) { - xfs_baread(mp->m_ddev_targp, + xfs_buf_readahead(mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, map[ra_index].br_startblock + ra_offset), diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index 5371d2dc360..0626a32c344 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -212,7 +212,7 @@ xfs_ialloc_inode_init( * to log a whole cluster of inodes instead of all the * individual transactions causing a lot of log traffic. */ - xfs_biozero(fbuf, 0, ninodes << mp->m_sb.sb_inodelog); + xfs_buf_zero(fbuf, 0, ninodes << mp->m_sb.sb_inodelog); for (i = 0; i < ninodes; i++) { int ioffset = i << mp->m_sb.sb_inodelog; uint isize = sizeof(struct xfs_dinode); diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 4fc72c9e372..493d6b0cbef 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2724,7 +2724,7 @@ cluster_corrupt_out: XFS_BUF_UNDONE(bp); XFS_BUF_STALE(bp); XFS_BUF_ERROR(bp,EIO); - xfs_biodone(bp); + xfs_buf_ioend(bp, 0); } else { XFS_BUF_STALE(bp); xfs_buf_relse(bp); diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index c8a30942430..f4fd49c9b98 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1310,7 +1310,7 @@ xlog_bdstrat( if (iclog->ic_state & XLOG_STATE_IOERROR) { XFS_BUF_ERROR(bp, EIO); XFS_BUF_STALE(bp); - xfs_biodone(bp); + xfs_buf_ioend(bp, 0); /* * It would seem logical to return EIO here, but we rely on * the log state machine to propagate I/O errors instead of diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 351d71117f1..966d3f97458 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -168,7 +168,7 @@ xlog_bread_noalign( XFS_BUF_SET_TARGET(bp, log->l_mp->m_logdev_targp); xfsbdstrat(log->l_mp, bp); - error = xfs_iowait(bp); + error = xfs_buf_iowait(bp); if (error) xfs_ioerror_alert("xlog_bread", log->l_mp, bp, XFS_BUF_ADDR(bp)); @@ -328,7 +328,7 @@ xlog_recover_iodone( SHUTDOWN_META_IO_ERROR); } XFS_BUF_CLR_IODONE_FUNC(bp); - xfs_biodone(bp); + xfs_buf_ioend(bp, 0); } /* @@ -3816,7 +3816,7 @@ xlog_do_recover( XFS_BUF_READ(bp); XFS_BUF_UNASYNC(bp); xfsbdstrat(log->l_mp, bp); - error = xfs_iowait(bp); + error = xfs_buf_iowait(bp); if (error) { xfs_ioerror_alert("xlog_do_recover", log->l_mp, bp, XFS_BUF_ADDR(bp)); diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 00538b7e694..b1498ab5a39 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1607,7 +1607,7 @@ xfs_unmountfs_writesb(xfs_mount_t *mp) XFS_BUF_UNASYNC(sbp); ASSERT(XFS_BUF_TARGET(sbp) == mp->m_ddev_targp); xfsbdstrat(mp, sbp); - error = xfs_iowait(sbp); + error = xfs_buf_iowait(sbp); if (error) xfs_ioerror_alert("xfs_unmountfs_writesb", mp, sbp, XFS_BUF_ADDR(sbp)); diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 90af025e683..c47918c302a 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -336,7 +336,7 @@ xfs_trans_read_buf( ASSERT(!XFS_BUF_ISASYNC(bp)); XFS_BUF_READ(bp); xfsbdstrat(tp->t_mountp, bp); - error = xfs_iowait(bp); + error = xfs_buf_iowait(bp); if (error) { xfs_ioerror_alert("xfs_trans_read_buf", mp, bp, blkno); diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index f82c8032db5..f4195f6b8bc 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -2460,7 +2460,7 @@ xfs_zero_remaining_bytes( XFS_BUF_READ(bp); XFS_BUF_SET_ADDR(bp, xfs_fsb_to_db(ip, imap.br_startblock)); xfsbdstrat(mp, bp); - error = xfs_iowait(bp); + error = xfs_buf_iowait(bp); if (error) { xfs_ioerror_alert("xfs_zero_remaining_bytes(read)", mp, bp, XFS_BUF_ADDR(bp)); @@ -2473,7 +2473,7 @@ xfs_zero_remaining_bytes( XFS_BUF_UNREAD(bp); XFS_BUF_WRITE(bp); xfsbdstrat(mp, bp); - error = xfs_iowait(bp); + error = xfs_buf_iowait(bp); if (error) { xfs_ioerror_alert("xfs_zero_remaining_bytes(write)", mp, bp, XFS_BUF_ADDR(bp)); -- cgit v1.2.3-18-g5258 From 6743099ce57a40509a86849a22317ed4b7516911 Mon Sep 17 00:00:00 2001 From: Arkadiusz Mi?kiewicz Date: Sun, 26 Sep 2010 06:10:18 +0000 Subject: xfs: Extend project quotas to support 32bit project ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for 32bit project quota identifiers. On disk format is backward compatible with 16bit projid numbers. projid on disk is now kept in two 16bit values - di_projid_lo (which holds the same position as old 16bit projid value) and new di_projid_hi (takes existing padding) and converts from/to 32bit value on the fly. xfs_admin (for existing fs), mkfs.xfs (for new fs) needs to be used to enable PROJID32BIT support. Signed-off-by: Arkadiusz MiÅ›kiewicz Reviewed-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_ioctl.c | 14 +++++++------- fs/xfs/linux-2.6/xfs_ioctl32.c | 4 +++- fs/xfs/linux-2.6/xfs_ioctl32.h | 6 ++++-- fs/xfs/linux-2.6/xfs_linux.h | 2 +- fs/xfs/quota/xfs_qm.c | 10 +++++----- fs/xfs/quota/xfs_qm_bhv.c | 2 +- fs/xfs/quota/xfs_qm_syscalls.c | 2 +- fs/xfs/xfs_dinode.h | 5 +++-- fs/xfs/xfs_fs.h | 6 ++++-- fs/xfs/xfs_inode.c | 14 ++++++++------ fs/xfs/xfs_inode.h | 26 +++++++++++++++++++++++--- fs/xfs/xfs_itable.c | 3 ++- fs/xfs/xfs_rename.c | 2 +- fs/xfs/xfs_sb.h | 10 +++++++++- fs/xfs/xfs_types.h | 2 -- fs/xfs/xfs_vnodeops.c | 16 ++++++++-------- 16 files changed, 80 insertions(+), 44 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index 10206be7a07..2ea238f6d38 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -790,7 +790,7 @@ xfs_ioc_fsgetxattr( xfs_ilock(ip, XFS_ILOCK_SHARED); fa.fsx_xflags = xfs_ip2xflags(ip); fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog; - fa.fsx_projid = ip->i_d.di_projid; + fa.fsx_projid = xfs_get_projid(ip); if (attr) { if (ip->i_afp) { @@ -909,10 +909,10 @@ xfs_ioctl_setattr( return XFS_ERROR(EIO); /* - * Disallow 32bit project ids because on-disk structure - * is 16bit only. + * Disallow 32bit project ids when projid32bit feature is not enabled. */ - if ((mask & FSX_PROJID) && (fa->fsx_projid > (__uint16_t)-1)) + if ((mask & FSX_PROJID) && (fa->fsx_projid > (__uint16_t)-1) && + !xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb)) return XFS_ERROR(EINVAL); /* @@ -961,7 +961,7 @@ xfs_ioctl_setattr( if (mask & FSX_PROJID) { if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && - ip->i_d.di_projid != fa->fsx_projid) { + xfs_get_projid(ip) != fa->fsx_projid) { ASSERT(tp); code = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp, capable(CAP_FOWNER) ? @@ -1063,12 +1063,12 @@ xfs_ioctl_setattr( * Change the ownerships and register quota modifications * in the transaction. */ - if (ip->i_d.di_projid != fa->fsx_projid) { + if (xfs_get_projid(ip) != fa->fsx_projid) { if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) { olddquot = xfs_qm_vop_chown(tp, ip, &ip->i_gdquot, gdqp); } - ip->i_d.di_projid = fa->fsx_projid; + xfs_set_projid(ip, fa->fsx_projid); /* * We may have to rev the inode as well as diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index 464bcc76e98..b3486dfa552 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -164,7 +164,8 @@ xfs_ioctl32_bstat_copyin( get_user(bstat->bs_extsize, &bstat32->bs_extsize) || get_user(bstat->bs_extents, &bstat32->bs_extents) || get_user(bstat->bs_gen, &bstat32->bs_gen) || - get_user(bstat->bs_projid, &bstat32->bs_projid) || + get_user(bstat->bs_projid_lo, &bstat32->bs_projid_lo) || + get_user(bstat->bs_projid_hi, &bstat32->bs_projid_hi) || get_user(bstat->bs_dmevmask, &bstat32->bs_dmevmask) || get_user(bstat->bs_dmstate, &bstat32->bs_dmstate) || get_user(bstat->bs_aextents, &bstat32->bs_aextents)) @@ -218,6 +219,7 @@ xfs_bulkstat_one_fmt_compat( put_user(buffer->bs_extents, &p32->bs_extents) || put_user(buffer->bs_gen, &p32->bs_gen) || put_user(buffer->bs_projid, &p32->bs_projid) || + put_user(buffer->bs_projid_hi, &p32->bs_projid_hi) || put_user(buffer->bs_dmevmask, &p32->bs_dmevmask) || put_user(buffer->bs_dmstate, &p32->bs_dmstate) || put_user(buffer->bs_aextents, &p32->bs_aextents)) diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.h b/fs/xfs/linux-2.6/xfs_ioctl32.h index 1024c4f8ba0..08b605792a9 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.h +++ b/fs/xfs/linux-2.6/xfs_ioctl32.h @@ -65,8 +65,10 @@ typedef struct compat_xfs_bstat { __s32 bs_extsize; /* extent size */ __s32 bs_extents; /* number of extents */ __u32 bs_gen; /* generation count */ - __u16 bs_projid; /* project id */ - unsigned char bs_pad[14]; /* pad space, unused */ + __u16 bs_projid_lo; /* lower part of project id */ +#define bs_projid bs_projid_lo /* (previously just bs_projid) */ + __u16 bs_projid_hi; /* high part of project id */ + unsigned char bs_pad[12]; /* pad space, unused */ __u32 bs_dmevmask; /* DMIG event mask */ __u16 bs_dmstate; /* DMIG state info */ __u16 bs_aextents; /* attribute number of extents */ diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h index 76ebc582dba..214ddd71ff7 100644 --- a/fs/xfs/linux-2.6/xfs_linux.h +++ b/fs/xfs/linux-2.6/xfs_linux.h @@ -143,7 +143,7 @@ #define SYNCHRONIZE() barrier() #define __return_address __builtin_return_address(0) -#define dfltprid 0 +#define XFS_PROJID_DEFAULT 0 #define MAXPATHLEN 1024 #define MIN(a,b) (min(a,b)) diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index d109cc557be..f8e854b4fde 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -835,7 +835,7 @@ xfs_qm_dqattach_locked( xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP, flags & XFS_QMOPT_DQALLOC, ip->i_udquot, &ip->i_gdquot) : - xfs_qm_dqattach_one(ip, ip->i_d.di_projid, XFS_DQ_PROJ, + xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ, flags & XFS_QMOPT_DQALLOC, ip->i_udquot, &ip->i_gdquot); /* @@ -1630,7 +1630,7 @@ xfs_qm_dqusage_adjust( } if (XFS_IS_PQUOTA_ON(mp)) { - error = xfs_qm_quotacheck_dqadjust(ip, ip->i_d.di_projid, + error = xfs_qm_quotacheck_dqadjust(ip, xfs_get_projid(ip), XFS_DQ_PROJ, nblks, rtblks); if (error) goto error0; @@ -2249,7 +2249,7 @@ xfs_qm_vop_dqalloc( xfs_dqunlock(gq); } } else if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) { - if (ip->i_d.di_projid != prid) { + if (xfs_get_projid(ip) != prid) { xfs_iunlock(ip, lockflags); if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid, XFS_DQ_PROJ, @@ -2371,7 +2371,7 @@ xfs_qm_vop_chown_reserve( } if (XFS_IS_OQUOTA_ON(ip->i_mount) && gdqp) { if (XFS_IS_PQUOTA_ON(ip->i_mount) && - ip->i_d.di_projid != be32_to_cpu(gdqp->q_core.d_id)) + xfs_get_projid(ip) != be32_to_cpu(gdqp->q_core.d_id)) prjflags = XFS_QMOPT_ENOSPC; if (prjflags || @@ -2475,7 +2475,7 @@ xfs_qm_vop_create_dqattach( ip->i_gdquot = gdqp; ASSERT(XFS_IS_OQUOTA_ON(mp)); ASSERT((XFS_IS_GQUOTA_ON(mp) ? - ip->i_d.di_gid : ip->i_d.di_projid) == + ip->i_d.di_gid : xfs_get_projid(ip)) == be32_to_cpu(gdqp->q_core.d_id)); xfs_trans_mod_dquot(tp, gdqp, XFS_TRANS_DQ_ICOUNT, 1); } diff --git a/fs/xfs/quota/xfs_qm_bhv.c b/fs/xfs/quota/xfs_qm_bhv.c index bea02d786c5..45b5cb1788a 100644 --- a/fs/xfs/quota/xfs_qm_bhv.c +++ b/fs/xfs/quota/xfs_qm_bhv.c @@ -81,7 +81,7 @@ xfs_qm_statvfs( xfs_mount_t *mp = ip->i_mount; xfs_dquot_t *dqp; - if (!xfs_qm_dqget(mp, NULL, ip->i_d.di_projid, XFS_DQ_PROJ, 0, &dqp)) { + if (!xfs_qm_dqget(mp, NULL, xfs_get_projid(ip), XFS_DQ_PROJ, 0, &dqp)) { xfs_fill_statvfs_from_dquot(statp, &dqp->q_core); xfs_qm_dqput(dqp); } diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index 57847434fa5..bdebc183223 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -1165,7 +1165,7 @@ xfs_qm_internalqcheck_adjust( } xfs_qm_internalqcheck_get_dquots(mp, (xfs_dqid_t) ip->i_d.di_uid, - (xfs_dqid_t) ip->i_d.di_projid, + (xfs_dqid_t) xfs_get_projid(ip), (xfs_dqid_t) ip->i_d.di_gid, &ud, &gd); if (XFS_IS_UQUOTA_ON(mp)) { diff --git a/fs/xfs/xfs_dinode.h b/fs/xfs/xfs_dinode.h index e5b153b2e6a..dffba9ba0db 100644 --- a/fs/xfs/xfs_dinode.h +++ b/fs/xfs/xfs_dinode.h @@ -49,8 +49,9 @@ typedef struct xfs_dinode { __be32 di_uid; /* owner's user id */ __be32 di_gid; /* owner's group id */ __be32 di_nlink; /* number of links to file */ - __be16 di_projid; /* owner's project id */ - __u8 di_pad[8]; /* unused, zeroed space */ + __be16 di_projid_lo; /* lower part of owner's project id */ + __be16 di_projid_hi; /* higher part owner's project id */ + __u8 di_pad[6]; /* unused, zeroed space */ __be16 di_flushiter; /* incremented on flush */ xfs_timestamp_t di_atime; /* time last accessed */ xfs_timestamp_t di_mtime; /* time last modified */ diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h index 10f6093671b..8f6fc1a9638 100644 --- a/fs/xfs/xfs_fs.h +++ b/fs/xfs/xfs_fs.h @@ -293,9 +293,11 @@ typedef struct xfs_bstat { __s32 bs_extsize; /* extent size */ __s32 bs_extents; /* number of extents */ __u32 bs_gen; /* generation count */ - __u16 bs_projid; /* project id */ + __u16 bs_projid_lo; /* lower part of project id */ +#define bs_projid bs_projid_lo /* (previously just bs_projid) */ __u16 bs_forkoff; /* inode fork offset in bytes */ - unsigned char bs_pad[12]; /* pad space, unused */ + __u16 bs_projid_hi; /* higher part of project id */ + unsigned char bs_pad[10]; /* pad space, unused */ __u32 bs_dmevmask; /* DMIG event mask */ __u16 bs_dmstate; /* DMIG state info */ __u16 bs_aextents; /* attribute number of extents */ diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 493d6b0cbef..108c7a085f9 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -660,7 +660,8 @@ xfs_dinode_from_disk( to->di_uid = be32_to_cpu(from->di_uid); to->di_gid = be32_to_cpu(from->di_gid); to->di_nlink = be32_to_cpu(from->di_nlink); - to->di_projid = be16_to_cpu(from->di_projid); + to->di_projid_lo = be16_to_cpu(from->di_projid_lo); + to->di_projid_hi = be16_to_cpu(from->di_projid_hi); memcpy(to->di_pad, from->di_pad, sizeof(to->di_pad)); to->di_flushiter = be16_to_cpu(from->di_flushiter); to->di_atime.t_sec = be32_to_cpu(from->di_atime.t_sec); @@ -695,7 +696,8 @@ xfs_dinode_to_disk( to->di_uid = cpu_to_be32(from->di_uid); to->di_gid = cpu_to_be32(from->di_gid); to->di_nlink = cpu_to_be32(from->di_nlink); - to->di_projid = cpu_to_be16(from->di_projid); + to->di_projid_lo = cpu_to_be16(from->di_projid_lo); + to->di_projid_hi = cpu_to_be16(from->di_projid_hi); memcpy(to->di_pad, from->di_pad, sizeof(to->di_pad)); to->di_flushiter = cpu_to_be16(from->di_flushiter); to->di_atime.t_sec = cpu_to_be32(from->di_atime.t_sec); @@ -874,7 +876,7 @@ xfs_iread( if (ip->i_d.di_version == 1) { ip->i_d.di_nlink = ip->i_d.di_onlink; ip->i_d.di_onlink = 0; - ip->i_d.di_projid = 0; + xfs_set_projid(ip, 0); } ip->i_delayed_blks = 0; @@ -982,7 +984,7 @@ xfs_ialloc( mode_t mode, xfs_nlink_t nlink, xfs_dev_t rdev, - xfs_prid_t prid, + prid_t prid, int okalloc, xfs_buf_t **ialloc_context, boolean_t *call_again, @@ -1026,7 +1028,7 @@ xfs_ialloc( ASSERT(ip->i_d.di_nlink == nlink); ip->i_d.di_uid = current_fsuid(); ip->i_d.di_gid = current_fsgid(); - ip->i_d.di_projid = prid; + xfs_set_projid(ip, prid); memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad)); /* @@ -3007,7 +3009,7 @@ xfs_iflush_int( memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad)); memset(&(dip->di_pad[0]), 0, sizeof(dip->di_pad)); - ASSERT(ip->i_d.di_projid == 0); + ASSERT(xfs_get_projid(ip) == 0); } } diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index ac82327ec9d..fac52290de9 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -134,8 +134,9 @@ typedef struct xfs_icdinode { __uint32_t di_uid; /* owner's user id */ __uint32_t di_gid; /* owner's group id */ __uint32_t di_nlink; /* number of links to file */ - __uint16_t di_projid; /* owner's project id */ - __uint8_t di_pad[8]; /* unused, zeroed space */ + __uint16_t di_projid_lo; /* lower part of owner's project id */ + __uint16_t di_projid_hi; /* higher part of owner's project id */ + __uint8_t di_pad[6]; /* unused, zeroed space */ __uint16_t di_flushiter; /* incremented on flush */ xfs_ictimestamp_t di_atime; /* time last accessed */ xfs_ictimestamp_t di_mtime; /* time last modified */ @@ -333,6 +334,25 @@ xfs_iflags_test_and_clear(xfs_inode_t *ip, unsigned short flags) return ret; } +/* + * Project quota id helpers (previously projid was 16bit only + * and using two 16bit values to hold new 32bit projid was choosen + * to retain compatibility with "old" filesystems). + */ +static inline prid_t +xfs_get_projid(struct xfs_inode *ip) +{ + return (prid_t)ip->i_d.di_projid_hi << 16 | ip->i_d.di_projid_lo; +} + +static inline void +xfs_set_projid(struct xfs_inode *ip, + prid_t projid) +{ + ip->i_d.di_projid_hi = (__uint16_t) (projid >> 16); + ip->i_d.di_projid_lo = (__uint16_t) (projid & 0xffff); +} + /* * Manage the i_flush queue embedded in the inode. This completion * queue synchronizes processes attempting to flush the in-core @@ -455,7 +475,7 @@ void xfs_inode_free(struct xfs_inode *ip); * xfs_inode.c prototypes. */ int xfs_ialloc(struct xfs_trans *, xfs_inode_t *, mode_t, - xfs_nlink_t, xfs_dev_t, xfs_prid_t, int, + xfs_nlink_t, xfs_dev_t, prid_t, int, struct xfs_buf **, boolean_t *, xfs_inode_t **); uint xfs_ip2xflags(struct xfs_inode *); diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 7e3626e5925..dc1882adaf5 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -92,7 +92,8 @@ xfs_bulkstat_one_int( * further change. */ buf->bs_nlink = dic->di_nlink; - buf->bs_projid = dic->di_projid; + buf->bs_projid_lo = dic->di_projid_lo; + buf->bs_projid_hi = dic->di_projid_hi; buf->bs_ino = ino; buf->bs_mode = dic->di_mode; buf->bs_uid = dic->di_uid; diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c index 9028733f7ed..d2af0a8381a 100644 --- a/fs/xfs/xfs_rename.c +++ b/fs/xfs/xfs_rename.c @@ -183,7 +183,7 @@ xfs_rename( * tree quota mechanism would be circumvented. */ if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && - (target_dp->i_d.di_projid != src_ip->i_d.di_projid))) { + (xfs_get_projid(target_dp) != xfs_get_projid(src_ip)))) { error = XFS_ERROR(EXDEV); goto error_return; } diff --git a/fs/xfs/xfs_sb.h b/fs/xfs/xfs_sb.h index 1b017c65749..1eb2ba58681 100644 --- a/fs/xfs/xfs_sb.h +++ b/fs/xfs/xfs_sb.h @@ -80,10 +80,12 @@ struct xfs_mount; #define XFS_SB_VERSION2_RESERVED4BIT 0x00000004 #define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */ #define XFS_SB_VERSION2_PARENTBIT 0x00000010 /* parent pointers */ +#define XFS_SB_VERSION2_PROJID32BIT 0x00000080 /* 32 bit project id */ #define XFS_SB_VERSION2_OKREALFBITS \ (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \ - XFS_SB_VERSION2_ATTR2BIT) + XFS_SB_VERSION2_ATTR2BIT | \ + XFS_SB_VERSION2_PROJID32BIT) #define XFS_SB_VERSION2_OKSASHFBITS \ (0) #define XFS_SB_VERSION2_OKREALBITS \ @@ -495,6 +497,12 @@ static inline void xfs_sb_version_removeattr2(xfs_sb_t *sbp) sbp->sb_versionnum &= ~XFS_SB_VERSION_MOREBITSBIT; } +static inline int xfs_sb_version_hasprojid32bit(xfs_sb_t *sbp) +{ + return xfs_sb_version_hasmorebits(sbp) && + (sbp->sb_features2 & XFS_SB_VERSION2_PROJID32BIT); +} + /* * end of superblock version macros */ diff --git a/fs/xfs/xfs_types.h b/fs/xfs/xfs_types.h index 320775295e3..26d1867d815 100644 --- a/fs/xfs/xfs_types.h +++ b/fs/xfs/xfs_types.h @@ -73,8 +73,6 @@ typedef __int32_t xfs_tid_t; /* transaction identifier */ typedef __uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ typedef __uint32_t xfs_dahash_t; /* dir/attr hash value */ -typedef __uint16_t xfs_prid_t; /* prid_t truncated to 16bits in XFS */ - typedef __uint32_t xlog_tid_t; /* transaction ID type */ /* diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index f4195f6b8bc..8e4a63c4151 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -114,7 +114,7 @@ xfs_setattr( */ ASSERT(udqp == NULL); ASSERT(gdqp == NULL); - code = xfs_qm_vop_dqalloc(ip, uid, gid, ip->i_d.di_projid, + code = xfs_qm_vop_dqalloc(ip, uid, gid, xfs_get_projid(ip), qflags, &udqp, &gdqp); if (code) return code; @@ -1268,7 +1268,7 @@ xfs_create( boolean_t unlock_dp_on_error = B_FALSE; uint cancel_flags; int committed; - xfs_prid_t prid; + prid_t prid; struct xfs_dquot *udqp = NULL; struct xfs_dquot *gdqp = NULL; uint resblks; @@ -1281,9 +1281,9 @@ xfs_create( return XFS_ERROR(EIO); if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) - prid = dp->i_d.di_projid; + prid = xfs_get_projid(dp); else - prid = dfltprid; + prid = XFS_PROJID_DEFAULT; /* * Make sure that we have allocated dquot(s) on disk. @@ -1882,7 +1882,7 @@ xfs_link( * the tree quota mechanism could be circumvented. */ if (unlikely((tdp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && - (tdp->i_d.di_projid != sip->i_d.di_projid))) { + (xfs_get_projid(tdp) != xfs_get_projid(sip)))) { error = XFS_ERROR(EXDEV); goto error_return; } @@ -1956,7 +1956,7 @@ xfs_symlink( int byte_cnt; int n; xfs_buf_t *bp; - xfs_prid_t prid; + prid_t prid; struct xfs_dquot *udqp, *gdqp; uint resblks; @@ -1979,9 +1979,9 @@ xfs_symlink( udqp = gdqp = NULL; if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) - prid = dp->i_d.di_projid; + prid = xfs_get_projid(dp); else - prid = (xfs_prid_t)dfltprid; + prid = XFS_PROJID_DEFAULT; /* * Make sure that we have allocated dquot(s) on disk. -- cgit v1.2.3-18-g5258 From a731cd116c9334e01bcf3e676c0c621fe7de6ce4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Sep 2010 14:33:15 +0000 Subject: xfs: semaphore cleanup Get rid of init_MUTEX[_LOCKED]() and use sema_init() instead. (Ported to current XFS code by .) Signed-off-by: Thomas Gleixner Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 47ef97f46fe..52785189212 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -189,7 +189,7 @@ _xfs_buf_initialize( init_completion(&bp->b_iowait); INIT_LIST_HEAD(&bp->b_list); RB_CLEAR_NODE(&bp->b_rbnode); - init_MUTEX_LOCKED(&bp->b_sema); /* held, no waiters */ + sema_init(&bp->b_sema, 0); /* held, no waiters */ XB_SET_OWNER(bp); bp->b_target = target; bp->b_file_offset = range_base; -- cgit v1.2.3-18-g5258 From 7681bfeeccff5efa9eb29bf09249a3c400b15327 Mon Sep 17 00:00:00 2001 From: Yasuaki Ishimatsu Date: Tue, 19 Oct 2010 09:05:00 +0200 Subject: block: fix accounting bug on cross partition merges /proc/diskstats would display a strange output as follows. $ cat /proc/diskstats |grep sda 8 0 sda 90524 7579 102154 20464 0 0 0 0 0 14096 20089 8 1 sda1 19085 1352 21841 4209 0 0 0 0 4294967064 15689 4293424691 ~~~~~~~~~~ 8 2 sda2 71252 3624 74891 15950 0 0 0 0 232 23995 1562390 8 3 sda3 54 487 2188 92 0 0 0 0 0 88 92 8 4 sda4 4 0 8 0 0 0 0 0 0 0 0 8 5 sda5 81 2027 2130 138 0 0 0 0 0 87 137 Its reason is the wrong way of accounting hd_struct->in_flight. When a bio is merged into a request belongs to different partition by ELEVATOR_FRONT_MERGE. The detailed root cause is as follows. Assuming that there are two partition, sda1 and sda2. 1. A request for sda2 is in request_queue. Hence sda1's hd_struct->in_flight is 0 and sda2's one is 1. | hd_struct->in_flight --------------------------- sda1 | 0 sda2 | 1 --------------------------- 2. A bio belongs to sda1 is issued and is merged into the request mentioned on step1 by ELEVATOR_BACK_MERGE. The first sector of the request is changed from sda2 region to sda1 region. However the two partition's hd_struct->in_flight are not changed. | hd_struct->in_flight --------------------------- sda1 | 0 sda2 | 1 --------------------------- 3. The request is finished and blk_account_io_done() is called. In this case, sda2's hd_struct->in_flight, not a sda1's one, is decremented. | hd_struct->in_flight --------------------------- sda1 | -1 sda2 | 1 --------------------------- The patch fixes the problem by caching the partition lookup inside the request structure, hence making sure that the increment and decrement will always happen on the same partition struct. This also speeds up IO with accounting enabled, since it cuts down on the number of lookups we have to do. When reloading partition tables, quiesce IO to ensure that no request references to the partition struct exists. When it is safe to free the partition table, the IO for that device is restarted again. Signed-off-by: Yasuaki Ishimatsu Cc: stable@kernel.org Signed-off-by: Jens Axboe --- fs/partitions/check.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'fs') diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 6dfbee03ccc..30f46c2cb9d 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -365,17 +365,25 @@ struct device_type part_type = { static void delete_partition_rcu_cb(struct rcu_head *head) { struct hd_struct *part = container_of(head, struct hd_struct, rcu_head); + struct gendisk *disk = part_to_disk(part); + struct request_queue *q = disk->queue; + unsigned long flags; part->start_sect = 0; part->nr_sects = 0; part_stat_set_all(part, 0); put_device(part_to_dev(part)); + + spin_lock_irqsave(q->queue_lock, flags); + elv_quiesce_end(q); + spin_unlock_irqrestore(q->queue_lock, flags); } void delete_partition(struct gendisk *disk, int partno) { struct disk_part_tbl *ptbl = disk->part_tbl; struct hd_struct *part; + struct request_queue *q = disk->queue; if (partno >= ptbl->len) return; @@ -390,6 +398,10 @@ void delete_partition(struct gendisk *disk, int partno) kobject_put(part->holder_dir); device_del(part_to_dev(part)); + spin_lock_irq(q->queue_lock); + elv_quiesce_start(q); + spin_unlock_irq(q->queue_lock); + call_rcu(&part->rcu_head, delete_partition_rcu_cb); } -- cgit v1.2.3-18-g5258 From c4a047272566b44b44222369d50a307c708c4f74 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 24 Aug 2009 22:42:56 +0000 Subject: fix rawctl compat ioctls breakage on amd64 and itanic RAW_SETBIND and RAW_GETBIND 32bit versions are fscked in interesting ways. 1) fs/compat_ioctl.c has COMPATIBLE_IOCTL(RAW_SETBIND) followed by HANDLE_IOCTL(RAW_SETBIND, raw_ioctl). The latter is ignored. 2) on amd64 (and itanic) the damn thing is broken - we have int + u64 + u64 and layouts on i386 and amd64 are _not_ the same. raw_ioctl() would work there, but it's never called due to (1). As it is, i386 /sbin/raw definitely doesn't work on amd64 boxen. 3) switching to raw_ioctl() as is would *not* work on e.g. sparc64 and ppc64, which would be rather sad, seeing that normal userland there is 32bit. The thing is, slapping __packed on the struct in question does not DTRT - it eliminates *all* padding. The real solution is to use compat_u64. 4) of course, all that stuff has no business being outside of raw.c in the first place - there should be ->compat_ioctl() for /dev/rawctl instead of messing with compat_ioctl.c. [akpm@linux-foundation.org: coding-style fixes] [arnd@arndb.de: port to 2.6.36] Signed-off-by: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Arnd Bergmann --- fs/compat_ioctl.c | 70 ------------------------------------------------------- 1 file changed, 70 deletions(-) (limited to 'fs') diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 03e59aa318e..d0ad09d5778 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -599,69 +599,6 @@ static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, #define HIDPGETCONNLIST _IOR('H', 210, int) #define HIDPGETCONNINFO _IOR('H', 211, int) -#ifdef CONFIG_BLOCK -struct raw32_config_request -{ - compat_int_t raw_minor; - __u64 block_major; - __u64 block_minor; -} __attribute__((packed)); - -static int get_raw32_request(struct raw_config_request *req, struct raw32_config_request __user *user_req) -{ - int ret; - - if (!access_ok(VERIFY_READ, user_req, sizeof(struct raw32_config_request))) - return -EFAULT; - - ret = __get_user(req->raw_minor, &user_req->raw_minor); - ret |= __get_user(req->block_major, &user_req->block_major); - ret |= __get_user(req->block_minor, &user_req->block_minor); - - return ret ? -EFAULT : 0; -} - -static int set_raw32_request(struct raw_config_request *req, struct raw32_config_request __user *user_req) -{ - int ret; - - if (!access_ok(VERIFY_WRITE, user_req, sizeof(struct raw32_config_request))) - return -EFAULT; - - ret = __put_user(req->raw_minor, &user_req->raw_minor); - ret |= __put_user(req->block_major, &user_req->block_major); - ret |= __put_user(req->block_minor, &user_req->block_minor); - - return ret ? -EFAULT : 0; -} - -static int raw_ioctl(unsigned fd, unsigned cmd, - struct raw32_config_request __user *user_req) -{ - int ret; - - switch (cmd) { - case RAW_SETBIND: - default: { /* RAW_GETBIND */ - struct raw_config_request req; - mm_segment_t oldfs = get_fs(); - - if ((ret = get_raw32_request(&req, user_req))) - return ret; - - set_fs(KERNEL_DS); - ret = sys_ioctl(fd,cmd,(unsigned long)&req); - set_fs(oldfs); - - if ((!ret) && (cmd == RAW_GETBIND)) { - ret = set_raw32_request(&req, user_req); - } - break; - } - } - return ret; -} -#endif /* CONFIG_BLOCK */ struct serial_struct32 { compat_int_t type; @@ -1262,9 +1199,6 @@ COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE5) COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS) COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS) COMPATIBLE_IOCTL(OSS_GETVERSION) -/* Raw devices */ -COMPATIBLE_IOCTL(RAW_SETBIND) -COMPATIBLE_IOCTL(RAW_GETBIND) /* SMB ioctls which do not need any translations */ COMPATIBLE_IOCTL(SMB_IOC_NEWCONN) /* Watchdog */ @@ -1523,10 +1457,6 @@ static long do_ioctl_trans(int fd, unsigned int cmd, case MTIOCGET32: case MTIOCPOS32: return mt_ioctl_trans(fd, cmd, argp); - /* Raw devices */ - case RAW_SETBIND: - case RAW_GETBIND: - return raw_ioctl(fd, cmd, argp); #endif /* One SMB ioctl needs translations. */ #define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, compat_uid_t) -- cgit v1.2.3-18-g5258 From 89f150f401c32b0a587dcb98d3bcfafe0b9c1c70 Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Tue, 19 Oct 2010 11:47:52 -0500 Subject: Clean up two declarations of blob_len - Eliminate double declaration of variable blob_len - Modify function build_ntlmssp_auth_blob to return error code as well as length of the blob. Signed-off-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/sess.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 2111bed71b1..e35dc60d325 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -464,6 +464,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, maximum possible size is fixed and small, making this approach cleaner. This function returns the length of the data in the blob */ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, + u16 *buflen, struct cifsSesInfo *ses, const struct nls_table *nls_cp) { @@ -558,7 +559,8 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, sec_blob->SessionKey.MaximumLength = 0; setup_ntlmv2_ret: - return tmp - pbuffer; + *buflen = tmp - pbuffer; + return rc; } @@ -591,7 +593,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int bytes_remaining; struct key *spnego_key = NULL; __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ - int blob_len; + u16 blob_len; char *ntlmsspblob = NULL; if (ses == NULL) @@ -848,8 +850,10 @@ ssetup_ntlmssp_authenticate: goto ssetup_exit; } - blob_len = build_ntlmssp_auth_blob(ntlmsspblob, - ses, nls_cp); + rc = build_ntlmssp_auth_blob(ntlmsspblob, + &blob_len, ses, nls_cp); + if (rc) + goto ssetup_exit; iov[1].iov_len = blob_len; iov[1].iov_base = ntlmsspblob; pSMB->req.SecurityBlobLength = @@ -927,7 +931,6 @@ ssetup_ntlmssp_authenticate: bcc_ptr = pByteArea(smb_buf); if (smb_buf->WordCount == 4) { - __u16 blob_len; blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); if (blob_len > bytes_remaining) { cERROR(1, "bad security blob length %d", blob_len); -- cgit v1.2.3-18-g5258 From 3e24e132878c83910b61eb7704511a6d96a0389f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 19 Oct 2010 17:55:54 +0200 Subject: cifs: cancel_delayed_work() + flush_scheduled_work() -> cancel_delayed_work_sync() flush_scheduled_work() is going away. Signed-off-by: Tejun Heo Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_dfs_ref.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index f4aab6f0117..c68a056f27f 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -44,8 +44,7 @@ static void cifs_dfs_expire_automounts(struct work_struct *work) void cifs_dfs_release_automount_timer(void) { BUG_ON(!list_empty(&cifs_dfs_automount_list)); - cancel_delayed_work(&cifs_dfs_automount_task); - flush_scheduled_work(); + cancel_delayed_work_sync(&cifs_dfs_automount_task); } /** -- cgit v1.2.3-18-g5258 From 7669a2c95e502a77f93f27e5449fc93a00d588b6 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Mon, 17 May 2010 12:31:35 -0700 Subject: ceph: lookup pool in osdmap by name Implement a pool lookup by name. This will be used by rbd. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/osdmap.c | 13 +++++++++++++ fs/ceph/osdmap.h | 2 ++ 2 files changed, 15 insertions(+) (limited to 'fs') diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index e31f118f139..3ccd11761cb 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -417,6 +417,19 @@ static struct ceph_pg_pool_info *__lookup_pg_pool(struct rb_root *root, int id) return NULL; } +int ceph_pg_poolid_by_name(struct ceph_osdmap *map, const char *name) +{ + struct rb_node *rbp; + + for (rbp = rb_first(&map->pg_pools); rbp; rbp = rb_next(rbp)) { + struct ceph_pg_pool_info *pi = + rb_entry(rbp, struct ceph_pg_pool_info, node); + if (pi->name && strcmp(pi->name, name) == 0) + return pi->id; + } + return -ENOENT; +} + static void __remove_pg_pool(struct rb_root *root, struct ceph_pg_pool_info *pi) { rb_erase(&pi->node, root); diff --git a/fs/ceph/osdmap.h b/fs/ceph/osdmap.h index 970b547e510..a592b211be3 100644 --- a/fs/ceph/osdmap.h +++ b/fs/ceph/osdmap.h @@ -125,4 +125,6 @@ extern int ceph_calc_pg_acting(struct ceph_osdmap *osdmap, struct ceph_pg pgid, extern int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, struct ceph_pg pgid); +extern int ceph_pg_poolid_by_name(struct ceph_osdmap *map, const char *name); + #endif -- cgit v1.2.3-18-g5258 From 3499e8a5d4dbb083324efd942e2c4fb7eb65f27c Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Tue, 6 Apr 2010 14:51:47 -0700 Subject: ceph: refactor osdc requests creation functions The osd requests creation are being decoupled from the vino parameter, allowing clients using the osd to use other arbitrary object names that are not necessarily vino based. Also, calc_raw_layout now takes a snap id. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/osd_client.c | 187 +++++++++++++++++++++++++++++++++++---------------- fs/ceph/osd_client.h | 25 +++++++ 2 files changed, 155 insertions(+), 57 deletions(-) (limited to 'fs') diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 3b5571b8ce2..2647dafd96f 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -22,6 +22,35 @@ static int __kick_requests(struct ceph_osd_client *osdc, static void kick_requests(struct ceph_osd_client *osdc, struct ceph_osd *osd); +void ceph_calc_raw_layout(struct ceph_osd_client *osdc, + struct ceph_file_layout *layout, + u64 snapid, + u64 off, u64 len, u64 *bno, + struct ceph_osd_request *req) +{ + struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; + struct ceph_osd_op *op = (void *)(reqhead + 1); + u64 orig_len = len; + u64 objoff, objlen; /* extent in object */ + + reqhead->snapid = cpu_to_le64(snapid); + + /* object extent? */ + ceph_calc_file_object_mapping(layout, off, &len, bno, + &objoff, &objlen); + if (len < orig_len) + dout(" skipping last %llu, final file extent %llu~%llu\n", + orig_len - len, off, len); + + op->extent.offset = cpu_to_le64(objoff); + op->extent.length = cpu_to_le64(objlen); + req->r_num_pages = calc_pages_for(off, len); + + dout("calc_layout bno=%llx %llu~%llu (%d pages)\n", + *bno, objoff, objlen, req->r_num_pages); + +} + /* * Implement client access to distributed object storage cluster. * @@ -48,34 +77,17 @@ static void kick_requests(struct ceph_osd_client *osdc, struct ceph_osd *osd); * fill osd op in request message. */ static void calc_layout(struct ceph_osd_client *osdc, - struct ceph_vino vino, struct ceph_file_layout *layout, + struct ceph_vino vino, + struct ceph_file_layout *layout, u64 off, u64 *plen, struct ceph_osd_request *req) { - struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; - struct ceph_osd_op *op = (void *)(reqhead + 1); - u64 orig_len = *plen; - u64 objoff, objlen; /* extent in object */ u64 bno; - reqhead->snapid = cpu_to_le64(vino.snap); - - /* object extent? */ - ceph_calc_file_object_mapping(layout, off, plen, &bno, - &objoff, &objlen); - if (*plen < orig_len) - dout(" skipping last %llu, final file extent %llu~%llu\n", - orig_len - *plen, off, *plen); + ceph_calc_raw_layout(osdc, layout, vino.snap, off, *plen, &bno, req); sprintf(req->r_oid, "%llx.%08llx", vino.ino, bno); req->r_oid_len = strlen(req->r_oid); - - op->extent.offset = cpu_to_le64(objoff); - op->extent.length = cpu_to_le64(objlen); - req->r_num_pages = calc_pages_for(off, *plen); - - dout("calc_layout %s (%d) %llu~%llu (%d pages)\n", - req->r_oid, req->r_oid_len, objoff, objlen, req->r_num_pages); } /* @@ -108,43 +120,34 @@ void ceph_osdc_release_request(struct kref *kref) kfree(req); } -/* - * build new request AND message, calculate layout, and adjust file - * extent as needed. - * - * if the file was recently truncated, we include information about its - * old and new size so that the object can be updated appropriately. (we - * avoid synchronously deleting truncated objects because it's slow.) - * - * if @do_sync, include a 'startsync' command so that the osd will flush - * data quickly. - */ -struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, - struct ceph_file_layout *layout, - struct ceph_vino vino, - u64 off, u64 *plen, - int opcode, int flags, +struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, + int flags, struct ceph_snap_context *snapc, int do_sync, - u32 truncate_seq, - u64 truncate_size, - struct timespec *mtime, - bool use_mempool, int num_reply) + bool use_mempool, + gfp_t gfp_flags, + struct page **pages) { struct ceph_osd_request *req; struct ceph_msg *msg; - struct ceph_osd_request_head *head; - struct ceph_osd_op *op; - void *p; int num_op = 1 + do_sync; - size_t msg_size = sizeof(*head) + num_op*sizeof(*op); - int i; + size_t msg_size = sizeof(struct ceph_osd_request_head) + + num_op*sizeof(struct ceph_osd_op); + + if (use_mempool) { + req = mempool_alloc(osdc->req_mempool, gfp_flags); + memset(req, 0, sizeof(*req)); + } else { + req = kzalloc(sizeof(*req), gfp_flags); + } + if (!req) + return NULL; if (use_mempool) { - req = mempool_alloc(osdc->req_mempool, GFP_NOFS); + req = mempool_alloc(osdc->req_mempool, gfp_flags); memset(req, 0, sizeof(*req)); } else { - req = kzalloc(sizeof(*req), GFP_NOFS); + req = kzalloc(sizeof(*req), gfp_flags); } if (req == NULL) return NULL; @@ -164,7 +167,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, msg = ceph_msgpool_get(&osdc->msgpool_op_reply, 0); else msg = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, - OSD_OPREPLY_FRONT_LEN, GFP_NOFS); + OSD_OPREPLY_FRONT_LEN, gfp_flags); if (!msg) { ceph_osdc_put_request(req); return NULL; @@ -178,18 +181,48 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, if (use_mempool) msg = ceph_msgpool_get(&osdc->msgpool_op, 0); else - msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, GFP_NOFS); + msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, gfp_flags); if (!msg) { ceph_osdc_put_request(req); return NULL; } msg->hdr.type = cpu_to_le16(CEPH_MSG_OSD_OP); memset(msg->front.iov_base, 0, msg->front.iov_len); + + req->r_request = msg; + req->r_pages = pages; + + return req; +} + +/* + * build new request AND message + * + */ +void ceph_osdc_build_request(struct ceph_osd_request *req, + u64 off, u64 *plen, + int opcode, + struct ceph_snap_context *snapc, + int do_sync, + u32 truncate_seq, + u64 truncate_size, + struct timespec *mtime, + const char *oid, + int oid_len) +{ + struct ceph_msg *msg = req->r_request; + struct ceph_osd_request_head *head; + struct ceph_osd_op *op; + void *p; + int num_op = 1 + do_sync; + size_t msg_size = sizeof(*head) + num_op*sizeof(*op); + int i; + int flags = req->r_flags; + head = msg->front.iov_base; op = (void *)(head + 1); p = (void *)(op + num_op); - req->r_request = msg; req->r_snapc = ceph_get_snap_context(snapc); head->client_inc = cpu_to_le32(1); /* always, for now. */ @@ -199,10 +232,6 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, head->num_ops = cpu_to_le16(num_op); op->op = cpu_to_le16(opcode); - /* calculate max write size */ - calc_layout(osdc, vino, layout, off, plen, req); - req->r_file_layout = *layout; /* keep a copy */ - if (flags & CEPH_OSD_FLAG_WRITE) { req->r_request->hdr.data_off = cpu_to_le16(off); req->r_request->hdr.data_len = cpu_to_le32(*plen); @@ -212,9 +241,9 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, op->extent.truncate_seq = cpu_to_le32(truncate_seq); /* fill in oid */ - head->object_len = cpu_to_le32(req->r_oid_len); - memcpy(p, req->r_oid, req->r_oid_len); - p += req->r_oid_len; + head->object_len = cpu_to_le32(oid_len); + memcpy(p, oid, oid_len); + p += oid_len; if (do_sync) { op++; @@ -233,6 +262,50 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, msg_size = p - msg->front.iov_base; msg->front.iov_len = msg_size; msg->hdr.front_len = cpu_to_le32(msg_size); + return; +} + +/* + * build new request AND message, calculate layout, and adjust file + * extent as needed. + * + * if the file was recently truncated, we include information about its + * old and new size so that the object can be updated appropriately. (we + * avoid synchronously deleting truncated objects because it's slow.) + * + * if @do_sync, include a 'startsync' command so that the osd will flush + * data quickly. + */ +struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, + struct ceph_file_layout *layout, + struct ceph_vino vino, + u64 off, u64 *plen, + int opcode, int flags, + struct ceph_snap_context *snapc, + int do_sync, + u32 truncate_seq, + u64 truncate_size, + struct timespec *mtime, + bool use_mempool, int num_reply) +{ + struct ceph_osd_request *req = + ceph_osdc_alloc_request(osdc, flags, + snapc, do_sync, + use_mempool, + GFP_NOFS, NULL); + if (IS_ERR(req)) + return req; + + /* calculate max write size */ + calc_layout(osdc, vino, layout, off, plen, req); + req->r_file_layout = *layout; /* keep a copy */ + + ceph_osdc_build_request(req, off, plen, opcode, + snapc, do_sync, + truncate_seq, truncate_size, + mtime, + req->r_oid, req->r_oid_len); + return req; } diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index ce776989ef6..b687c2ea72e 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -119,6 +119,31 @@ extern void ceph_osdc_handle_reply(struct ceph_osd_client *osdc, extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg); +extern void ceph_calc_raw_layout(struct ceph_osd_client *osdc, + struct ceph_file_layout *layout, + u64 snapid, + u64 off, u64 len, u64 *bno, + struct ceph_osd_request *req); + +extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, + int flags, + struct ceph_snap_context *snapc, + int do_sync, + bool use_mempool, + gfp_t gfp_flags, + struct page **pages); + +extern void ceph_osdc_build_request(struct ceph_osd_request *req, + u64 off, u64 *plen, + int opcode, + struct ceph_snap_context *snapc, + int do_sync, + u32 truncate_seq, + u64 truncate_size, + struct timespec *mtime, + const char *oid, + int oid_len); + extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, struct ceph_file_layout *layout, struct ceph_vino vino, -- cgit v1.2.3-18-g5258 From 68b4476b0bc13fef18266b4140309a30e86739d2 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Tue, 6 Apr 2010 15:01:27 -0700 Subject: ceph: messenger and osdc changes for rbd Allow the messenger to send/receive data in a bio. This is added so that we wouldn't need to copy the data into pages or some other buffer when doing IO for an rbd block device. We can now have trailing variable sized data for osd ops. Also osd ops encoding is more modular. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/messenger.c | 219 +++++++++++++++++++++++++++++++++++++------- fs/ceph/messenger.h | 4 + fs/ceph/osd_client.c | 249 ++++++++++++++++++++++++++++++++++++++++----------- fs/ceph/osd_client.h | 61 ++++++++++--- fs/ceph/pagelist.c | 2 +- fs/ceph/pagelist.h | 2 +- 6 files changed, 436 insertions(+), 101 deletions(-) (limited to 'fs') diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 2502d76fcec..17a09b32a59 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include "super.h" @@ -529,8 +531,11 @@ static void prepare_write_message(struct ceph_connection *con) if (le32_to_cpu(m->hdr.data_len) > 0) { /* initialize page iterator */ con->out_msg_pos.page = 0; - con->out_msg_pos.page_pos = - le16_to_cpu(m->hdr.data_off) & ~PAGE_MASK; + if (m->pages) + con->out_msg_pos.page_pos = + le16_to_cpu(m->hdr.data_off) & ~PAGE_MASK; + else + con->out_msg_pos.page_pos = 0; con->out_msg_pos.data_pos = 0; con->out_msg_pos.did_page_crc = 0; con->out_more = 1; /* data + footer will follow */ @@ -712,6 +717,31 @@ out: return ret; /* done! */ } +#ifdef CONFIG_BLOCK +static void init_bio_iter(struct bio *bio, struct bio **iter, int *seg) +{ + if (!bio) { + *iter = NULL; + *seg = 0; + return; + } + *iter = bio; + *seg = bio->bi_idx; +} + +static void iter_bio_next(struct bio **bio_iter, int *seg) +{ + if (*bio_iter == NULL) + return; + + BUG_ON(*seg >= (*bio_iter)->bi_vcnt); + + (*seg)++; + if (*seg == (*bio_iter)->bi_vcnt) + init_bio_iter((*bio_iter)->bi_next, bio_iter, seg); +} +#endif + /* * Write as much message data payload as we can. If we finish, queue * up the footer. @@ -726,21 +756,46 @@ static int write_partial_msg_pages(struct ceph_connection *con) size_t len; int crc = con->msgr->nocrc; int ret; + int total_max_write; + int in_trail = 0; + size_t trail_len = (msg->trail ? msg->trail->length : 0); dout("write_partial_msg_pages %p msg %p page %d/%d offset %d\n", con, con->out_msg, con->out_msg_pos.page, con->out_msg->nr_pages, con->out_msg_pos.page_pos); - while (con->out_msg_pos.page < con->out_msg->nr_pages) { +#ifdef CONFIG_BLOCK + if (msg->bio && !msg->bio_iter) + init_bio_iter(msg->bio, &msg->bio_iter, &msg->bio_seg); +#endif + + while (data_len > con->out_msg_pos.data_pos) { struct page *page = NULL; void *kaddr = NULL; + int max_write = PAGE_SIZE; + int page_shift = 0; + + total_max_write = data_len - trail_len - + con->out_msg_pos.data_pos; /* * if we are calculating the data crc (the default), we need * to map the page. if our pages[] has been revoked, use the * zero page. */ - if (msg->pages) { + + /* have we reached the trail part of the data? */ + if (con->out_msg_pos.data_pos >= data_len - trail_len) { + in_trail = 1; + + total_max_write = data_len - con->out_msg_pos.data_pos; + + page = list_first_entry(&msg->trail->head, + struct page, lru); + if (crc) + kaddr = kmap(page); + max_write = PAGE_SIZE; + } else if (msg->pages) { page = msg->pages[con->out_msg_pos.page]; if (crc) kaddr = kmap(page); @@ -749,13 +804,25 @@ static int write_partial_msg_pages(struct ceph_connection *con) struct page, lru); if (crc) kaddr = kmap(page); +#ifdef CONFIG_BLOCK + } else if (msg->bio) { + struct bio_vec *bv; + + bv = bio_iovec_idx(msg->bio_iter, msg->bio_seg); + page = bv->bv_page; + page_shift = bv->bv_offset; + if (crc) + kaddr = kmap(page) + page_shift; + max_write = bv->bv_len; +#endif } else { page = con->msgr->zero_page; if (crc) kaddr = page_address(con->msgr->zero_page); } - len = min((int)(PAGE_SIZE - con->out_msg_pos.page_pos), - (int)(data_len - con->out_msg_pos.data_pos)); + len = min_t(int, max_write - con->out_msg_pos.page_pos, + total_max_write); + if (crc && !con->out_msg_pos.did_page_crc) { void *base = kaddr + con->out_msg_pos.page_pos; u32 tmpcrc = le32_to_cpu(con->out_msg->footer.data_crc); @@ -765,13 +832,14 @@ static int write_partial_msg_pages(struct ceph_connection *con) cpu_to_le32(crc32c(tmpcrc, base, len)); con->out_msg_pos.did_page_crc = 1; } - ret = kernel_sendpage(con->sock, page, - con->out_msg_pos.page_pos, len, + con->out_msg_pos.page_pos + page_shift, + len, MSG_DONTWAIT | MSG_NOSIGNAL | MSG_MORE); - if (crc && (msg->pages || msg->pagelist)) + if (crc && + (msg->pages || msg->pagelist || msg->bio || in_trail)) kunmap(page); if (ret <= 0) @@ -783,9 +851,16 @@ static int write_partial_msg_pages(struct ceph_connection *con) con->out_msg_pos.page_pos = 0; con->out_msg_pos.page++; con->out_msg_pos.did_page_crc = 0; - if (msg->pagelist) + if (in_trail) + list_move_tail(&page->lru, + &msg->trail->head); + else if (msg->pagelist) list_move_tail(&page->lru, &msg->pagelist->head); +#ifdef CONFIG_BLOCK + else if (msg->bio) + iter_bio_next(&msg->bio_iter, &msg->bio_seg); +#endif } } @@ -1305,8 +1380,7 @@ static int read_partial_message_section(struct ceph_connection *con, struct kvec *section, unsigned int sec_len, u32 *crc) { - int left; - int ret; + int ret, left; BUG_ON(!section); @@ -1329,13 +1403,83 @@ static int read_partial_message_section(struct ceph_connection *con, static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, struct ceph_msg_header *hdr, int *skip); + + +static int read_partial_message_pages(struct ceph_connection *con, + struct page **pages, + unsigned data_len, int datacrc) +{ + void *p; + int ret; + int left; + + left = min((int)(data_len - con->in_msg_pos.data_pos), + (int)(PAGE_SIZE - con->in_msg_pos.page_pos)); + /* (page) data */ + BUG_ON(pages == NULL); + p = kmap(pages[con->in_msg_pos.page]); + ret = ceph_tcp_recvmsg(con->sock, p + con->in_msg_pos.page_pos, + left); + if (ret > 0 && datacrc) + con->in_data_crc = + crc32c(con->in_data_crc, + p + con->in_msg_pos.page_pos, ret); + kunmap(pages[con->in_msg_pos.page]); + if (ret <= 0) + return ret; + con->in_msg_pos.data_pos += ret; + con->in_msg_pos.page_pos += ret; + if (con->in_msg_pos.page_pos == PAGE_SIZE) { + con->in_msg_pos.page_pos = 0; + con->in_msg_pos.page++; + } + + return ret; +} + +#ifdef CONFIG_BLOCK +static int read_partial_message_bio(struct ceph_connection *con, + struct bio **bio_iter, int *bio_seg, + unsigned data_len, int datacrc) +{ + struct bio_vec *bv = bio_iovec_idx(*bio_iter, *bio_seg); + void *p; + int ret, left; + + if (IS_ERR(bv)) + return PTR_ERR(bv); + + left = min((int)(data_len - con->in_msg_pos.data_pos), + (int)(bv->bv_len - con->in_msg_pos.page_pos)); + + p = kmap(bv->bv_page) + bv->bv_offset; + + ret = ceph_tcp_recvmsg(con->sock, p + con->in_msg_pos.page_pos, + left); + if (ret > 0 && datacrc) + con->in_data_crc = + crc32c(con->in_data_crc, + p + con->in_msg_pos.page_pos, ret); + kunmap(bv->bv_page); + if (ret <= 0) + return ret; + con->in_msg_pos.data_pos += ret; + con->in_msg_pos.page_pos += ret; + if (con->in_msg_pos.page_pos == bv->bv_len) { + con->in_msg_pos.page_pos = 0; + iter_bio_next(bio_iter, bio_seg); + } + + return ret; +} +#endif + /* * read (part of) a message. */ static int read_partial_message(struct ceph_connection *con) { struct ceph_msg *m = con->in_msg; - void *p; int ret; int to, left; unsigned front_len, middle_len, data_len, data_off; @@ -1422,7 +1566,10 @@ static int read_partial_message(struct ceph_connection *con) m->middle->vec.iov_len = 0; con->in_msg_pos.page = 0; - con->in_msg_pos.page_pos = data_off & ~PAGE_MASK; + if (m->pages) + con->in_msg_pos.page_pos = data_off & ~PAGE_MASK; + else + con->in_msg_pos.page_pos = 0; con->in_msg_pos.data_pos = 0; } @@ -1440,27 +1587,29 @@ static int read_partial_message(struct ceph_connection *con) if (ret <= 0) return ret; } +#ifdef CONFIG_BLOCK + if (m->bio && !m->bio_iter) + init_bio_iter(m->bio, &m->bio_iter, &m->bio_seg); +#endif /* (page) data */ while (con->in_msg_pos.data_pos < data_len) { - left = min((int)(data_len - con->in_msg_pos.data_pos), - (int)(PAGE_SIZE - con->in_msg_pos.page_pos)); - BUG_ON(m->pages == NULL); - p = kmap(m->pages[con->in_msg_pos.page]); - ret = ceph_tcp_recvmsg(con->sock, p + con->in_msg_pos.page_pos, - left); - if (ret > 0 && datacrc) - con->in_data_crc = - crc32c(con->in_data_crc, - p + con->in_msg_pos.page_pos, ret); - kunmap(m->pages[con->in_msg_pos.page]); - if (ret <= 0) - return ret; - con->in_msg_pos.data_pos += ret; - con->in_msg_pos.page_pos += ret; - if (con->in_msg_pos.page_pos == PAGE_SIZE) { - con->in_msg_pos.page_pos = 0; - con->in_msg_pos.page++; + if (m->pages) { + ret = read_partial_message_pages(con, m->pages, + data_len, datacrc); + if (ret <= 0) + return ret; +#ifdef CONFIG_BLOCK + } else if (m->bio) { + + ret = read_partial_message_bio(con, + &m->bio_iter, &m->bio_seg, + data_len, datacrc); + if (ret <= 0) + return ret; +#endif + } else { + BUG_ON(1); } } @@ -2136,6 +2285,10 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) m->nr_pages = 0; m->pages = NULL; m->pagelist = NULL; + m->bio = NULL; + m->bio_iter = NULL; + m->bio_seg = 0; + m->trail = NULL; dout("ceph_msg_new %p front %d\n", m, front_len); return m; @@ -2250,6 +2403,8 @@ void ceph_msg_last_put(struct kref *kref) m->pagelist = NULL; } + m->trail = NULL; + if (m->pool) ceph_msgpool_put(m->pool, m); else diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h index 76fbc957bc1..5a79450604e 100644 --- a/fs/ceph/messenger.h +++ b/fs/ceph/messenger.h @@ -82,6 +82,10 @@ struct ceph_msg { struct ceph_pagelist *pagelist; /* instead of pages */ struct list_head list_head; struct kref kref; + struct bio *bio; /* instead of pages/pagelist */ + struct bio *bio_iter; /* bio iterator */ + int bio_seg; /* current bio segment */ + struct ceph_pagelist *trail; /* the trailing part of the data */ bool front_is_vmalloc; bool more_to_follow; bool needs_out_seq; diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 2647dafd96f..c5d818e73ad 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -6,12 +6,16 @@ #include #include #include +#ifdef CONFIG_BLOCK +#include +#endif #include "super.h" #include "osd_client.h" #include "messenger.h" #include "decode.h" #include "auth.h" +#include "pagelist.h" #define OSD_OP_FRONT_LEN 4096 #define OSD_OPREPLY_FRONT_LEN 512 @@ -22,29 +26,50 @@ static int __kick_requests(struct ceph_osd_client *osdc, static void kick_requests(struct ceph_osd_client *osdc, struct ceph_osd *osd); +static int op_needs_trail(int op) +{ + switch (op) { + case CEPH_OSD_OP_GETXATTR: + case CEPH_OSD_OP_SETXATTR: + case CEPH_OSD_OP_CMPXATTR: + case CEPH_OSD_OP_CALL: + return 1; + default: + return 0; + } +} + +static int op_has_extent(int op) +{ + return (op == CEPH_OSD_OP_READ || + op == CEPH_OSD_OP_WRITE); +} + void ceph_calc_raw_layout(struct ceph_osd_client *osdc, struct ceph_file_layout *layout, u64 snapid, - u64 off, u64 len, u64 *bno, - struct ceph_osd_request *req) + u64 off, u64 *plen, u64 *bno, + struct ceph_osd_request *req, + struct ceph_osd_req_op *op) { struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; - struct ceph_osd_op *op = (void *)(reqhead + 1); - u64 orig_len = len; + u64 orig_len = *plen; u64 objoff, objlen; /* extent in object */ reqhead->snapid = cpu_to_le64(snapid); /* object extent? */ - ceph_calc_file_object_mapping(layout, off, &len, bno, + ceph_calc_file_object_mapping(layout, off, plen, bno, &objoff, &objlen); - if (len < orig_len) + if (*plen < orig_len) dout(" skipping last %llu, final file extent %llu~%llu\n", - orig_len - len, off, len); + orig_len - *plen, off, *plen); - op->extent.offset = cpu_to_le64(objoff); - op->extent.length = cpu_to_le64(objlen); - req->r_num_pages = calc_pages_for(off, len); + if (op_has_extent(op->op)) { + op->extent.offset = objoff; + op->extent.length = objlen; + } + req->r_num_pages = calc_pages_for(off, *plen); dout("calc_layout bno=%llx %llu~%llu (%d pages)\n", *bno, objoff, objlen, req->r_num_pages); @@ -80,11 +105,13 @@ static void calc_layout(struct ceph_osd_client *osdc, struct ceph_vino vino, struct ceph_file_layout *layout, u64 off, u64 *plen, - struct ceph_osd_request *req) + struct ceph_osd_request *req, + struct ceph_osd_req_op *op) { u64 bno; - ceph_calc_raw_layout(osdc, layout, vino.snap, off, *plen, &bno, req); + ceph_calc_raw_layout(osdc, layout, vino.snap, off, + plen, &bno, req, op); sprintf(req->r_oid, "%llx.%08llx", vino.ino, bno); req->r_oid_len = strlen(req->r_oid); @@ -113,35 +140,64 @@ void ceph_osdc_release_request(struct kref *kref) if (req->r_own_pages) ceph_release_page_vector(req->r_pages, req->r_num_pages); +#ifdef CONFIG_BLOCK + if (req->r_bio) + bio_put(req->r_bio); +#endif ceph_put_snap_context(req->r_snapc); + if (req->r_trail) { + ceph_pagelist_release(req->r_trail); + kfree(req->r_trail); + } if (req->r_mempool) mempool_free(req, req->r_osdc->req_mempool); else kfree(req); } +static int op_needs_trail(int op) +{ + switch (op) { + case CEPH_OSD_OP_GETXATTR: + case CEPH_OSD_OP_SETXATTR: + case CEPH_OSD_OP_CMPXATTR: + return 1; + default: + return 0; + } +} + +static int get_num_ops(struct ceph_osd_req_op *ops, int *needs_trail) +{ + int i = 0; + + if (needs_trail) + *needs_trail = 0; + while (ops[i].op) { + if (needs_trail && op_needs_trail(ops[i].op)) + *needs_trail = 1; + i++; + } + + return i; +} + struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, int flags, struct ceph_snap_context *snapc, - int do_sync, + struct ceph_osd_req_op *ops, bool use_mempool, gfp_t gfp_flags, - struct page **pages) + struct page **pages, + struct bio *bio) { struct ceph_osd_request *req; struct ceph_msg *msg; - int num_op = 1 + do_sync; - size_t msg_size = sizeof(struct ceph_osd_request_head) + - num_op*sizeof(struct ceph_osd_op); + int needs_trail; + int num_op = get_num_ops(ops, &needs_trail); + size_t msg_size = sizeof(struct ceph_osd_request_head); - if (use_mempool) { - req = mempool_alloc(osdc->req_mempool, gfp_flags); - memset(req, 0, sizeof(*req)); - } else { - req = kzalloc(sizeof(*req), gfp_flags); - } - if (!req) - return NULL; + msg_size += num_op*sizeof(struct ceph_osd_op); if (use_mempool) { req = mempool_alloc(osdc->req_mempool, gfp_flags); @@ -154,6 +210,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, req->r_osdc = osdc; req->r_mempool = use_mempool; + kref_init(&req->r_kref); init_completion(&req->r_completion); init_completion(&req->r_safe_completion); @@ -174,6 +231,15 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, } req->r_reply = msg; + /* allocate space for the trailing data */ + if (needs_trail) { + req->r_trail = kmalloc(sizeof(struct ceph_pagelist), gfp_flags); + if (!req->r_trail) { + ceph_osdc_put_request(req); + return NULL; + } + ceph_pagelist_init(req->r_trail); + } /* create request message; allow space for oid */ msg_size += 40; if (snapc) @@ -186,38 +252,87 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, ceph_osdc_put_request(req); return NULL; } + msg->hdr.type = cpu_to_le16(CEPH_MSG_OSD_OP); memset(msg->front.iov_base, 0, msg->front.iov_len); req->r_request = msg; req->r_pages = pages; +#ifdef CONFIG_BLOCK + if (bio) { + req->r_bio = bio; + bio_get(req->r_bio); + } +#endif return req; } +static void osd_req_encode_op(struct ceph_osd_request *req, + struct ceph_osd_op *dst, + struct ceph_osd_req_op *src) +{ + dst->op = cpu_to_le16(src->op); + + switch (dst->op) { + case CEPH_OSD_OP_READ: + case CEPH_OSD_OP_WRITE: + dst->extent.offset = + cpu_to_le64(src->extent.offset); + dst->extent.length = + cpu_to_le64(src->extent.length); + dst->extent.truncate_size = + cpu_to_le64(src->extent.truncate_size); + dst->extent.truncate_seq = + cpu_to_le32(src->extent.truncate_seq); + break; + + case CEPH_OSD_OP_GETXATTR: + case CEPH_OSD_OP_SETXATTR: + case CEPH_OSD_OP_CMPXATTR: + BUG_ON(!req->r_trail); + + dst->xattr.name_len = cpu_to_le32(src->xattr.name_len); + dst->xattr.value_len = cpu_to_le32(src->xattr.value_len); + dst->xattr.cmp_op = src->xattr.cmp_op; + dst->xattr.cmp_mode = src->xattr.cmp_mode; + ceph_pagelist_append(req->r_trail, src->xattr.name, + src->xattr.name_len); + ceph_pagelist_append(req->r_trail, src->xattr.val, + src->xattr.value_len); + break; + case CEPH_OSD_OP_STARTSYNC: + break; + default: + pr_err("unrecognized osd opcode %d\n", dst->op); + WARN_ON(1); + break; + } + dst->payload_len = cpu_to_le32(src->payload_len); +} + /* * build new request AND message * */ void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 *plen, - int opcode, - struct ceph_snap_context *snapc, - int do_sync, - u32 truncate_seq, - u64 truncate_size, - struct timespec *mtime, - const char *oid, - int oid_len) + u64 off, u64 *plen, + struct ceph_osd_req_op *src_ops, + struct ceph_snap_context *snapc, + struct timespec *mtime, + const char *oid, + int oid_len) { struct ceph_msg *msg = req->r_request; struct ceph_osd_request_head *head; + struct ceph_osd_req_op *src_op; struct ceph_osd_op *op; void *p; - int num_op = 1 + do_sync; + int num_op = get_num_ops(src_ops, NULL); size_t msg_size = sizeof(*head) + num_op*sizeof(*op); - int i; int flags = req->r_flags; + u64 data_len = 0; + int i; head = msg->front.iov_base; op = (void *)(head + 1); @@ -230,25 +345,23 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, if (flags & CEPH_OSD_FLAG_WRITE) ceph_encode_timespec(&head->mtime, mtime); head->num_ops = cpu_to_le16(num_op); - op->op = cpu_to_le16(opcode); - if (flags & CEPH_OSD_FLAG_WRITE) { - req->r_request->hdr.data_off = cpu_to_le16(off); - req->r_request->hdr.data_len = cpu_to_le32(*plen); - op->payload_len = cpu_to_le32(*plen); - } - op->extent.truncate_size = cpu_to_le64(truncate_size); - op->extent.truncate_seq = cpu_to_le32(truncate_seq); /* fill in oid */ head->object_len = cpu_to_le32(oid_len); memcpy(p, oid, oid_len); p += oid_len; - if (do_sync) { + src_op = src_ops; + while (src_op->op) { + osd_req_encode_op(req, op, src_op); + src_op++; op++; - op->op = cpu_to_le16(CEPH_OSD_OP_STARTSYNC); } + + if (req->r_trail) + data_len += req->r_trail->length; + if (snapc) { head->snap_seq = cpu_to_le64(snapc->seq); head->num_snaps = cpu_to_le32(snapc->num_snaps); @@ -258,6 +371,14 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, } } + if (flags & CEPH_OSD_FLAG_WRITE) { + req->r_request->hdr.data_off = cpu_to_le16(off); + req->r_request->hdr.data_len = cpu_to_le32(*plen + data_len); + } else if (data_len) { + req->r_request->hdr.data_off = 0; + req->r_request->hdr.data_len = cpu_to_le32(data_len); + } + BUG_ON(p > msg->front.iov_base + msg->front.iov_len); msg_size = p - msg->front.iov_base; msg->front.iov_len = msg_size; @@ -288,21 +409,34 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, struct timespec *mtime, bool use_mempool, int num_reply) { - struct ceph_osd_request *req = - ceph_osdc_alloc_request(osdc, flags, - snapc, do_sync, + struct ceph_osd_req_op ops[3]; + struct ceph_osd_request *req; + + ops[0].op = opcode; + ops[0].extent.truncate_seq = truncate_seq; + ops[0].extent.truncate_size = truncate_size; + ops[0].payload_len = 0; + + if (do_sync) { + ops[1].op = CEPH_OSD_OP_STARTSYNC; + ops[1].payload_len = 0; + ops[2].op = 0; + } else + ops[1].op = 0; + + req = ceph_osdc_alloc_request(osdc, flags, + snapc, ops, use_mempool, - GFP_NOFS, NULL); + GFP_NOFS, NULL, NULL); if (IS_ERR(req)) return req; /* calculate max write size */ - calc_layout(osdc, vino, layout, off, plen, req); + calc_layout(osdc, vino, layout, off, plen, req, ops); req->r_file_layout = *layout; /* keep a copy */ - ceph_osdc_build_request(req, off, plen, opcode, - snapc, do_sync, - truncate_seq, truncate_size, + ceph_osdc_build_request(req, off, plen, ops, + snapc, mtime, req->r_oid, req->r_oid_len); @@ -1177,6 +1311,10 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, req->r_request->pages = req->r_pages; req->r_request->nr_pages = req->r_num_pages; +#ifdef CONFIG_BLOCK + req->r_request->bio = req->r_bio; +#endif + req->r_request->trail = req->r_trail; register_request(osdc, req); @@ -1493,6 +1631,9 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, } m->pages = req->r_pages; m->nr_pages = req->r_num_pages; +#ifdef CONFIG_BLOCK + m->bio = req->r_bio; +#endif } *skip = 0; req->r_con_filling_msg = ceph_con_get(con); diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index b687c2ea72e..d583d1bf6cd 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -15,6 +15,7 @@ struct ceph_snap_context; struct ceph_osd_request; struct ceph_osd_client; struct ceph_authorizer; +struct ceph_pagelist; /* * completion callback for async writepages @@ -80,6 +81,11 @@ struct ceph_osd_request { struct page **r_pages; /* pages for data payload */ int r_pages_from_pool; int r_own_pages; /* if true, i own page list */ +#ifdef CONFIG_BLOCK + struct bio *r_bio; /* instead of pages */ +#endif + + struct ceph_pagelist *r_trail; /* trailing part of the data */ }; struct ceph_osd_client { @@ -110,6 +116,36 @@ struct ceph_osd_client { struct ceph_msgpool msgpool_op_reply; }; +struct ceph_osd_req_op { + u16 op; /* CEPH_OSD_OP_* */ + u32 flags; /* CEPH_OSD_FLAG_* */ + union { + struct { + u64 offset, length; + u64 truncate_size; + u32 truncate_seq; + } extent; + struct { + const char *name; + u32 name_len; + const char *val; + u32 value_len; + __u8 cmp_op; /* CEPH_OSD_CMPXATTR_OP_* */ + __u8 cmp_mode; /* CEPH_OSD_CMPXATTR_MODE_* */ + } xattr; + struct { + __u8 class_len; + __u8 method_len; + __u8 argc; + u32 indata_len; + } cls; + struct { + u64 cookie, count; + } pgls; + }; + u32 payload_len; +}; + extern int ceph_osdc_init(struct ceph_osd_client *osdc, struct ceph_client *client); extern void ceph_osdc_stop(struct ceph_osd_client *osdc); @@ -122,27 +158,26 @@ extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc, extern void ceph_calc_raw_layout(struct ceph_osd_client *osdc, struct ceph_file_layout *layout, u64 snapid, - u64 off, u64 len, u64 *bno, - struct ceph_osd_request *req); + u64 off, u64 *plen, u64 *bno, + struct ceph_osd_request *req, + struct ceph_osd_req_op *op); extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, int flags, struct ceph_snap_context *snapc, - int do_sync, + struct ceph_osd_req_op *ops, bool use_mempool, gfp_t gfp_flags, - struct page **pages); + struct page **pages, + struct bio *bio); extern void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 *plen, - int opcode, - struct ceph_snap_context *snapc, - int do_sync, - u32 truncate_seq, - u64 truncate_size, - struct timespec *mtime, - const char *oid, - int oid_len); + u64 off, u64 *plen, + struct ceph_osd_req_op *src_ops, + struct ceph_snap_context *snapc, + struct timespec *mtime, + const char *oid, + int oid_len); extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, struct ceph_file_layout *layout, diff --git a/fs/ceph/pagelist.c b/fs/ceph/pagelist.c index 46a368b6dce..326e1c04176 100644 --- a/fs/ceph/pagelist.c +++ b/fs/ceph/pagelist.c @@ -39,7 +39,7 @@ static int ceph_pagelist_addpage(struct ceph_pagelist *pl) return 0; } -int ceph_pagelist_append(struct ceph_pagelist *pl, void *buf, size_t len) +int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len) { while (pl->room < len) { size_t bit = pl->room; diff --git a/fs/ceph/pagelist.h b/fs/ceph/pagelist.h index e8a4187e108..cc9327aa1c9 100644 --- a/fs/ceph/pagelist.h +++ b/fs/ceph/pagelist.h @@ -19,7 +19,7 @@ static inline void ceph_pagelist_init(struct ceph_pagelist *pl) } extern int ceph_pagelist_release(struct ceph_pagelist *pl); -extern int ceph_pagelist_append(struct ceph_pagelist *pl, void *d, size_t l); +extern int ceph_pagelist_append(struct ceph_pagelist *pl, const void *d, size_t l); static inline int ceph_pagelist_encode_64(struct ceph_pagelist *pl, u64 v) { -- cgit v1.2.3-18-g5258 From ae1533b62b3369e6ae32338f4a77d64d0e88f676 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Tue, 18 May 2010 16:38:08 -0700 Subject: ceph-rbd: osdc support for osd call and rollback operations This will be used for rbd snapshots administration. Signed-off-by: Yehuda Sadeh --- fs/ceph/decode.h | 5 +++++ fs/ceph/osd_client.c | 18 ++++++++++++++++++ fs/ceph/osd_client.h | 6 ++++++ 3 files changed, 29 insertions(+) (limited to 'fs') diff --git a/fs/ceph/decode.h b/fs/ceph/decode.h index 3d25415afe6..c5b6939fb32 100644 --- a/fs/ceph/decode.h +++ b/fs/ceph/decode.h @@ -191,6 +191,11 @@ static inline void ceph_encode_string(void **p, void *end, ceph_encode_need(p, end, n, bad); \ ceph_encode_copy(p, pv, n); \ } while (0) +#define ceph_encode_string_safe(p, end, s, n, bad) \ + do { \ + ceph_encode_need(p, end, n, bad); \ + ceph_encode_string(p, end, s, n); \ + } while (0) #endif diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index c5d818e73ad..0edb43f1ef2 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -161,6 +161,7 @@ static int op_needs_trail(int op) case CEPH_OSD_OP_GETXATTR: case CEPH_OSD_OP_SETXATTR: case CEPH_OSD_OP_CMPXATTR: + case CEPH_OSD_OP_CALL: return 1; default: return 0; @@ -301,6 +302,23 @@ static void osd_req_encode_op(struct ceph_osd_request *req, ceph_pagelist_append(req->r_trail, src->xattr.val, src->xattr.value_len); break; + case CEPH_OSD_OP_CALL: + BUG_ON(!req->r_trail); + + dst->cls.class_len = src->cls.class_len; + dst->cls.method_len = src->cls.method_len; + dst->cls.indata_len = cpu_to_le32(src->cls.indata_len); + + ceph_pagelist_append(req->r_trail, src->cls.class_name, + src->cls.class_len); + ceph_pagelist_append(req->r_trail, src->cls.method_name, + src->cls.method_len); + ceph_pagelist_append(req->r_trail, src->cls.indata, + src->cls.indata_len); + break; + case CEPH_OSD_OP_ROLLBACK: + dst->snap.snapid = cpu_to_le64(src->snap.snapid); + break; case CEPH_OSD_OP_STARTSYNC: break; default: diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h index d583d1bf6cd..65c9c560f1a 100644 --- a/fs/ceph/osd_client.h +++ b/fs/ceph/osd_client.h @@ -134,14 +134,20 @@ struct ceph_osd_req_op { __u8 cmp_mode; /* CEPH_OSD_CMPXATTR_MODE_* */ } xattr; struct { + const char *class_name; __u8 class_len; + const char *method_name; __u8 method_len; __u8 argc; + const char *indata; u32 indata_len; } cls; struct { u64 cookie, count; } pgls; + struct { + u64 snapid; + } snap; }; u32 payload_len; }; -- cgit v1.2.3-18-g5258 From 3d14c5d2b6e15c21d8e5467dc62d33127c23a644 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Tue, 6 Apr 2010 15:14:15 -0700 Subject: ceph: factor out libceph from Ceph file system This factors out protocol and low-level storage parts of ceph into a separate libceph module living in net/ceph and include/linux/ceph. This is mostly a matter of moving files around. However, a few key pieces of the interface change as well: - ceph_client becomes ceph_fs_client and ceph_client, where the latter captures the mon and osd clients, and the fs_client gets the mds client and file system specific pieces. - Mount option parsing and debugfs setup is correspondingly broken into two pieces. - The mon client gets a generic handler callback for otherwise unknown messages (mds map, in this case). - The basic supported/required feature bits can be expanded (and are by ceph_fs_client). No functional change, aside from some subtle error handling cases that got cleaned up in the refactoring process. Signed-off-by: Sage Weil --- fs/ceph/Kconfig | 14 +- fs/ceph/Makefile | 11 +- fs/ceph/README | 20 - fs/ceph/addr.c | 65 +- fs/ceph/armor.c | 103 -- fs/ceph/auth.c | 259 ----- fs/ceph/auth.h | 92 -- fs/ceph/auth_none.c | 131 --- fs/ceph/auth_none.h | 30 - fs/ceph/auth_x.c | 687 ------------- fs/ceph/auth_x.h | 49 - fs/ceph/auth_x_protocol.h | 90 -- fs/ceph/buffer.c | 65 -- fs/ceph/buffer.h | 39 - fs/ceph/caps.c | 35 +- fs/ceph/ceph_debug.h | 37 - fs/ceph/ceph_frag.c | 3 +- fs/ceph/ceph_frag.h | 109 -- fs/ceph/ceph_fs.c | 72 -- fs/ceph/ceph_fs.h | 728 -------------- fs/ceph/ceph_hash.c | 118 --- fs/ceph/ceph_hash.h | 13 - fs/ceph/ceph_strings.c | 193 ---- fs/ceph/crush/crush.c | 151 --- fs/ceph/crush/crush.h | 180 ---- fs/ceph/crush/hash.c | 149 --- fs/ceph/crush/hash.h | 17 - fs/ceph/crush/mapper.c | 609 ------------ fs/ceph/crush/mapper.h | 20 - fs/ceph/crypto.c | 412 -------- fs/ceph/crypto.h | 48 - fs/ceph/debugfs.c | 407 ++------ fs/ceph/decode.h | 201 ---- fs/ceph/dir.c | 55 +- fs/ceph/export.c | 5 +- fs/ceph/file.c | 207 +--- fs/ceph/inode.c | 19 +- fs/ceph/ioctl.c | 11 +- fs/ceph/locks.c | 6 +- fs/ceph/mds_client.c | 86 +- fs/ceph/mds_client.h | 20 +- fs/ceph/mdsmap.c | 11 +- fs/ceph/mdsmap.h | 62 -- fs/ceph/messenger.c | 2432 --------------------------------------------- fs/ceph/messenger.h | 257 ----- fs/ceph/mon_client.c | 1018 ------------------- fs/ceph/mon_client.h | 121 --- fs/ceph/msgpool.c | 64 -- fs/ceph/msgpool.h | 25 - fs/ceph/msgr.h | 175 ---- fs/ceph/osd_client.c | 1771 --------------------------------- fs/ceph/osd_client.h | 233 ----- fs/ceph/osdmap.c | 1123 --------------------- fs/ceph/osdmap.h | 130 --- fs/ceph/pagelist.c | 63 -- fs/ceph/pagelist.h | 54 - fs/ceph/rados.h | 405 -------- fs/ceph/snap.c | 10 +- fs/ceph/strings.c | 117 +++ fs/ceph/super.c | 1154 +++++++++------------ fs/ceph/super.h | 397 +++----- fs/ceph/types.h | 29 - fs/ceph/xattr.c | 15 +- 63 files changed, 1040 insertions(+), 14192 deletions(-) delete mode 100644 fs/ceph/README delete mode 100644 fs/ceph/armor.c delete mode 100644 fs/ceph/auth.c delete mode 100644 fs/ceph/auth.h delete mode 100644 fs/ceph/auth_none.c delete mode 100644 fs/ceph/auth_none.h delete mode 100644 fs/ceph/auth_x.c delete mode 100644 fs/ceph/auth_x.h delete mode 100644 fs/ceph/auth_x_protocol.h delete mode 100644 fs/ceph/buffer.c delete mode 100644 fs/ceph/buffer.h delete mode 100644 fs/ceph/ceph_debug.h delete mode 100644 fs/ceph/ceph_frag.h delete mode 100644 fs/ceph/ceph_fs.c delete mode 100644 fs/ceph/ceph_fs.h delete mode 100644 fs/ceph/ceph_hash.c delete mode 100644 fs/ceph/ceph_hash.h delete mode 100644 fs/ceph/ceph_strings.c delete mode 100644 fs/ceph/crush/crush.c delete mode 100644 fs/ceph/crush/crush.h delete mode 100644 fs/ceph/crush/hash.c delete mode 100644 fs/ceph/crush/hash.h delete mode 100644 fs/ceph/crush/mapper.c delete mode 100644 fs/ceph/crush/mapper.h delete mode 100644 fs/ceph/crypto.c delete mode 100644 fs/ceph/crypto.h delete mode 100644 fs/ceph/decode.h delete mode 100644 fs/ceph/mdsmap.h delete mode 100644 fs/ceph/messenger.c delete mode 100644 fs/ceph/messenger.h delete mode 100644 fs/ceph/mon_client.c delete mode 100644 fs/ceph/mon_client.h delete mode 100644 fs/ceph/msgpool.c delete mode 100644 fs/ceph/msgpool.h delete mode 100644 fs/ceph/msgr.h delete mode 100644 fs/ceph/osd_client.c delete mode 100644 fs/ceph/osd_client.h delete mode 100644 fs/ceph/osdmap.c delete mode 100644 fs/ceph/osdmap.h delete mode 100644 fs/ceph/pagelist.c delete mode 100644 fs/ceph/pagelist.h delete mode 100644 fs/ceph/rados.h create mode 100644 fs/ceph/strings.c delete mode 100644 fs/ceph/types.h (limited to 'fs') diff --git a/fs/ceph/Kconfig b/fs/ceph/Kconfig index 0fcd2640c23..9eb134ea6eb 100644 --- a/fs/ceph/Kconfig +++ b/fs/ceph/Kconfig @@ -1,9 +1,11 @@ config CEPH_FS tristate "Ceph distributed file system (EXPERIMENTAL)" depends on INET && EXPERIMENTAL + select CEPH_LIB select LIBCRC32C select CRYPTO_AES select CRYPTO + default n help Choose Y or M here to include support for mounting the experimental Ceph distributed file system. Ceph is an extremely @@ -14,15 +16,3 @@ config CEPH_FS If unsure, say N. -config CEPH_FS_PRETTYDEBUG - bool "Include file:line in ceph debug output" - depends on CEPH_FS - default n - help - If you say Y here, debug output will include a filename and - line to aid debugging. This icnreases kernel size and slows - execution slightly when debug call sites are enabled (e.g., - via CONFIG_DYNAMIC_DEBUG). - - If unsure, say N. - diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile index 278e1172600..9e6c4f2e8ff 100644 --- a/fs/ceph/Makefile +++ b/fs/ceph/Makefile @@ -8,15 +8,8 @@ obj-$(CONFIG_CEPH_FS) += ceph.o ceph-objs := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \ export.o caps.o snap.o xattr.o \ - messenger.o msgpool.o buffer.o pagelist.o \ - mds_client.o mdsmap.o \ - mon_client.o \ - osd_client.o osdmap.o crush/crush.o crush/mapper.o crush/hash.o \ - debugfs.o \ - auth.o auth_none.o \ - crypto.o armor.o \ - auth_x.o \ - ceph_fs.o ceph_strings.o ceph_hash.o ceph_frag.o + mds_client.o mdsmap.o strings.o ceph_frag.o \ + debugfs.o else #Otherwise we were called directly from the command diff --git a/fs/ceph/README b/fs/ceph/README deleted file mode 100644 index 18352fab37c..00000000000 --- a/fs/ceph/README +++ /dev/null @@ -1,20 +0,0 @@ -# -# The following files are shared by (and manually synchronized -# between) the Ceph userland and kernel client. -# -# userland kernel -src/include/ceph_fs.h fs/ceph/ceph_fs.h -src/include/ceph_fs.cc fs/ceph/ceph_fs.c -src/include/msgr.h fs/ceph/msgr.h -src/include/rados.h fs/ceph/rados.h -src/include/ceph_strings.cc fs/ceph/ceph_strings.c -src/include/ceph_frag.h fs/ceph/ceph_frag.h -src/include/ceph_frag.cc fs/ceph/ceph_frag.c -src/include/ceph_hash.h fs/ceph/ceph_hash.h -src/include/ceph_hash.cc fs/ceph/ceph_hash.c -src/crush/crush.c fs/ceph/crush/crush.c -src/crush/crush.h fs/ceph/crush/crush.h -src/crush/mapper.c fs/ceph/crush/mapper.c -src/crush/mapper.h fs/ceph/crush/mapper.h -src/crush/hash.h fs/ceph/crush/hash.h -src/crush/hash.c fs/ceph/crush/hash.c diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index efbc604001c..51bcc5ce323 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -1,4 +1,4 @@ -#include "ceph_debug.h" +#include #include #include @@ -10,7 +10,8 @@ #include #include "super.h" -#include "osd_client.h" +#include "mds_client.h" +#include /* * Ceph address space ops. @@ -193,7 +194,8 @@ static int readpage_nounlock(struct file *filp, struct page *page) { struct inode *inode = filp->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_osd_client *osdc = &ceph_inode_to_client(inode)->osdc; + struct ceph_osd_client *osdc = + &ceph_inode_to_client(inode)->client->osdc; int err = 0; u64 len = PAGE_CACHE_SIZE; @@ -265,7 +267,8 @@ static int ceph_readpages(struct file *file, struct address_space *mapping, { struct inode *inode = file->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_osd_client *osdc = &ceph_inode_to_client(inode)->osdc; + struct ceph_osd_client *osdc = + &ceph_inode_to_client(inode)->client->osdc; int rc = 0; struct page **pages; loff_t offset; @@ -365,7 +368,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) { struct inode *inode; struct ceph_inode_info *ci; - struct ceph_client *client; + struct ceph_fs_client *fsc; struct ceph_osd_client *osdc; loff_t page_off = page->index << PAGE_CACHE_SHIFT; int len = PAGE_CACHE_SIZE; @@ -383,8 +386,8 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) } inode = page->mapping->host; ci = ceph_inode(inode); - client = ceph_inode_to_client(inode); - osdc = &client->osdc; + fsc = ceph_inode_to_client(inode); + osdc = &fsc->client->osdc; /* verify this is a writeable snap context */ snapc = (void *)page->private; @@ -414,10 +417,10 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) dout("writepage %p page %p index %lu on %llu~%u snapc %p\n", inode, page, page->index, page_off, len, snapc); - writeback_stat = atomic_long_inc_return(&client->writeback_count); + writeback_stat = atomic_long_inc_return(&fsc->writeback_count); if (writeback_stat > - CONGESTION_ON_THRESH(client->mount_args->congestion_kb)) - set_bdi_congested(&client->backing_dev_info, BLK_RW_ASYNC); + CONGESTION_ON_THRESH(fsc->mount_options->congestion_kb)) + set_bdi_congested(&fsc->backing_dev_info, BLK_RW_ASYNC); set_page_writeback(page); err = ceph_osdc_writepages(osdc, ceph_vino(inode), @@ -496,7 +499,7 @@ static void writepages_finish(struct ceph_osd_request *req, struct address_space *mapping = inode->i_mapping; __s32 rc = -EIO; u64 bytes = 0; - struct ceph_client *client = ceph_inode_to_client(inode); + struct ceph_fs_client *fsc = ceph_inode_to_client(inode); long writeback_stat; unsigned issued = ceph_caps_issued(ci); @@ -529,10 +532,10 @@ static void writepages_finish(struct ceph_osd_request *req, WARN_ON(!PageUptodate(page)); writeback_stat = - atomic_long_dec_return(&client->writeback_count); + atomic_long_dec_return(&fsc->writeback_count); if (writeback_stat < - CONGESTION_OFF_THRESH(client->mount_args->congestion_kb)) - clear_bdi_congested(&client->backing_dev_info, + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + clear_bdi_congested(&fsc->backing_dev_info, BLK_RW_ASYNC); ceph_put_snap_context((void *)page->private); @@ -569,13 +572,13 @@ static void writepages_finish(struct ceph_osd_request *req, * mempool. we avoid the mempool if we can because req->r_num_pages * may be less than the maximum write size. */ -static void alloc_page_vec(struct ceph_client *client, +static void alloc_page_vec(struct ceph_fs_client *fsc, struct ceph_osd_request *req) { req->r_pages = kmalloc(sizeof(struct page *) * req->r_num_pages, GFP_NOFS); if (!req->r_pages) { - req->r_pages = mempool_alloc(client->wb_pagevec_pool, GFP_NOFS); + req->r_pages = mempool_alloc(fsc->wb_pagevec_pool, GFP_NOFS); req->r_pages_from_pool = 1; WARN_ON(!req->r_pages); } @@ -590,7 +593,7 @@ static int ceph_writepages_start(struct address_space *mapping, struct inode *inode = mapping->host; struct backing_dev_info *bdi = mapping->backing_dev_info; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_client *client; + struct ceph_fs_client *fsc; pgoff_t index, start, end; int range_whole = 0; int should_loop = 1; @@ -617,13 +620,13 @@ static int ceph_writepages_start(struct address_space *mapping, wbc->sync_mode == WB_SYNC_NONE ? "NONE" : (wbc->sync_mode == WB_SYNC_ALL ? "ALL" : "HOLD")); - client = ceph_inode_to_client(inode); - if (client->mount_state == CEPH_MOUNT_SHUTDOWN) { + fsc = ceph_inode_to_client(inode); + if (fsc->mount_state == CEPH_MOUNT_SHUTDOWN) { pr_warning("writepage_start %p on forced umount\n", inode); return -EIO; /* we're in a forced umount, don't write! */ } - if (client->mount_args->wsize && client->mount_args->wsize < wsize) - wsize = client->mount_args->wsize; + if (fsc->mount_options->wsize && fsc->mount_options->wsize < wsize) + wsize = fsc->mount_options->wsize; if (wsize < PAGE_CACHE_SIZE) wsize = PAGE_CACHE_SIZE; max_pages_ever = wsize >> PAGE_CACHE_SHIFT; @@ -769,7 +772,7 @@ get_more_pages: offset = (unsigned long long)page->index << PAGE_CACHE_SHIFT; len = wsize; - req = ceph_osdc_new_request(&client->osdc, + req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, ceph_vino(inode), offset, &len, @@ -782,7 +785,7 @@ get_more_pages: &inode->i_mtime, true, 1); max_pages = req->r_num_pages; - alloc_page_vec(client, req); + alloc_page_vec(fsc, req); req->r_callback = writepages_finish; req->r_inode = inode; } @@ -794,10 +797,10 @@ get_more_pages: inode, page, page->index); writeback_stat = - atomic_long_inc_return(&client->writeback_count); + atomic_long_inc_return(&fsc->writeback_count); if (writeback_stat > CONGESTION_ON_THRESH( - client->mount_args->congestion_kb)) { - set_bdi_congested(&client->backing_dev_info, + fsc->mount_options->congestion_kb)) { + set_bdi_congested(&fsc->backing_dev_info, BLK_RW_ASYNC); } @@ -846,7 +849,7 @@ get_more_pages: op->payload_len = cpu_to_le32(len); req->r_request->hdr.data_len = cpu_to_le32(len); - ceph_osdc_start_request(&client->osdc, req, true); + ceph_osdc_start_request(&fsc->client->osdc, req, true); req = NULL; /* continue? */ @@ -915,7 +918,7 @@ static int ceph_update_writeable_page(struct file *file, { struct inode *inode = file->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; loff_t page_off = pos & PAGE_CACHE_MASK; int pos_in_page = pos & ~PAGE_CACHE_MASK; int end_in_page = pos_in_page + len; @@ -1053,8 +1056,8 @@ static int ceph_write_end(struct file *file, struct address_space *mapping, struct page *page, void *fsdata) { struct inode *inode = file->f_dentry->d_inode; - struct ceph_client *client = ceph_inode_to_client(inode); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_inode_to_client(inode); + struct ceph_mds_client *mdsc = fsc->mdsc; unsigned from = pos & (PAGE_CACHE_SIZE - 1); int check_cap = 0; @@ -1123,7 +1126,7 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { struct inode *inode = vma->vm_file->f_dentry->d_inode; struct page *page = vmf->page; - struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; loff_t off = page->index << PAGE_CACHE_SHIFT; loff_t size, len; int ret; diff --git a/fs/ceph/armor.c b/fs/ceph/armor.c deleted file mode 100644 index eb2a666b0be..00000000000 --- a/fs/ceph/armor.c +++ /dev/null @@ -1,103 +0,0 @@ - -#include - -int ceph_armor(char *dst, const char *src, const char *end); -int ceph_unarmor(char *dst, const char *src, const char *end); - -/* - * base64 encode/decode. - */ - -static const char *pem_key = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -static int encode_bits(int c) -{ - return pem_key[c]; -} - -static int decode_bits(char c) -{ - if (c >= 'A' && c <= 'Z') - return c - 'A'; - if (c >= 'a' && c <= 'z') - return c - 'a' + 26; - if (c >= '0' && c <= '9') - return c - '0' + 52; - if (c == '+') - return 62; - if (c == '/') - return 63; - if (c == '=') - return 0; /* just non-negative, please */ - return -EINVAL; -} - -int ceph_armor(char *dst, const char *src, const char *end) -{ - int olen = 0; - int line = 0; - - while (src < end) { - unsigned char a, b, c; - - a = *src++; - *dst++ = encode_bits(a >> 2); - if (src < end) { - b = *src++; - *dst++ = encode_bits(((a & 3) << 4) | (b >> 4)); - if (src < end) { - c = *src++; - *dst++ = encode_bits(((b & 15) << 2) | - (c >> 6)); - *dst++ = encode_bits(c & 63); - } else { - *dst++ = encode_bits((b & 15) << 2); - *dst++ = '='; - } - } else { - *dst++ = encode_bits(((a & 3) << 4)); - *dst++ = '='; - *dst++ = '='; - } - olen += 4; - line += 4; - if (line == 64) { - line = 0; - *(dst++) = '\n'; - olen++; - } - } - return olen; -} - -int ceph_unarmor(char *dst, const char *src, const char *end) -{ - int olen = 0; - - while (src < end) { - int a, b, c, d; - - if (src < end && src[0] == '\n') - src++; - if (src + 4 > end) - return -EINVAL; - a = decode_bits(src[0]); - b = decode_bits(src[1]); - c = decode_bits(src[2]); - d = decode_bits(src[3]); - if (a < 0 || b < 0 || c < 0 || d < 0) - return -EINVAL; - - *dst++ = (a << 2) | (b >> 4); - if (src[2] == '=') - return olen + 1; - *dst++ = ((b & 15) << 4) | (c >> 2); - if (src[3] == '=') - return olen + 2; - *dst++ = ((c & 3) << 6) | d; - olen += 3; - src += 4; - } - return olen; -} diff --git a/fs/ceph/auth.c b/fs/ceph/auth.c deleted file mode 100644 index 6d2e3060062..00000000000 --- a/fs/ceph/auth.c +++ /dev/null @@ -1,259 +0,0 @@ -#include "ceph_debug.h" - -#include -#include -#include - -#include "types.h" -#include "auth_none.h" -#include "auth_x.h" -#include "decode.h" -#include "super.h" - -#include "messenger.h" - -/* - * get protocol handler - */ -static u32 supported_protocols[] = { - CEPH_AUTH_NONE, - CEPH_AUTH_CEPHX -}; - -static int ceph_auth_init_protocol(struct ceph_auth_client *ac, int protocol) -{ - switch (protocol) { - case CEPH_AUTH_NONE: - return ceph_auth_none_init(ac); - case CEPH_AUTH_CEPHX: - return ceph_x_init(ac); - default: - return -ENOENT; - } -} - -/* - * setup, teardown. - */ -struct ceph_auth_client *ceph_auth_init(const char *name, const char *secret) -{ - struct ceph_auth_client *ac; - int ret; - - dout("auth_init name '%s' secret '%s'\n", name, secret); - - ret = -ENOMEM; - ac = kzalloc(sizeof(*ac), GFP_NOFS); - if (!ac) - goto out; - - ac->negotiating = true; - if (name) - ac->name = name; - else - ac->name = CEPH_AUTH_NAME_DEFAULT; - dout("auth_init name %s secret %s\n", ac->name, secret); - ac->secret = secret; - return ac; - -out: - return ERR_PTR(ret); -} - -void ceph_auth_destroy(struct ceph_auth_client *ac) -{ - dout("auth_destroy %p\n", ac); - if (ac->ops) - ac->ops->destroy(ac); - kfree(ac); -} - -/* - * Reset occurs when reconnecting to the monitor. - */ -void ceph_auth_reset(struct ceph_auth_client *ac) -{ - dout("auth_reset %p\n", ac); - if (ac->ops && !ac->negotiating) - ac->ops->reset(ac); - ac->negotiating = true; -} - -int ceph_entity_name_encode(const char *name, void **p, void *end) -{ - int len = strlen(name); - - if (*p + 2*sizeof(u32) + len > end) - return -ERANGE; - ceph_encode_32(p, CEPH_ENTITY_TYPE_CLIENT); - ceph_encode_32(p, len); - ceph_encode_copy(p, name, len); - return 0; -} - -/* - * Initiate protocol negotiation with monitor. Include entity name - * and list supported protocols. - */ -int ceph_auth_build_hello(struct ceph_auth_client *ac, void *buf, size_t len) -{ - struct ceph_mon_request_header *monhdr = buf; - void *p = monhdr + 1, *end = buf + len, *lenp; - int i, num; - int ret; - - dout("auth_build_hello\n"); - monhdr->have_version = 0; - monhdr->session_mon = cpu_to_le16(-1); - monhdr->session_mon_tid = 0; - - ceph_encode_32(&p, 0); /* no protocol, yet */ - - lenp = p; - p += sizeof(u32); - - ceph_decode_need(&p, end, 1 + sizeof(u32), bad); - ceph_encode_8(&p, 1); - num = ARRAY_SIZE(supported_protocols); - ceph_encode_32(&p, num); - ceph_decode_need(&p, end, num * sizeof(u32), bad); - for (i = 0; i < num; i++) - ceph_encode_32(&p, supported_protocols[i]); - - ret = ceph_entity_name_encode(ac->name, &p, end); - if (ret < 0) - return ret; - ceph_decode_need(&p, end, sizeof(u64), bad); - ceph_encode_64(&p, ac->global_id); - - ceph_encode_32(&lenp, p - lenp - sizeof(u32)); - return p - buf; - -bad: - return -ERANGE; -} - -static int ceph_build_auth_request(struct ceph_auth_client *ac, - void *msg_buf, size_t msg_len) -{ - struct ceph_mon_request_header *monhdr = msg_buf; - void *p = monhdr + 1; - void *end = msg_buf + msg_len; - int ret; - - monhdr->have_version = 0; - monhdr->session_mon = cpu_to_le16(-1); - monhdr->session_mon_tid = 0; - - ceph_encode_32(&p, ac->protocol); - - ret = ac->ops->build_request(ac, p + sizeof(u32), end); - if (ret < 0) { - pr_err("error %d building auth method %s request\n", ret, - ac->ops->name); - return ret; - } - dout(" built request %d bytes\n", ret); - ceph_encode_32(&p, ret); - return p + ret - msg_buf; -} - -/* - * Handle auth message from monitor. - */ -int ceph_handle_auth_reply(struct ceph_auth_client *ac, - void *buf, size_t len, - void *reply_buf, size_t reply_len) -{ - void *p = buf; - void *end = buf + len; - int protocol; - s32 result; - u64 global_id; - void *payload, *payload_end; - int payload_len; - char *result_msg; - int result_msg_len; - int ret = -EINVAL; - - dout("handle_auth_reply %p %p\n", p, end); - ceph_decode_need(&p, end, sizeof(u32) * 3 + sizeof(u64), bad); - protocol = ceph_decode_32(&p); - result = ceph_decode_32(&p); - global_id = ceph_decode_64(&p); - payload_len = ceph_decode_32(&p); - payload = p; - p += payload_len; - ceph_decode_need(&p, end, sizeof(u32), bad); - result_msg_len = ceph_decode_32(&p); - result_msg = p; - p += result_msg_len; - if (p != end) - goto bad; - - dout(" result %d '%.*s' gid %llu len %d\n", result, result_msg_len, - result_msg, global_id, payload_len); - - payload_end = payload + payload_len; - - if (global_id && ac->global_id != global_id) { - dout(" set global_id %lld -> %lld\n", ac->global_id, global_id); - ac->global_id = global_id; - } - - if (ac->negotiating) { - /* server does not support our protocols? */ - if (!protocol && result < 0) { - ret = result; - goto out; - } - /* set up (new) protocol handler? */ - if (ac->protocol && ac->protocol != protocol) { - ac->ops->destroy(ac); - ac->protocol = 0; - ac->ops = NULL; - } - if (ac->protocol != protocol) { - ret = ceph_auth_init_protocol(ac, protocol); - if (ret) { - pr_err("error %d on auth protocol %d init\n", - ret, protocol); - goto out; - } - } - - ac->negotiating = false; - } - - ret = ac->ops->handle_reply(ac, result, payload, payload_end); - if (ret == -EAGAIN) { - return ceph_build_auth_request(ac, reply_buf, reply_len); - } else if (ret) { - pr_err("auth method '%s' error %d\n", ac->ops->name, ret); - return ret; - } - return 0; - -bad: - pr_err("failed to decode auth msg\n"); -out: - return ret; -} - -int ceph_build_auth(struct ceph_auth_client *ac, - void *msg_buf, size_t msg_len) -{ - if (!ac->protocol) - return ceph_auth_build_hello(ac, msg_buf, msg_len); - BUG_ON(!ac->ops); - if (ac->ops->should_authenticate(ac)) - return ceph_build_auth_request(ac, msg_buf, msg_len); - return 0; -} - -int ceph_auth_is_authenticated(struct ceph_auth_client *ac) -{ - if (!ac->ops) - return 0; - return ac->ops->is_authenticated(ac); -} diff --git a/fs/ceph/auth.h b/fs/ceph/auth.h deleted file mode 100644 index d38a2fb4a13..00000000000 --- a/fs/ceph/auth.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef _FS_CEPH_AUTH_H -#define _FS_CEPH_AUTH_H - -#include "types.h" -#include "buffer.h" - -/* - * Abstract interface for communicating with the authenticate module. - * There is some handshake that takes place between us and the monitor - * to acquire the necessary keys. These are used to generate an - * 'authorizer' that we use when connecting to a service (mds, osd). - */ - -struct ceph_auth_client; -struct ceph_authorizer; - -struct ceph_auth_client_ops { - const char *name; - - /* - * true if we are authenticated and can connect to - * services. - */ - int (*is_authenticated)(struct ceph_auth_client *ac); - - /* - * true if we should (re)authenticate, e.g., when our tickets - * are getting old and crusty. - */ - int (*should_authenticate)(struct ceph_auth_client *ac); - - /* - * build requests and process replies during monitor - * handshake. if handle_reply returns -EAGAIN, we build - * another request. - */ - int (*build_request)(struct ceph_auth_client *ac, void *buf, void *end); - int (*handle_reply)(struct ceph_auth_client *ac, int result, - void *buf, void *end); - - /* - * Create authorizer for connecting to a service, and verify - * the response to authenticate the service. - */ - int (*create_authorizer)(struct ceph_auth_client *ac, int peer_type, - struct ceph_authorizer **a, - void **buf, size_t *len, - void **reply_buf, size_t *reply_len); - int (*verify_authorizer_reply)(struct ceph_auth_client *ac, - struct ceph_authorizer *a, size_t len); - void (*destroy_authorizer)(struct ceph_auth_client *ac, - struct ceph_authorizer *a); - void (*invalidate_authorizer)(struct ceph_auth_client *ac, - int peer_type); - - /* reset when we (re)connect to a monitor */ - void (*reset)(struct ceph_auth_client *ac); - - void (*destroy)(struct ceph_auth_client *ac); -}; - -struct ceph_auth_client { - u32 protocol; /* CEPH_AUTH_* */ - void *private; /* for use by protocol implementation */ - const struct ceph_auth_client_ops *ops; /* null iff protocol==0 */ - - bool negotiating; /* true if negotiating protocol */ - const char *name; /* entity name */ - u64 global_id; /* our unique id in system */ - const char *secret; /* our secret key */ - unsigned want_keys; /* which services we want */ -}; - -extern struct ceph_auth_client *ceph_auth_init(const char *name, - const char *secret); -extern void ceph_auth_destroy(struct ceph_auth_client *ac); - -extern void ceph_auth_reset(struct ceph_auth_client *ac); - -extern int ceph_auth_build_hello(struct ceph_auth_client *ac, - void *buf, size_t len); -extern int ceph_handle_auth_reply(struct ceph_auth_client *ac, - void *buf, size_t len, - void *reply_buf, size_t reply_len); -extern int ceph_entity_name_encode(const char *name, void **p, void *end); - -extern int ceph_build_auth(struct ceph_auth_client *ac, - void *msg_buf, size_t msg_len); - -extern int ceph_auth_is_authenticated(struct ceph_auth_client *ac); - -#endif diff --git a/fs/ceph/auth_none.c b/fs/ceph/auth_none.c deleted file mode 100644 index ad1dc21286c..00000000000 --- a/fs/ceph/auth_none.c +++ /dev/null @@ -1,131 +0,0 @@ - -#include "ceph_debug.h" - -#include -#include -#include -#include - -#include "auth_none.h" -#include "auth.h" -#include "decode.h" - -static void reset(struct ceph_auth_client *ac) -{ - struct ceph_auth_none_info *xi = ac->private; - - xi->starting = true; - xi->built_authorizer = false; -} - -static void destroy(struct ceph_auth_client *ac) -{ - kfree(ac->private); - ac->private = NULL; -} - -static int is_authenticated(struct ceph_auth_client *ac) -{ - struct ceph_auth_none_info *xi = ac->private; - - return !xi->starting; -} - -static int should_authenticate(struct ceph_auth_client *ac) -{ - struct ceph_auth_none_info *xi = ac->private; - - return xi->starting; -} - -/* - * the generic auth code decode the global_id, and we carry no actual - * authenticate state, so nothing happens here. - */ -static int handle_reply(struct ceph_auth_client *ac, int result, - void *buf, void *end) -{ - struct ceph_auth_none_info *xi = ac->private; - - xi->starting = false; - return result; -} - -/* - * build an 'authorizer' with our entity_name and global_id. we can - * reuse a single static copy since it is identical for all services - * we connect to. - */ -static int ceph_auth_none_create_authorizer( - struct ceph_auth_client *ac, int peer_type, - struct ceph_authorizer **a, - void **buf, size_t *len, - void **reply_buf, size_t *reply_len) -{ - struct ceph_auth_none_info *ai = ac->private; - struct ceph_none_authorizer *au = &ai->au; - void *p, *end; - int ret; - - if (!ai->built_authorizer) { - p = au->buf; - end = p + sizeof(au->buf); - ceph_encode_8(&p, 1); - ret = ceph_entity_name_encode(ac->name, &p, end - 8); - if (ret < 0) - goto bad; - ceph_decode_need(&p, end, sizeof(u64), bad2); - ceph_encode_64(&p, ac->global_id); - au->buf_len = p - (void *)au->buf; - ai->built_authorizer = true; - dout("built authorizer len %d\n", au->buf_len); - } - - *a = (struct ceph_authorizer *)au; - *buf = au->buf; - *len = au->buf_len; - *reply_buf = au->reply_buf; - *reply_len = sizeof(au->reply_buf); - return 0; - -bad2: - ret = -ERANGE; -bad: - return ret; -} - -static void ceph_auth_none_destroy_authorizer(struct ceph_auth_client *ac, - struct ceph_authorizer *a) -{ - /* nothing to do */ -} - -static const struct ceph_auth_client_ops ceph_auth_none_ops = { - .name = "none", - .reset = reset, - .destroy = destroy, - .is_authenticated = is_authenticated, - .should_authenticate = should_authenticate, - .handle_reply = handle_reply, - .create_authorizer = ceph_auth_none_create_authorizer, - .destroy_authorizer = ceph_auth_none_destroy_authorizer, -}; - -int ceph_auth_none_init(struct ceph_auth_client *ac) -{ - struct ceph_auth_none_info *xi; - - dout("ceph_auth_none_init %p\n", ac); - xi = kzalloc(sizeof(*xi), GFP_NOFS); - if (!xi) - return -ENOMEM; - - xi->starting = true; - xi->built_authorizer = false; - - ac->protocol = CEPH_AUTH_NONE; - ac->private = xi; - ac->ops = &ceph_auth_none_ops; - return 0; -} - diff --git a/fs/ceph/auth_none.h b/fs/ceph/auth_none.h deleted file mode 100644 index 8164df1a08b..00000000000 --- a/fs/ceph/auth_none.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _FS_CEPH_AUTH_NONE_H -#define _FS_CEPH_AUTH_NONE_H - -#include - -#include "auth.h" - -/* - * null security mode. - * - * we use a single static authorizer that simply encodes our entity name - * and global id. - */ - -struct ceph_none_authorizer { - char buf[128]; - int buf_len; - char reply_buf[0]; -}; - -struct ceph_auth_none_info { - bool starting; - bool built_authorizer; - struct ceph_none_authorizer au; /* we only need one; it's static */ -}; - -extern int ceph_auth_none_init(struct ceph_auth_client *ac); - -#endif - diff --git a/fs/ceph/auth_x.c b/fs/ceph/auth_x.c deleted file mode 100644 index a2d002cbdec..00000000000 --- a/fs/ceph/auth_x.c +++ /dev/null @@ -1,687 +0,0 @@ - -#include "ceph_debug.h" - -#include -#include -#include -#include - -#include "auth_x.h" -#include "auth_x_protocol.h" -#include "crypto.h" -#include "auth.h" -#include "decode.h" - -#define TEMP_TICKET_BUF_LEN 256 - -static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed); - -static int ceph_x_is_authenticated(struct ceph_auth_client *ac) -{ - struct ceph_x_info *xi = ac->private; - int need; - - ceph_x_validate_tickets(ac, &need); - dout("ceph_x_is_authenticated want=%d need=%d have=%d\n", - ac->want_keys, need, xi->have_keys); - return (ac->want_keys & xi->have_keys) == ac->want_keys; -} - -static int ceph_x_should_authenticate(struct ceph_auth_client *ac) -{ - struct ceph_x_info *xi = ac->private; - int need; - - ceph_x_validate_tickets(ac, &need); - dout("ceph_x_should_authenticate want=%d need=%d have=%d\n", - ac->want_keys, need, xi->have_keys); - return need != 0; -} - -static int ceph_x_encrypt_buflen(int ilen) -{ - return sizeof(struct ceph_x_encrypt_header) + ilen + 16 + - sizeof(u32); -} - -static int ceph_x_encrypt(struct ceph_crypto_key *secret, - void *ibuf, int ilen, void *obuf, size_t olen) -{ - struct ceph_x_encrypt_header head = { - .struct_v = 1, - .magic = cpu_to_le64(CEPHX_ENC_MAGIC) - }; - size_t len = olen - sizeof(u32); - int ret; - - ret = ceph_encrypt2(secret, obuf + sizeof(u32), &len, - &head, sizeof(head), ibuf, ilen); - if (ret) - return ret; - ceph_encode_32(&obuf, len); - return len + sizeof(u32); -} - -static int ceph_x_decrypt(struct ceph_crypto_key *secret, - void **p, void *end, void *obuf, size_t olen) -{ - struct ceph_x_encrypt_header head; - size_t head_len = sizeof(head); - int len, ret; - - len = ceph_decode_32(p); - if (*p + len > end) - return -EINVAL; - - dout("ceph_x_decrypt len %d\n", len); - ret = ceph_decrypt2(secret, &head, &head_len, obuf, &olen, - *p, len); - if (ret) - return ret; - if (head.struct_v != 1 || le64_to_cpu(head.magic) != CEPHX_ENC_MAGIC) - return -EPERM; - *p += len; - return olen; -} - -/* - * get existing (or insert new) ticket handler - */ -static struct ceph_x_ticket_handler * -get_ticket_handler(struct ceph_auth_client *ac, int service) -{ - struct ceph_x_ticket_handler *th; - struct ceph_x_info *xi = ac->private; - struct rb_node *parent = NULL, **p = &xi->ticket_handlers.rb_node; - - while (*p) { - parent = *p; - th = rb_entry(parent, struct ceph_x_ticket_handler, node); - if (service < th->service) - p = &(*p)->rb_left; - else if (service > th->service) - p = &(*p)->rb_right; - else - return th; - } - - /* add it */ - th = kzalloc(sizeof(*th), GFP_NOFS); - if (!th) - return ERR_PTR(-ENOMEM); - th->service = service; - rb_link_node(&th->node, parent, p); - rb_insert_color(&th->node, &xi->ticket_handlers); - return th; -} - -static void remove_ticket_handler(struct ceph_auth_client *ac, - struct ceph_x_ticket_handler *th) -{ - struct ceph_x_info *xi = ac->private; - - dout("remove_ticket_handler %p %d\n", th, th->service); - rb_erase(&th->node, &xi->ticket_handlers); - ceph_crypto_key_destroy(&th->session_key); - if (th->ticket_blob) - ceph_buffer_put(th->ticket_blob); - kfree(th); -} - -static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac, - struct ceph_crypto_key *secret, - void *buf, void *end) -{ - struct ceph_x_info *xi = ac->private; - int num; - void *p = buf; - int ret; - char *dbuf; - char *ticket_buf; - u8 reply_struct_v; - - dbuf = kmalloc(TEMP_TICKET_BUF_LEN, GFP_NOFS); - if (!dbuf) - return -ENOMEM; - - ret = -ENOMEM; - ticket_buf = kmalloc(TEMP_TICKET_BUF_LEN, GFP_NOFS); - if (!ticket_buf) - goto out_dbuf; - - ceph_decode_need(&p, end, 1 + sizeof(u32), bad); - reply_struct_v = ceph_decode_8(&p); - if (reply_struct_v != 1) - goto bad; - num = ceph_decode_32(&p); - dout("%d tickets\n", num); - while (num--) { - int type; - u8 tkt_struct_v, blob_struct_v; - struct ceph_x_ticket_handler *th; - void *dp, *dend; - int dlen; - char is_enc; - struct timespec validity; - struct ceph_crypto_key old_key; - void *tp, *tpend; - struct ceph_timespec new_validity; - struct ceph_crypto_key new_session_key; - struct ceph_buffer *new_ticket_blob; - unsigned long new_expires, new_renew_after; - u64 new_secret_id; - - ceph_decode_need(&p, end, sizeof(u32) + 1, bad); - - type = ceph_decode_32(&p); - dout(" ticket type %d %s\n", type, ceph_entity_type_name(type)); - - tkt_struct_v = ceph_decode_8(&p); - if (tkt_struct_v != 1) - goto bad; - - th = get_ticket_handler(ac, type); - if (IS_ERR(th)) { - ret = PTR_ERR(th); - goto out; - } - - /* blob for me */ - dlen = ceph_x_decrypt(secret, &p, end, dbuf, - TEMP_TICKET_BUF_LEN); - if (dlen <= 0) { - ret = dlen; - goto out; - } - dout(" decrypted %d bytes\n", dlen); - dend = dbuf + dlen; - dp = dbuf; - - tkt_struct_v = ceph_decode_8(&dp); - if (tkt_struct_v != 1) - goto bad; - - memcpy(&old_key, &th->session_key, sizeof(old_key)); - ret = ceph_crypto_key_decode(&new_session_key, &dp, dend); - if (ret) - goto out; - - ceph_decode_copy(&dp, &new_validity, sizeof(new_validity)); - ceph_decode_timespec(&validity, &new_validity); - new_expires = get_seconds() + validity.tv_sec; - new_renew_after = new_expires - (validity.tv_sec / 4); - dout(" expires=%lu renew_after=%lu\n", new_expires, - new_renew_after); - - /* ticket blob for service */ - ceph_decode_8_safe(&p, end, is_enc, bad); - tp = ticket_buf; - if (is_enc) { - /* encrypted */ - dout(" encrypted ticket\n"); - dlen = ceph_x_decrypt(&old_key, &p, end, ticket_buf, - TEMP_TICKET_BUF_LEN); - if (dlen < 0) { - ret = dlen; - goto out; - } - dlen = ceph_decode_32(&tp); - } else { - /* unencrypted */ - ceph_decode_32_safe(&p, end, dlen, bad); - ceph_decode_need(&p, end, dlen, bad); - ceph_decode_copy(&p, ticket_buf, dlen); - } - tpend = tp + dlen; - dout(" ticket blob is %d bytes\n", dlen); - ceph_decode_need(&tp, tpend, 1 + sizeof(u64), bad); - blob_struct_v = ceph_decode_8(&tp); - new_secret_id = ceph_decode_64(&tp); - ret = ceph_decode_buffer(&new_ticket_blob, &tp, tpend); - if (ret) - goto out; - - /* all is well, update our ticket */ - ceph_crypto_key_destroy(&th->session_key); - if (th->ticket_blob) - ceph_buffer_put(th->ticket_blob); - th->session_key = new_session_key; - th->ticket_blob = new_ticket_blob; - th->validity = new_validity; - th->secret_id = new_secret_id; - th->expires = new_expires; - th->renew_after = new_renew_after; - dout(" got ticket service %d (%s) secret_id %lld len %d\n", - type, ceph_entity_type_name(type), th->secret_id, - (int)th->ticket_blob->vec.iov_len); - xi->have_keys |= th->service; - } - - ret = 0; -out: - kfree(ticket_buf); -out_dbuf: - kfree(dbuf); - return ret; - -bad: - ret = -EINVAL; - goto out; -} - -static int ceph_x_build_authorizer(struct ceph_auth_client *ac, - struct ceph_x_ticket_handler *th, - struct ceph_x_authorizer *au) -{ - int maxlen; - struct ceph_x_authorize_a *msg_a; - struct ceph_x_authorize_b msg_b; - void *p, *end; - int ret; - int ticket_blob_len = - (th->ticket_blob ? th->ticket_blob->vec.iov_len : 0); - - dout("build_authorizer for %s %p\n", - ceph_entity_type_name(th->service), au); - - maxlen = sizeof(*msg_a) + sizeof(msg_b) + - ceph_x_encrypt_buflen(ticket_blob_len); - dout(" need len %d\n", maxlen); - if (au->buf && au->buf->alloc_len < maxlen) { - ceph_buffer_put(au->buf); - au->buf = NULL; - } - if (!au->buf) { - au->buf = ceph_buffer_new(maxlen, GFP_NOFS); - if (!au->buf) - return -ENOMEM; - } - au->service = th->service; - - msg_a = au->buf->vec.iov_base; - msg_a->struct_v = 1; - msg_a->global_id = cpu_to_le64(ac->global_id); - msg_a->service_id = cpu_to_le32(th->service); - msg_a->ticket_blob.struct_v = 1; - msg_a->ticket_blob.secret_id = cpu_to_le64(th->secret_id); - msg_a->ticket_blob.blob_len = cpu_to_le32(ticket_blob_len); - if (ticket_blob_len) { - memcpy(msg_a->ticket_blob.blob, th->ticket_blob->vec.iov_base, - th->ticket_blob->vec.iov_len); - } - dout(" th %p secret_id %lld %lld\n", th, th->secret_id, - le64_to_cpu(msg_a->ticket_blob.secret_id)); - - p = msg_a + 1; - p += ticket_blob_len; - end = au->buf->vec.iov_base + au->buf->vec.iov_len; - - get_random_bytes(&au->nonce, sizeof(au->nonce)); - msg_b.struct_v = 1; - msg_b.nonce = cpu_to_le64(au->nonce); - ret = ceph_x_encrypt(&th->session_key, &msg_b, sizeof(msg_b), - p, end - p); - if (ret < 0) - goto out_buf; - p += ret; - au->buf->vec.iov_len = p - au->buf->vec.iov_base; - dout(" built authorizer nonce %llx len %d\n", au->nonce, - (int)au->buf->vec.iov_len); - BUG_ON(au->buf->vec.iov_len > maxlen); - return 0; - -out_buf: - ceph_buffer_put(au->buf); - au->buf = NULL; - return ret; -} - -static int ceph_x_encode_ticket(struct ceph_x_ticket_handler *th, - void **p, void *end) -{ - ceph_decode_need(p, end, 1 + sizeof(u64), bad); - ceph_encode_8(p, 1); - ceph_encode_64(p, th->secret_id); - if (th->ticket_blob) { - const char *buf = th->ticket_blob->vec.iov_base; - u32 len = th->ticket_blob->vec.iov_len; - - ceph_encode_32_safe(p, end, len, bad); - ceph_encode_copy_safe(p, end, buf, len, bad); - } else { - ceph_encode_32_safe(p, end, 0, bad); - } - - return 0; -bad: - return -ERANGE; -} - -static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed) -{ - int want = ac->want_keys; - struct ceph_x_info *xi = ac->private; - int service; - - *pneed = ac->want_keys & ~(xi->have_keys); - - for (service = 1; service <= want; service <<= 1) { - struct ceph_x_ticket_handler *th; - - if (!(ac->want_keys & service)) - continue; - - if (*pneed & service) - continue; - - th = get_ticket_handler(ac, service); - - if (IS_ERR(th)) { - *pneed |= service; - continue; - } - - if (get_seconds() >= th->renew_after) - *pneed |= service; - if (get_seconds() >= th->expires) - xi->have_keys &= ~service; - } -} - - -static int ceph_x_build_request(struct ceph_auth_client *ac, - void *buf, void *end) -{ - struct ceph_x_info *xi = ac->private; - int need; - struct ceph_x_request_header *head = buf; - int ret; - struct ceph_x_ticket_handler *th = - get_ticket_handler(ac, CEPH_ENTITY_TYPE_AUTH); - - if (IS_ERR(th)) - return PTR_ERR(th); - - ceph_x_validate_tickets(ac, &need); - - dout("build_request want %x have %x need %x\n", - ac->want_keys, xi->have_keys, need); - - if (need & CEPH_ENTITY_TYPE_AUTH) { - struct ceph_x_authenticate *auth = (void *)(head + 1); - void *p = auth + 1; - struct ceph_x_challenge_blob tmp; - char tmp_enc[40]; - u64 *u; - - if (p > end) - return -ERANGE; - - dout(" get_auth_session_key\n"); - head->op = cpu_to_le16(CEPHX_GET_AUTH_SESSION_KEY); - - /* encrypt and hash */ - get_random_bytes(&auth->client_challenge, sizeof(u64)); - tmp.client_challenge = auth->client_challenge; - tmp.server_challenge = cpu_to_le64(xi->server_challenge); - ret = ceph_x_encrypt(&xi->secret, &tmp, sizeof(tmp), - tmp_enc, sizeof(tmp_enc)); - if (ret < 0) - return ret; - - auth->struct_v = 1; - auth->key = 0; - for (u = (u64 *)tmp_enc; u + 1 <= (u64 *)(tmp_enc + ret); u++) - auth->key ^= *(__le64 *)u; - dout(" server_challenge %llx client_challenge %llx key %llx\n", - xi->server_challenge, le64_to_cpu(auth->client_challenge), - le64_to_cpu(auth->key)); - - /* now encode the old ticket if exists */ - ret = ceph_x_encode_ticket(th, &p, end); - if (ret < 0) - return ret; - - return p - buf; - } - - if (need) { - void *p = head + 1; - struct ceph_x_service_ticket_request *req; - - if (p > end) - return -ERANGE; - head->op = cpu_to_le16(CEPHX_GET_PRINCIPAL_SESSION_KEY); - - ret = ceph_x_build_authorizer(ac, th, &xi->auth_authorizer); - if (ret) - return ret; - ceph_encode_copy(&p, xi->auth_authorizer.buf->vec.iov_base, - xi->auth_authorizer.buf->vec.iov_len); - - req = p; - req->keys = cpu_to_le32(need); - p += sizeof(*req); - return p - buf; - } - - return 0; -} - -static int ceph_x_handle_reply(struct ceph_auth_client *ac, int result, - void *buf, void *end) -{ - struct ceph_x_info *xi = ac->private; - struct ceph_x_reply_header *head = buf; - struct ceph_x_ticket_handler *th; - int len = end - buf; - int op; - int ret; - - if (result) - return result; /* XXX hmm? */ - - if (xi->starting) { - /* it's a hello */ - struct ceph_x_server_challenge *sc = buf; - - if (len != sizeof(*sc)) - return -EINVAL; - xi->server_challenge = le64_to_cpu(sc->server_challenge); - dout("handle_reply got server challenge %llx\n", - xi->server_challenge); - xi->starting = false; - xi->have_keys &= ~CEPH_ENTITY_TYPE_AUTH; - return -EAGAIN; - } - - op = le16_to_cpu(head->op); - result = le32_to_cpu(head->result); - dout("handle_reply op %d result %d\n", op, result); - switch (op) { - case CEPHX_GET_AUTH_SESSION_KEY: - /* verify auth key */ - ret = ceph_x_proc_ticket_reply(ac, &xi->secret, - buf + sizeof(*head), end); - break; - - case CEPHX_GET_PRINCIPAL_SESSION_KEY: - th = get_ticket_handler(ac, CEPH_ENTITY_TYPE_AUTH); - if (IS_ERR(th)) - return PTR_ERR(th); - ret = ceph_x_proc_ticket_reply(ac, &th->session_key, - buf + sizeof(*head), end); - break; - - default: - return -EINVAL; - } - if (ret) - return ret; - if (ac->want_keys == xi->have_keys) - return 0; - return -EAGAIN; -} - -static int ceph_x_create_authorizer( - struct ceph_auth_client *ac, int peer_type, - struct ceph_authorizer **a, - void **buf, size_t *len, - void **reply_buf, size_t *reply_len) -{ - struct ceph_x_authorizer *au; - struct ceph_x_ticket_handler *th; - int ret; - - th = get_ticket_handler(ac, peer_type); - if (IS_ERR(th)) - return PTR_ERR(th); - - au = kzalloc(sizeof(*au), GFP_NOFS); - if (!au) - return -ENOMEM; - - ret = ceph_x_build_authorizer(ac, th, au); - if (ret) { - kfree(au); - return ret; - } - - *a = (struct ceph_authorizer *)au; - *buf = au->buf->vec.iov_base; - *len = au->buf->vec.iov_len; - *reply_buf = au->reply_buf; - *reply_len = sizeof(au->reply_buf); - return 0; -} - -static int ceph_x_verify_authorizer_reply(struct ceph_auth_client *ac, - struct ceph_authorizer *a, size_t len) -{ - struct ceph_x_authorizer *au = (void *)a; - struct ceph_x_ticket_handler *th; - int ret = 0; - struct ceph_x_authorize_reply reply; - void *p = au->reply_buf; - void *end = p + sizeof(au->reply_buf); - - th = get_ticket_handler(ac, au->service); - if (IS_ERR(th)) - return PTR_ERR(th); - ret = ceph_x_decrypt(&th->session_key, &p, end, &reply, sizeof(reply)); - if (ret < 0) - return ret; - if (ret != sizeof(reply)) - return -EPERM; - - if (au->nonce + 1 != le64_to_cpu(reply.nonce_plus_one)) - ret = -EPERM; - else - ret = 0; - dout("verify_authorizer_reply nonce %llx got %llx ret %d\n", - au->nonce, le64_to_cpu(reply.nonce_plus_one), ret); - return ret; -} - -static void ceph_x_destroy_authorizer(struct ceph_auth_client *ac, - struct ceph_authorizer *a) -{ - struct ceph_x_authorizer *au = (void *)a; - - ceph_buffer_put(au->buf); - kfree(au); -} - - -static void ceph_x_reset(struct ceph_auth_client *ac) -{ - struct ceph_x_info *xi = ac->private; - - dout("reset\n"); - xi->starting = true; - xi->server_challenge = 0; -} - -static void ceph_x_destroy(struct ceph_auth_client *ac) -{ - struct ceph_x_info *xi = ac->private; - struct rb_node *p; - - dout("ceph_x_destroy %p\n", ac); - ceph_crypto_key_destroy(&xi->secret); - - while ((p = rb_first(&xi->ticket_handlers)) != NULL) { - struct ceph_x_ticket_handler *th = - rb_entry(p, struct ceph_x_ticket_handler, node); - remove_ticket_handler(ac, th); - } - - if (xi->auth_authorizer.buf) - ceph_buffer_put(xi->auth_authorizer.buf); - - kfree(ac->private); - ac->private = NULL; -} - -static void ceph_x_invalidate_authorizer(struct ceph_auth_client *ac, - int peer_type) -{ - struct ceph_x_ticket_handler *th; - - th = get_ticket_handler(ac, peer_type); - if (!IS_ERR(th)) - remove_ticket_handler(ac, th); -} - - -static const struct ceph_auth_client_ops ceph_x_ops = { - .name = "x", - .is_authenticated = ceph_x_is_authenticated, - .should_authenticate = ceph_x_should_authenticate, - .build_request = ceph_x_build_request, - .handle_reply = ceph_x_handle_reply, - .create_authorizer = ceph_x_create_authorizer, - .verify_authorizer_reply = ceph_x_verify_authorizer_reply, - .destroy_authorizer = ceph_x_destroy_authorizer, - .invalidate_authorizer = ceph_x_invalidate_authorizer, - .reset = ceph_x_reset, - .destroy = ceph_x_destroy, -}; - - -int ceph_x_init(struct ceph_auth_client *ac) -{ - struct ceph_x_info *xi; - int ret; - - dout("ceph_x_init %p\n", ac); - ret = -ENOMEM; - xi = kzalloc(sizeof(*xi), GFP_NOFS); - if (!xi) - goto out; - - ret = -EINVAL; - if (!ac->secret) { - pr_err("no secret set (for auth_x protocol)\n"); - goto out_nomem; - } - - ret = ceph_crypto_key_unarmor(&xi->secret, ac->secret); - if (ret) - goto out_nomem; - - xi->starting = true; - xi->ticket_handlers = RB_ROOT; - - ac->protocol = CEPH_AUTH_CEPHX; - ac->private = xi; - ac->ops = &ceph_x_ops; - return 0; - -out_nomem: - kfree(xi); -out: - return ret; -} - - diff --git a/fs/ceph/auth_x.h b/fs/ceph/auth_x.h deleted file mode 100644 index ff6f8180e68..00000000000 --- a/fs/ceph/auth_x.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _FS_CEPH_AUTH_X_H -#define _FS_CEPH_AUTH_X_H - -#include - -#include "crypto.h" -#include "auth.h" -#include "auth_x_protocol.h" - -/* - * Handle ticket for a single service. - */ -struct ceph_x_ticket_handler { - struct rb_node node; - unsigned service; - - struct ceph_crypto_key session_key; - struct ceph_timespec validity; - - u64 secret_id; - struct ceph_buffer *ticket_blob; - - unsigned long renew_after, expires; -}; - - -struct ceph_x_authorizer { - struct ceph_buffer *buf; - unsigned service; - u64 nonce; - char reply_buf[128]; /* big enough for encrypted blob */ -}; - -struct ceph_x_info { - struct ceph_crypto_key secret; - - bool starting; - u64 server_challenge; - - unsigned have_keys; - struct rb_root ticket_handlers; - - struct ceph_x_authorizer auth_authorizer; -}; - -extern int ceph_x_init(struct ceph_auth_client *ac); - -#endif - diff --git a/fs/ceph/auth_x_protocol.h b/fs/ceph/auth_x_protocol.h deleted file mode 100644 index 671d30576c4..00000000000 --- a/fs/ceph/auth_x_protocol.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef __FS_CEPH_AUTH_X_PROTOCOL -#define __FS_CEPH_AUTH_X_PROTOCOL - -#define CEPHX_GET_AUTH_SESSION_KEY 0x0100 -#define CEPHX_GET_PRINCIPAL_SESSION_KEY 0x0200 -#define CEPHX_GET_ROTATING_KEY 0x0400 - -/* common bits */ -struct ceph_x_ticket_blob { - __u8 struct_v; - __le64 secret_id; - __le32 blob_len; - char blob[]; -} __attribute__ ((packed)); - - -/* common request/reply headers */ -struct ceph_x_request_header { - __le16 op; -} __attribute__ ((packed)); - -struct ceph_x_reply_header { - __le16 op; - __le32 result; -} __attribute__ ((packed)); - - -/* authenticate handshake */ - -/* initial hello (no reply header) */ -struct ceph_x_server_challenge { - __u8 struct_v; - __le64 server_challenge; -} __attribute__ ((packed)); - -struct ceph_x_authenticate { - __u8 struct_v; - __le64 client_challenge; - __le64 key; - /* ticket blob */ -} __attribute__ ((packed)); - -struct ceph_x_service_ticket_request { - __u8 struct_v; - __le32 keys; -} __attribute__ ((packed)); - -struct ceph_x_challenge_blob { - __le64 server_challenge; - __le64 client_challenge; -} __attribute__ ((packed)); - - - -/* authorize handshake */ - -/* - * The authorizer consists of two pieces: - * a - service id, ticket blob - * b - encrypted with session key - */ -struct ceph_x_authorize_a { - __u8 struct_v; - __le64 global_id; - __le32 service_id; - struct ceph_x_ticket_blob ticket_blob; -} __attribute__ ((packed)); - -struct ceph_x_authorize_b { - __u8 struct_v; - __le64 nonce; -} __attribute__ ((packed)); - -struct ceph_x_authorize_reply { - __u8 struct_v; - __le64 nonce_plus_one; -} __attribute__ ((packed)); - - -/* - * encyption bundle - */ -#define CEPHX_ENC_MAGIC 0xff009cad8826aa55ull - -struct ceph_x_encrypt_header { - __u8 struct_v; - __le64 magic; -} __attribute__ ((packed)); - -#endif diff --git a/fs/ceph/buffer.c b/fs/ceph/buffer.c deleted file mode 100644 index cd39f17021d..00000000000 --- a/fs/ceph/buffer.c +++ /dev/null @@ -1,65 +0,0 @@ - -#include "ceph_debug.h" - -#include - -#include "buffer.h" -#include "decode.h" - -struct ceph_buffer *ceph_buffer_new(size_t len, gfp_t gfp) -{ - struct ceph_buffer *b; - - b = kmalloc(sizeof(*b), gfp); - if (!b) - return NULL; - - b->vec.iov_base = kmalloc(len, gfp | __GFP_NOWARN); - if (b->vec.iov_base) { - b->is_vmalloc = false; - } else { - b->vec.iov_base = __vmalloc(len, gfp, PAGE_KERNEL); - if (!b->vec.iov_base) { - kfree(b); - return NULL; - } - b->is_vmalloc = true; - } - - kref_init(&b->kref); - b->alloc_len = len; - b->vec.iov_len = len; - dout("buffer_new %p\n", b); - return b; -} - -void ceph_buffer_release(struct kref *kref) -{ - struct ceph_buffer *b = container_of(kref, struct ceph_buffer, kref); - - dout("buffer_release %p\n", b); - if (b->vec.iov_base) { - if (b->is_vmalloc) - vfree(b->vec.iov_base); - else - kfree(b->vec.iov_base); - } - kfree(b); -} - -int ceph_decode_buffer(struct ceph_buffer **b, void **p, void *end) -{ - size_t len; - - ceph_decode_need(p, end, sizeof(u32), bad); - len = ceph_decode_32(p); - dout("decode_buffer len %d\n", (int)len); - ceph_decode_need(p, end, len, bad); - *b = ceph_buffer_new(len, GFP_NOFS); - if (!*b) - return -ENOMEM; - ceph_decode_copy(p, (*b)->vec.iov_base, len); - return 0; -bad: - return -EINVAL; -} diff --git a/fs/ceph/buffer.h b/fs/ceph/buffer.h deleted file mode 100644 index 58d19014068..00000000000 --- a/fs/ceph/buffer.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef __FS_CEPH_BUFFER_H -#define __FS_CEPH_BUFFER_H - -#include -#include -#include -#include -#include - -/* - * a simple reference counted buffer. - * - * use kmalloc for small sizes (<= one page), vmalloc for larger - * sizes. - */ -struct ceph_buffer { - struct kref kref; - struct kvec vec; - size_t alloc_len; - bool is_vmalloc; -}; - -extern struct ceph_buffer *ceph_buffer_new(size_t len, gfp_t gfp); -extern void ceph_buffer_release(struct kref *kref); - -static inline struct ceph_buffer *ceph_buffer_get(struct ceph_buffer *b) -{ - kref_get(&b->kref); - return b; -} - -static inline void ceph_buffer_put(struct ceph_buffer *b) -{ - kref_put(&b->kref, ceph_buffer_release); -} - -extern int ceph_decode_buffer(struct ceph_buffer **b, void **p, void *end); - -#endif diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 5e9da996a15..3cff67cbb9c 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1,4 +1,4 @@ -#include "ceph_debug.h" +#include #include #include @@ -9,8 +9,9 @@ #include #include "super.h" -#include "decode.h" -#include "messenger.h" +#include "mds_client.h" +#include +#include /* * Capability management @@ -287,11 +288,11 @@ void ceph_put_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap) spin_unlock(&mdsc->caps_list_lock); } -void ceph_reservation_status(struct ceph_client *client, +void ceph_reservation_status(struct ceph_fs_client *fsc, int *total, int *avail, int *used, int *reserved, int *min) { - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_client *mdsc = fsc->mdsc; if (total) *total = mdsc->caps_total_count; @@ -399,7 +400,7 @@ static void __insert_cap_node(struct ceph_inode_info *ci, static void __cap_set_timeouts(struct ceph_mds_client *mdsc, struct ceph_inode_info *ci) { - struct ceph_mount_args *ma = mdsc->client->mount_args; + struct ceph_mount_options *ma = mdsc->fsc->mount_options; ci->i_hold_caps_min = round_jiffies(jiffies + ma->caps_wanted_delay_min * HZ); @@ -515,7 +516,7 @@ int ceph_add_cap(struct inode *inode, unsigned seq, unsigned mseq, u64 realmino, int flags, struct ceph_cap_reservation *caps_reservation) { - struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_cap *new_cap = NULL; struct ceph_cap *cap; @@ -873,7 +874,7 @@ void __ceph_remove_cap(struct ceph_cap *cap) struct ceph_mds_session *session = cap->session; struct ceph_inode_info *ci = cap->ci; struct ceph_mds_client *mdsc = - &ceph_sb_to_client(ci->vfs_inode.i_sb)->mdsc; + ceph_sb_to_client(ci->vfs_inode.i_sb)->mdsc; int removed = 0; dout("__ceph_remove_cap %p from %p\n", cap, &ci->vfs_inode); @@ -1210,7 +1211,7 @@ void __ceph_flush_snaps(struct ceph_inode_info *ci, int mds; struct ceph_cap_snap *capsnap; u32 mseq; - struct ceph_mds_client *mdsc = &ceph_inode_to_client(inode)->mdsc; + struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; struct ceph_mds_session *session = NULL; /* if session != NULL, we hold session->s_mutex */ u64 next_follows = 0; /* keep track of how far we've gotten through the @@ -1336,7 +1337,7 @@ static void ceph_flush_snaps(struct ceph_inode_info *ci) void __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) { struct ceph_mds_client *mdsc = - &ceph_sb_to_client(ci->vfs_inode.i_sb)->mdsc; + ceph_sb_to_client(ci->vfs_inode.i_sb)->mdsc; struct inode *inode = &ci->vfs_inode; int was = ci->i_dirty_caps; int dirty = 0; @@ -1378,7 +1379,7 @@ void __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) static int __mark_caps_flushing(struct inode *inode, struct ceph_mds_session *session) { - struct ceph_mds_client *mdsc = &ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; struct ceph_inode_info *ci = ceph_inode(inode); int flushing; @@ -1462,8 +1463,8 @@ static int try_nonblocking_invalidate(struct inode *inode) void ceph_check_caps(struct ceph_inode_info *ci, int flags, struct ceph_mds_session *session) { - struct ceph_client *client = ceph_inode_to_client(&ci->vfs_inode); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_inode_to_client(&ci->vfs_inode); + struct ceph_mds_client *mdsc = fsc->mdsc; struct inode *inode = &ci->vfs_inode; struct ceph_cap *cap; int file_wanted, used; @@ -1706,7 +1707,7 @@ ack: static int try_flush_caps(struct inode *inode, struct ceph_mds_session *session, unsigned *flush_tid) { - struct ceph_mds_client *mdsc = &ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; struct ceph_inode_info *ci = ceph_inode(inode); int unlock_session = session ? 0 : 1; int flushing = 0; @@ -1872,7 +1873,7 @@ int ceph_write_inode(struct inode *inode, struct writeback_control *wbc) caps_are_flushed(inode, flush_tid)); } else { struct ceph_mds_client *mdsc = - &ceph_sb_to_client(inode->i_sb)->mdsc; + ceph_sb_to_client(inode->i_sb)->mdsc; spin_lock(&inode->i_lock); if (__ceph_caps_dirty(ci)) @@ -2465,7 +2466,7 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, __releases(inode->i_lock) { struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_mds_client *mdsc = &ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; unsigned seq = le32_to_cpu(m->seq); int dirty = le32_to_cpu(m->dirty); int cleaned = 0; @@ -2713,7 +2714,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, struct ceph_msg *msg) { struct ceph_mds_client *mdsc = session->s_mdsc; - struct super_block *sb = mdsc->client->sb; + struct super_block *sb = mdsc->fsc->sb; struct inode *inode; struct ceph_cap *cap; struct ceph_mds_caps *h; diff --git a/fs/ceph/ceph_debug.h b/fs/ceph/ceph_debug.h deleted file mode 100644 index 1818c230561..00000000000 --- a/fs/ceph/ceph_debug.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _FS_CEPH_DEBUG_H -#define _FS_CEPH_DEBUG_H - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#ifdef CONFIG_CEPH_FS_PRETTYDEBUG - -/* - * wrap pr_debug to include a filename:lineno prefix on each line. - * this incurs some overhead (kernel size and execution time) due to - * the extra function call at each call site. - */ - -# if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) -extern const char *ceph_file_part(const char *s, int len); -# define dout(fmt, ...) \ - pr_debug(" %12.12s:%-4d : " fmt, \ - ceph_file_part(__FILE__, sizeof(__FILE__)), \ - __LINE__, ##__VA_ARGS__) -# else -/* faux printk call just to see any compiler warnings. */ -# define dout(fmt, ...) do { \ - if (0) \ - printk(KERN_DEBUG fmt, ##__VA_ARGS__); \ - } while (0) -# endif - -#else - -/* - * or, just wrap pr_debug - */ -# define dout(fmt, ...) pr_debug(" " fmt, ##__VA_ARGS__) - -#endif - -#endif diff --git a/fs/ceph/ceph_frag.c b/fs/ceph/ceph_frag.c index ab6cf35c409..bdce8b1fbd0 100644 --- a/fs/ceph/ceph_frag.c +++ b/fs/ceph/ceph_frag.c @@ -1,7 +1,8 @@ /* * Ceph 'frag' type */ -#include "types.h" +#include +#include int ceph_frag_compare(__u32 a, __u32 b) { diff --git a/fs/ceph/ceph_frag.h b/fs/ceph/ceph_frag.h deleted file mode 100644 index 5babb8e9535..00000000000 --- a/fs/ceph/ceph_frag.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef FS_CEPH_FRAG_H -#define FS_CEPH_FRAG_H - -/* - * "Frags" are a way to describe a subset of a 32-bit number space, - * using a mask and a value to match against that mask. Any given frag - * (subset of the number space) can be partitioned into 2^n sub-frags. - * - * Frags are encoded into a 32-bit word: - * 8 upper bits = "bits" - * 24 lower bits = "value" - * (We could go to 5+27 bits, but who cares.) - * - * We use the _most_ significant bits of the 24 bit value. This makes - * values logically sort. - * - * Unfortunately, because the "bits" field is still in the high bits, we - * can't sort encoded frags numerically. However, it does allow you - * to feed encoded frags as values into frag_contains_value. - */ -static inline __u32 ceph_frag_make(__u32 b, __u32 v) -{ - return (b << 24) | - (v & (0xffffffu << (24-b)) & 0xffffffu); -} -static inline __u32 ceph_frag_bits(__u32 f) -{ - return f >> 24; -} -static inline __u32 ceph_frag_value(__u32 f) -{ - return f & 0xffffffu; -} -static inline __u32 ceph_frag_mask(__u32 f) -{ - return (0xffffffu << (24-ceph_frag_bits(f))) & 0xffffffu; -} -static inline __u32 ceph_frag_mask_shift(__u32 f) -{ - return 24 - ceph_frag_bits(f); -} - -static inline int ceph_frag_contains_value(__u32 f, __u32 v) -{ - return (v & ceph_frag_mask(f)) == ceph_frag_value(f); -} -static inline int ceph_frag_contains_frag(__u32 f, __u32 sub) -{ - /* is sub as specific as us, and contained by us? */ - return ceph_frag_bits(sub) >= ceph_frag_bits(f) && - (ceph_frag_value(sub) & ceph_frag_mask(f)) == ceph_frag_value(f); -} - -static inline __u32 ceph_frag_parent(__u32 f) -{ - return ceph_frag_make(ceph_frag_bits(f) - 1, - ceph_frag_value(f) & (ceph_frag_mask(f) << 1)); -} -static inline int ceph_frag_is_left_child(__u32 f) -{ - return ceph_frag_bits(f) > 0 && - (ceph_frag_value(f) & (0x1000000 >> ceph_frag_bits(f))) == 0; -} -static inline int ceph_frag_is_right_child(__u32 f) -{ - return ceph_frag_bits(f) > 0 && - (ceph_frag_value(f) & (0x1000000 >> ceph_frag_bits(f))) == 1; -} -static inline __u32 ceph_frag_sibling(__u32 f) -{ - return ceph_frag_make(ceph_frag_bits(f), - ceph_frag_value(f) ^ (0x1000000 >> ceph_frag_bits(f))); -} -static inline __u32 ceph_frag_left_child(__u32 f) -{ - return ceph_frag_make(ceph_frag_bits(f)+1, ceph_frag_value(f)); -} -static inline __u32 ceph_frag_right_child(__u32 f) -{ - return ceph_frag_make(ceph_frag_bits(f)+1, - ceph_frag_value(f) | (0x1000000 >> (1+ceph_frag_bits(f)))); -} -static inline __u32 ceph_frag_make_child(__u32 f, int by, int i) -{ - int newbits = ceph_frag_bits(f) + by; - return ceph_frag_make(newbits, - ceph_frag_value(f) | (i << (24 - newbits))); -} -static inline int ceph_frag_is_leftmost(__u32 f) -{ - return ceph_frag_value(f) == 0; -} -static inline int ceph_frag_is_rightmost(__u32 f) -{ - return ceph_frag_value(f) == ceph_frag_mask(f); -} -static inline __u32 ceph_frag_next(__u32 f) -{ - return ceph_frag_make(ceph_frag_bits(f), - ceph_frag_value(f) + (0x1000000 >> ceph_frag_bits(f))); -} - -/* - * comparator to sort frags logically, as when traversing the - * number space in ascending order... - */ -int ceph_frag_compare(__u32 a, __u32 b); - -#endif diff --git a/fs/ceph/ceph_fs.c b/fs/ceph/ceph_fs.c deleted file mode 100644 index 3ac6cc7c115..00000000000 --- a/fs/ceph/ceph_fs.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Some non-inline ceph helpers - */ -#include "types.h" - -/* - * return true if @layout appears to be valid - */ -int ceph_file_layout_is_valid(const struct ceph_file_layout *layout) -{ - __u32 su = le32_to_cpu(layout->fl_stripe_unit); - __u32 sc = le32_to_cpu(layout->fl_stripe_count); - __u32 os = le32_to_cpu(layout->fl_object_size); - - /* stripe unit, object size must be non-zero, 64k increment */ - if (!su || (su & (CEPH_MIN_STRIPE_UNIT-1))) - return 0; - if (!os || (os & (CEPH_MIN_STRIPE_UNIT-1))) - return 0; - /* object size must be a multiple of stripe unit */ - if (os < su || os % su) - return 0; - /* stripe count must be non-zero */ - if (!sc) - return 0; - return 1; -} - - -int ceph_flags_to_mode(int flags) -{ - int mode; - -#ifdef O_DIRECTORY /* fixme */ - if ((flags & O_DIRECTORY) == O_DIRECTORY) - return CEPH_FILE_MODE_PIN; -#endif - if ((flags & O_APPEND) == O_APPEND) - flags |= O_WRONLY; - - if ((flags & O_ACCMODE) == O_RDWR) - mode = CEPH_FILE_MODE_RDWR; - else if ((flags & O_ACCMODE) == O_WRONLY) - mode = CEPH_FILE_MODE_WR; - else - mode = CEPH_FILE_MODE_RD; - -#ifdef O_LAZY - if (flags & O_LAZY) - mode |= CEPH_FILE_MODE_LAZY; -#endif - - return mode; -} - -int ceph_caps_for_mode(int mode) -{ - int caps = CEPH_CAP_PIN; - - if (mode & CEPH_FILE_MODE_RD) - caps |= CEPH_CAP_FILE_SHARED | - CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE; - if (mode & CEPH_FILE_MODE_WR) - caps |= CEPH_CAP_FILE_EXCL | - CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | - CEPH_CAP_AUTH_SHARED | CEPH_CAP_AUTH_EXCL | - CEPH_CAP_XATTR_SHARED | CEPH_CAP_XATTR_EXCL; - if (mode & CEPH_FILE_MODE_LAZY) - caps |= CEPH_CAP_FILE_LAZYIO; - - return caps; -} diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h deleted file mode 100644 index d5619ac8671..00000000000 --- a/fs/ceph/ceph_fs.h +++ /dev/null @@ -1,728 +0,0 @@ -/* - * ceph_fs.h - Ceph constants and data types to share between kernel and - * user space. - * - * Most types in this file are defined as little-endian, and are - * primarily intended to describe data structures that pass over the - * wire or that are stored on disk. - * - * LGPL2 - */ - -#ifndef CEPH_FS_H -#define CEPH_FS_H - -#include "msgr.h" -#include "rados.h" - -/* - * subprotocol versions. when specific messages types or high-level - * protocols change, bump the affected components. we keep rev - * internal cluster protocols separately from the public, - * client-facing protocol. - */ -#define CEPH_OSD_PROTOCOL 8 /* cluster internal */ -#define CEPH_MDS_PROTOCOL 12 /* cluster internal */ -#define CEPH_MON_PROTOCOL 5 /* cluster internal */ -#define CEPH_OSDC_PROTOCOL 24 /* server/client */ -#define CEPH_MDSC_PROTOCOL 32 /* server/client */ -#define CEPH_MONC_PROTOCOL 15 /* server/client */ - - -#define CEPH_INO_ROOT 1 -#define CEPH_INO_CEPH 2 /* hidden .ceph dir */ - -/* arbitrary limit on max # of monitors (cluster of 3 is typical) */ -#define CEPH_MAX_MON 31 - - -/* - * feature bits - */ -#define CEPH_FEATURE_UID (1<<0) -#define CEPH_FEATURE_NOSRCADDR (1<<1) -#define CEPH_FEATURE_MONCLOCKCHECK (1<<2) -#define CEPH_FEATURE_FLOCK (1<<3) - - -/* - * ceph_file_layout - describe data layout for a file/inode - */ -struct ceph_file_layout { - /* file -> object mapping */ - __le32 fl_stripe_unit; /* stripe unit, in bytes. must be multiple - of page size. */ - __le32 fl_stripe_count; /* over this many objects */ - __le32 fl_object_size; /* until objects are this big, then move to - new objects */ - __le32 fl_cas_hash; /* 0 = none; 1 = sha256 */ - - /* pg -> disk layout */ - __le32 fl_object_stripe_unit; /* for per-object parity, if any */ - - /* object -> pg layout */ - __le32 fl_pg_preferred; /* preferred primary for pg (-1 for none) */ - __le32 fl_pg_pool; /* namespace, crush ruleset, rep level */ -} __attribute__ ((packed)); - -#define CEPH_MIN_STRIPE_UNIT 65536 - -int ceph_file_layout_is_valid(const struct ceph_file_layout *layout); - - -/* crypto algorithms */ -#define CEPH_CRYPTO_NONE 0x0 -#define CEPH_CRYPTO_AES 0x1 - -#define CEPH_AES_IV "cephsageyudagreg" - -/* security/authentication protocols */ -#define CEPH_AUTH_UNKNOWN 0x0 -#define CEPH_AUTH_NONE 0x1 -#define CEPH_AUTH_CEPHX 0x2 - -#define CEPH_AUTH_UID_DEFAULT ((__u64) -1) - - -/********************************************* - * message layer - */ - -/* - * message types - */ - -/* misc */ -#define CEPH_MSG_SHUTDOWN 1 -#define CEPH_MSG_PING 2 - -/* client <-> monitor */ -#define CEPH_MSG_MON_MAP 4 -#define CEPH_MSG_MON_GET_MAP 5 -#define CEPH_MSG_STATFS 13 -#define CEPH_MSG_STATFS_REPLY 14 -#define CEPH_MSG_MON_SUBSCRIBE 15 -#define CEPH_MSG_MON_SUBSCRIBE_ACK 16 -#define CEPH_MSG_AUTH 17 -#define CEPH_MSG_AUTH_REPLY 18 - -/* client <-> mds */ -#define CEPH_MSG_MDS_MAP 21 - -#define CEPH_MSG_CLIENT_SESSION 22 -#define CEPH_MSG_CLIENT_RECONNECT 23 - -#define CEPH_MSG_CLIENT_REQUEST 24 -#define CEPH_MSG_CLIENT_REQUEST_FORWARD 25 -#define CEPH_MSG_CLIENT_REPLY 26 -#define CEPH_MSG_CLIENT_CAPS 0x310 -#define CEPH_MSG_CLIENT_LEASE 0x311 -#define CEPH_MSG_CLIENT_SNAP 0x312 -#define CEPH_MSG_CLIENT_CAPRELEASE 0x313 - -/* pool ops */ -#define CEPH_MSG_POOLOP_REPLY 48 -#define CEPH_MSG_POOLOP 49 - - -/* osd */ -#define CEPH_MSG_OSD_MAP 41 -#define CEPH_MSG_OSD_OP 42 -#define CEPH_MSG_OSD_OPREPLY 43 - -/* pool operations */ -enum { - POOL_OP_CREATE = 0x01, - POOL_OP_DELETE = 0x02, - POOL_OP_AUID_CHANGE = 0x03, - POOL_OP_CREATE_SNAP = 0x11, - POOL_OP_DELETE_SNAP = 0x12, - POOL_OP_CREATE_UNMANAGED_SNAP = 0x21, - POOL_OP_DELETE_UNMANAGED_SNAP = 0x22, -}; - -struct ceph_mon_request_header { - __le64 have_version; - __le16 session_mon; - __le64 session_mon_tid; -} __attribute__ ((packed)); - -struct ceph_mon_statfs { - struct ceph_mon_request_header monhdr; - struct ceph_fsid fsid; -} __attribute__ ((packed)); - -struct ceph_statfs { - __le64 kb, kb_used, kb_avail; - __le64 num_objects; -} __attribute__ ((packed)); - -struct ceph_mon_statfs_reply { - struct ceph_fsid fsid; - __le64 version; - struct ceph_statfs st; -} __attribute__ ((packed)); - -const char *ceph_pool_op_name(int op); - -struct ceph_mon_poolop { - struct ceph_mon_request_header monhdr; - struct ceph_fsid fsid; - __le32 pool; - __le32 op; - __le64 auid; - __le64 snapid; - __le32 name_len; -} __attribute__ ((packed)); - -struct ceph_mon_poolop_reply { - struct ceph_mon_request_header monhdr; - struct ceph_fsid fsid; - __le32 reply_code; - __le32 epoch; - char has_data; - char data[0]; -} __attribute__ ((packed)); - -struct ceph_mon_unmanaged_snap { - __le64 snapid; -} __attribute__ ((packed)); - -struct ceph_osd_getmap { - struct ceph_mon_request_header monhdr; - struct ceph_fsid fsid; - __le32 start; -} __attribute__ ((packed)); - -struct ceph_mds_getmap { - struct ceph_mon_request_header monhdr; - struct ceph_fsid fsid; -} __attribute__ ((packed)); - -struct ceph_client_mount { - struct ceph_mon_request_header monhdr; -} __attribute__ ((packed)); - -struct ceph_mon_subscribe_item { - __le64 have_version; __le64 have; - __u8 onetime; -} __attribute__ ((packed)); - -struct ceph_mon_subscribe_ack { - __le32 duration; /* seconds */ - struct ceph_fsid fsid; -} __attribute__ ((packed)); - -/* - * mds states - * > 0 -> in - * <= 0 -> out - */ -#define CEPH_MDS_STATE_DNE 0 /* down, does not exist. */ -#define CEPH_MDS_STATE_STOPPED -1 /* down, once existed, but no subtrees. - empty log. */ -#define CEPH_MDS_STATE_BOOT -4 /* up, boot announcement. */ -#define CEPH_MDS_STATE_STANDBY -5 /* up, idle. waiting for assignment. */ -#define CEPH_MDS_STATE_CREATING -6 /* up, creating MDS instance. */ -#define CEPH_MDS_STATE_STARTING -7 /* up, starting previously stopped mds */ -#define CEPH_MDS_STATE_STANDBY_REPLAY -8 /* up, tailing active node's journal */ - -#define CEPH_MDS_STATE_REPLAY 8 /* up, replaying journal. */ -#define CEPH_MDS_STATE_RESOLVE 9 /* up, disambiguating distributed - operations (import, rename, etc.) */ -#define CEPH_MDS_STATE_RECONNECT 10 /* up, reconnect to clients */ -#define CEPH_MDS_STATE_REJOIN 11 /* up, rejoining distributed cache */ -#define CEPH_MDS_STATE_CLIENTREPLAY 12 /* up, replaying client operations */ -#define CEPH_MDS_STATE_ACTIVE 13 /* up, active */ -#define CEPH_MDS_STATE_STOPPING 14 /* up, but exporting metadata */ - -extern const char *ceph_mds_state_name(int s); - - -/* - * metadata lock types. - * - these are bitmasks.. we can compose them - * - they also define the lock ordering by the MDS - * - a few of these are internal to the mds - */ -#define CEPH_LOCK_DVERSION 1 -#define CEPH_LOCK_DN 2 -#define CEPH_LOCK_ISNAP 16 -#define CEPH_LOCK_IVERSION 32 /* mds internal */ -#define CEPH_LOCK_IFILE 64 -#define CEPH_LOCK_IAUTH 128 -#define CEPH_LOCK_ILINK 256 -#define CEPH_LOCK_IDFT 512 /* dir frag tree */ -#define CEPH_LOCK_INEST 1024 /* mds internal */ -#define CEPH_LOCK_IXATTR 2048 -#define CEPH_LOCK_IFLOCK 4096 /* advisory file locks */ -#define CEPH_LOCK_INO 8192 /* immutable inode bits; not a lock */ - -/* client_session ops */ -enum { - CEPH_SESSION_REQUEST_OPEN, - CEPH_SESSION_OPEN, - CEPH_SESSION_REQUEST_CLOSE, - CEPH_SESSION_CLOSE, - CEPH_SESSION_REQUEST_RENEWCAPS, - CEPH_SESSION_RENEWCAPS, - CEPH_SESSION_STALE, - CEPH_SESSION_RECALL_STATE, -}; - -extern const char *ceph_session_op_name(int op); - -struct ceph_mds_session_head { - __le32 op; - __le64 seq; - struct ceph_timespec stamp; - __le32 max_caps, max_leases; -} __attribute__ ((packed)); - -/* client_request */ -/* - * metadata ops. - * & 0x001000 -> write op - * & 0x010000 -> follow symlink (e.g. stat(), not lstat()). - & & 0x100000 -> use weird ino/path trace - */ -#define CEPH_MDS_OP_WRITE 0x001000 -enum { - CEPH_MDS_OP_LOOKUP = 0x00100, - CEPH_MDS_OP_GETATTR = 0x00101, - CEPH_MDS_OP_LOOKUPHASH = 0x00102, - CEPH_MDS_OP_LOOKUPPARENT = 0x00103, - - CEPH_MDS_OP_SETXATTR = 0x01105, - CEPH_MDS_OP_RMXATTR = 0x01106, - CEPH_MDS_OP_SETLAYOUT = 0x01107, - CEPH_MDS_OP_SETATTR = 0x01108, - CEPH_MDS_OP_SETFILELOCK= 0x01109, - CEPH_MDS_OP_GETFILELOCK= 0x00110, - - CEPH_MDS_OP_MKNOD = 0x01201, - CEPH_MDS_OP_LINK = 0x01202, - CEPH_MDS_OP_UNLINK = 0x01203, - CEPH_MDS_OP_RENAME = 0x01204, - CEPH_MDS_OP_MKDIR = 0x01220, - CEPH_MDS_OP_RMDIR = 0x01221, - CEPH_MDS_OP_SYMLINK = 0x01222, - - CEPH_MDS_OP_CREATE = 0x01301, - CEPH_MDS_OP_OPEN = 0x00302, - CEPH_MDS_OP_READDIR = 0x00305, - - CEPH_MDS_OP_LOOKUPSNAP = 0x00400, - CEPH_MDS_OP_MKSNAP = 0x01400, - CEPH_MDS_OP_RMSNAP = 0x01401, - CEPH_MDS_OP_LSSNAP = 0x00402, -}; - -extern const char *ceph_mds_op_name(int op); - - -#define CEPH_SETATTR_MODE 1 -#define CEPH_SETATTR_UID 2 -#define CEPH_SETATTR_GID 4 -#define CEPH_SETATTR_MTIME 8 -#define CEPH_SETATTR_ATIME 16 -#define CEPH_SETATTR_SIZE 32 -#define CEPH_SETATTR_CTIME 64 - -union ceph_mds_request_args { - struct { - __le32 mask; /* CEPH_CAP_* */ - } __attribute__ ((packed)) getattr; - struct { - __le32 mode; - __le32 uid; - __le32 gid; - struct ceph_timespec mtime; - struct ceph_timespec atime; - __le64 size, old_size; /* old_size needed by truncate */ - __le32 mask; /* CEPH_SETATTR_* */ - } __attribute__ ((packed)) setattr; - struct { - __le32 frag; /* which dir fragment */ - __le32 max_entries; /* how many dentries to grab */ - __le32 max_bytes; - } __attribute__ ((packed)) readdir; - struct { - __le32 mode; - __le32 rdev; - } __attribute__ ((packed)) mknod; - struct { - __le32 mode; - } __attribute__ ((packed)) mkdir; - struct { - __le32 flags; - __le32 mode; - __le32 stripe_unit; /* layout for newly created file */ - __le32 stripe_count; /* ... */ - __le32 object_size; - __le32 file_replication; - __le32 preferred; - } __attribute__ ((packed)) open; - struct { - __le32 flags; - } __attribute__ ((packed)) setxattr; - struct { - struct ceph_file_layout layout; - } __attribute__ ((packed)) setlayout; - struct { - __u8 rule; /* currently fcntl or flock */ - __u8 type; /* shared, exclusive, remove*/ - __le64 pid; /* process id requesting the lock */ - __le64 pid_namespace; - __le64 start; /* initial location to lock */ - __le64 length; /* num bytes to lock from start */ - __u8 wait; /* will caller wait for lock to become available? */ - } __attribute__ ((packed)) filelock_change; -} __attribute__ ((packed)); - -#define CEPH_MDS_FLAG_REPLAY 1 /* this is a replayed op */ -#define CEPH_MDS_FLAG_WANT_DENTRY 2 /* want dentry in reply */ - -struct ceph_mds_request_head { - __le64 oldest_client_tid; - __le32 mdsmap_epoch; /* on client */ - __le32 flags; /* CEPH_MDS_FLAG_* */ - __u8 num_retry, num_fwd; /* count retry, fwd attempts */ - __le16 num_releases; /* # include cap/lease release records */ - __le32 op; /* mds op code */ - __le32 caller_uid, caller_gid; - __le64 ino; /* use this ino for openc, mkdir, mknod, - etc. (if replaying) */ - union ceph_mds_request_args args; -} __attribute__ ((packed)); - -/* cap/lease release record */ -struct ceph_mds_request_release { - __le64 ino, cap_id; /* ino and unique cap id */ - __le32 caps, wanted; /* new issued, wanted */ - __le32 seq, issue_seq, mseq; - __le32 dname_seq; /* if releasing a dentry lease, a */ - __le32 dname_len; /* string follows. */ -} __attribute__ ((packed)); - -/* client reply */ -struct ceph_mds_reply_head { - __le32 op; - __le32 result; - __le32 mdsmap_epoch; - __u8 safe; /* true if committed to disk */ - __u8 is_dentry, is_target; /* true if dentry, target inode records - are included with reply */ -} __attribute__ ((packed)); - -/* one for each node split */ -struct ceph_frag_tree_split { - __le32 frag; /* this frag splits... */ - __le32 by; /* ...by this many bits */ -} __attribute__ ((packed)); - -struct ceph_frag_tree_head { - __le32 nsplits; /* num ceph_frag_tree_split records */ - struct ceph_frag_tree_split splits[]; -} __attribute__ ((packed)); - -/* capability issue, for bundling with mds reply */ -struct ceph_mds_reply_cap { - __le32 caps, wanted; /* caps issued, wanted */ - __le64 cap_id; - __le32 seq, mseq; - __le64 realm; /* snap realm */ - __u8 flags; /* CEPH_CAP_FLAG_* */ -} __attribute__ ((packed)); - -#define CEPH_CAP_FLAG_AUTH 1 /* cap is issued by auth mds */ - -/* inode record, for bundling with mds reply */ -struct ceph_mds_reply_inode { - __le64 ino; - __le64 snapid; - __le32 rdev; - __le64 version; /* inode version */ - __le64 xattr_version; /* version for xattr blob */ - struct ceph_mds_reply_cap cap; /* caps issued for this inode */ - struct ceph_file_layout layout; - struct ceph_timespec ctime, mtime, atime; - __le32 time_warp_seq; - __le64 size, max_size, truncate_size; - __le32 truncate_seq; - __le32 mode, uid, gid; - __le32 nlink; - __le64 files, subdirs, rbytes, rfiles, rsubdirs; /* dir stats */ - struct ceph_timespec rctime; - struct ceph_frag_tree_head fragtree; /* (must be at end of struct) */ -} __attribute__ ((packed)); -/* followed by frag array, then symlink string, then xattr blob */ - -/* reply_lease follows dname, and reply_inode */ -struct ceph_mds_reply_lease { - __le16 mask; /* lease type(s) */ - __le32 duration_ms; /* lease duration */ - __le32 seq; -} __attribute__ ((packed)); - -struct ceph_mds_reply_dirfrag { - __le32 frag; /* fragment */ - __le32 auth; /* auth mds, if this is a delegation point */ - __le32 ndist; /* number of mds' this is replicated on */ - __le32 dist[]; -} __attribute__ ((packed)); - -#define CEPH_LOCK_FCNTL 1 -#define CEPH_LOCK_FLOCK 2 - -#define CEPH_LOCK_SHARED 1 -#define CEPH_LOCK_EXCL 2 -#define CEPH_LOCK_UNLOCK 4 - -struct ceph_filelock { - __le64 start;/* file offset to start lock at */ - __le64 length; /* num bytes to lock; 0 for all following start */ - __le64 client; /* which client holds the lock */ - __le64 pid; /* process id holding the lock on the client */ - __le64 pid_namespace; - __u8 type; /* shared lock, exclusive lock, or unlock */ -} __attribute__ ((packed)); - - -/* file access modes */ -#define CEPH_FILE_MODE_PIN 0 -#define CEPH_FILE_MODE_RD 1 -#define CEPH_FILE_MODE_WR 2 -#define CEPH_FILE_MODE_RDWR 3 /* RD | WR */ -#define CEPH_FILE_MODE_LAZY 4 /* lazy io */ -#define CEPH_FILE_MODE_NUM 8 /* bc these are bit fields.. mostly */ - -int ceph_flags_to_mode(int flags); - - -/* capability bits */ -#define CEPH_CAP_PIN 1 /* no specific capabilities beyond the pin */ - -/* generic cap bits */ -#define CEPH_CAP_GSHARED 1 /* client can reads */ -#define CEPH_CAP_GEXCL 2 /* client can read and update */ -#define CEPH_CAP_GCACHE 4 /* (file) client can cache reads */ -#define CEPH_CAP_GRD 8 /* (file) client can read */ -#define CEPH_CAP_GWR 16 /* (file) client can write */ -#define CEPH_CAP_GBUFFER 32 /* (file) client can buffer writes */ -#define CEPH_CAP_GWREXTEND 64 /* (file) client can extend EOF */ -#define CEPH_CAP_GLAZYIO 128 /* (file) client can perform lazy io */ - -/* per-lock shift */ -#define CEPH_CAP_SAUTH 2 -#define CEPH_CAP_SLINK 4 -#define CEPH_CAP_SXATTR 6 -#define CEPH_CAP_SFILE 8 -#define CEPH_CAP_SFLOCK 20 - -#define CEPH_CAP_BITS 22 - -/* composed values */ -#define CEPH_CAP_AUTH_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SAUTH) -#define CEPH_CAP_AUTH_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SAUTH) -#define CEPH_CAP_LINK_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SLINK) -#define CEPH_CAP_LINK_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SLINK) -#define CEPH_CAP_XATTR_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SXATTR) -#define CEPH_CAP_XATTR_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SXATTR) -#define CEPH_CAP_FILE(x) (x << CEPH_CAP_SFILE) -#define CEPH_CAP_FILE_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SFILE) -#define CEPH_CAP_FILE_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SFILE) -#define CEPH_CAP_FILE_CACHE (CEPH_CAP_GCACHE << CEPH_CAP_SFILE) -#define CEPH_CAP_FILE_RD (CEPH_CAP_GRD << CEPH_CAP_SFILE) -#define CEPH_CAP_FILE_WR (CEPH_CAP_GWR << CEPH_CAP_SFILE) -#define CEPH_CAP_FILE_BUFFER (CEPH_CAP_GBUFFER << CEPH_CAP_SFILE) -#define CEPH_CAP_FILE_WREXTEND (CEPH_CAP_GWREXTEND << CEPH_CAP_SFILE) -#define CEPH_CAP_FILE_LAZYIO (CEPH_CAP_GLAZYIO << CEPH_CAP_SFILE) -#define CEPH_CAP_FLOCK_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SFLOCK) -#define CEPH_CAP_FLOCK_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SFLOCK) - - -/* cap masks (for getattr) */ -#define CEPH_STAT_CAP_INODE CEPH_CAP_PIN -#define CEPH_STAT_CAP_TYPE CEPH_CAP_PIN /* mode >> 12 */ -#define CEPH_STAT_CAP_SYMLINK CEPH_CAP_PIN -#define CEPH_STAT_CAP_UID CEPH_CAP_AUTH_SHARED -#define CEPH_STAT_CAP_GID CEPH_CAP_AUTH_SHARED -#define CEPH_STAT_CAP_MODE CEPH_CAP_AUTH_SHARED -#define CEPH_STAT_CAP_NLINK CEPH_CAP_LINK_SHARED -#define CEPH_STAT_CAP_LAYOUT CEPH_CAP_FILE_SHARED -#define CEPH_STAT_CAP_MTIME CEPH_CAP_FILE_SHARED -#define CEPH_STAT_CAP_SIZE CEPH_CAP_FILE_SHARED -#define CEPH_STAT_CAP_ATIME CEPH_CAP_FILE_SHARED /* fixme */ -#define CEPH_STAT_CAP_XATTR CEPH_CAP_XATTR_SHARED -#define CEPH_STAT_CAP_INODE_ALL (CEPH_CAP_PIN | \ - CEPH_CAP_AUTH_SHARED | \ - CEPH_CAP_LINK_SHARED | \ - CEPH_CAP_FILE_SHARED | \ - CEPH_CAP_XATTR_SHARED) - -#define CEPH_CAP_ANY_SHARED (CEPH_CAP_AUTH_SHARED | \ - CEPH_CAP_LINK_SHARED | \ - CEPH_CAP_XATTR_SHARED | \ - CEPH_CAP_FILE_SHARED) -#define CEPH_CAP_ANY_RD (CEPH_CAP_ANY_SHARED | CEPH_CAP_FILE_RD | \ - CEPH_CAP_FILE_CACHE) - -#define CEPH_CAP_ANY_EXCL (CEPH_CAP_AUTH_EXCL | \ - CEPH_CAP_LINK_EXCL | \ - CEPH_CAP_XATTR_EXCL | \ - CEPH_CAP_FILE_EXCL) -#define CEPH_CAP_ANY_FILE_WR (CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | \ - CEPH_CAP_FILE_EXCL) -#define CEPH_CAP_ANY_WR (CEPH_CAP_ANY_EXCL | CEPH_CAP_ANY_FILE_WR) -#define CEPH_CAP_ANY (CEPH_CAP_ANY_RD | CEPH_CAP_ANY_EXCL | \ - CEPH_CAP_ANY_FILE_WR | CEPH_CAP_FILE_LAZYIO | \ - CEPH_CAP_PIN) - -#define CEPH_CAP_LOCKS (CEPH_LOCK_IFILE | CEPH_LOCK_IAUTH | CEPH_LOCK_ILINK | \ - CEPH_LOCK_IXATTR) - -int ceph_caps_for_mode(int mode); - -enum { - CEPH_CAP_OP_GRANT, /* mds->client grant */ - CEPH_CAP_OP_REVOKE, /* mds->client revoke */ - CEPH_CAP_OP_TRUNC, /* mds->client trunc notify */ - CEPH_CAP_OP_EXPORT, /* mds has exported the cap */ - CEPH_CAP_OP_IMPORT, /* mds has imported the cap */ - CEPH_CAP_OP_UPDATE, /* client->mds update */ - CEPH_CAP_OP_DROP, /* client->mds drop cap bits */ - CEPH_CAP_OP_FLUSH, /* client->mds cap writeback */ - CEPH_CAP_OP_FLUSH_ACK, /* mds->client flushed */ - CEPH_CAP_OP_FLUSHSNAP, /* client->mds flush snapped metadata */ - CEPH_CAP_OP_FLUSHSNAP_ACK, /* mds->client flushed snapped metadata */ - CEPH_CAP_OP_RELEASE, /* client->mds release (clean) cap */ - CEPH_CAP_OP_RENEW, /* client->mds renewal request */ -}; - -extern const char *ceph_cap_op_name(int op); - -/* - * caps message, used for capability callbacks, acks, requests, etc. - */ -struct ceph_mds_caps { - __le32 op; /* CEPH_CAP_OP_* */ - __le64 ino, realm; - __le64 cap_id; - __le32 seq, issue_seq; - __le32 caps, wanted, dirty; /* latest issued/wanted/dirty */ - __le32 migrate_seq; - __le64 snap_follows; - __le32 snap_trace_len; - - /* authlock */ - __le32 uid, gid, mode; - - /* linklock */ - __le32 nlink; - - /* xattrlock */ - __le32 xattr_len; - __le64 xattr_version; - - /* filelock */ - __le64 size, max_size, truncate_size; - __le32 truncate_seq; - struct ceph_timespec mtime, atime, ctime; - struct ceph_file_layout layout; - __le32 time_warp_seq; -} __attribute__ ((packed)); - -/* cap release msg head */ -struct ceph_mds_cap_release { - __le32 num; /* number of cap_items that follow */ -} __attribute__ ((packed)); - -struct ceph_mds_cap_item { - __le64 ino; - __le64 cap_id; - __le32 migrate_seq, seq; -} __attribute__ ((packed)); - -#define CEPH_MDS_LEASE_REVOKE 1 /* mds -> client */ -#define CEPH_MDS_LEASE_RELEASE 2 /* client -> mds */ -#define CEPH_MDS_LEASE_RENEW 3 /* client <-> mds */ -#define CEPH_MDS_LEASE_REVOKE_ACK 4 /* client -> mds */ - -extern const char *ceph_lease_op_name(int o); - -/* lease msg header */ -struct ceph_mds_lease { - __u8 action; /* CEPH_MDS_LEASE_* */ - __le16 mask; /* which lease */ - __le64 ino; - __le64 first, last; /* snap range */ - __le32 seq; - __le32 duration_ms; /* duration of renewal */ -} __attribute__ ((packed)); -/* followed by a __le32+string for dname */ - -/* client reconnect */ -struct ceph_mds_cap_reconnect { - __le64 cap_id; - __le32 wanted; - __le32 issued; - __le64 snaprealm; - __le64 pathbase; /* base ino for our path to this ino */ - __le32 flock_len; /* size of flock state blob, if any */ -} __attribute__ ((packed)); -/* followed by flock blob */ - -struct ceph_mds_cap_reconnect_v1 { - __le64 cap_id; - __le32 wanted; - __le32 issued; - __le64 size; - struct ceph_timespec mtime, atime; - __le64 snaprealm; - __le64 pathbase; /* base ino for our path to this ino */ -} __attribute__ ((packed)); - -struct ceph_mds_snaprealm_reconnect { - __le64 ino; /* snap realm base */ - __le64 seq; /* snap seq for this snap realm */ - __le64 parent; /* parent realm */ -} __attribute__ ((packed)); - -/* - * snaps - */ -enum { - CEPH_SNAP_OP_UPDATE, /* CREATE or DESTROY */ - CEPH_SNAP_OP_CREATE, - CEPH_SNAP_OP_DESTROY, - CEPH_SNAP_OP_SPLIT, -}; - -extern const char *ceph_snap_op_name(int o); - -/* snap msg header */ -struct ceph_mds_snap_head { - __le32 op; /* CEPH_SNAP_OP_* */ - __le64 split; /* ino to split off, if any */ - __le32 num_split_inos; /* # inos belonging to new child realm */ - __le32 num_split_realms; /* # child realms udner new child realm */ - __le32 trace_len; /* size of snap trace blob */ -} __attribute__ ((packed)); -/* followed by split ino list, then split realms, then the trace blob */ - -/* - * encode info about a snaprealm, as viewed by a client - */ -struct ceph_mds_snap_realm { - __le64 ino; /* ino */ - __le64 created; /* snap: when created */ - __le64 parent; /* ino: parent realm */ - __le64 parent_since; /* snap: same parent since */ - __le64 seq; /* snap: version */ - __le32 num_snaps; - __le32 num_prior_parent_snaps; -} __attribute__ ((packed)); -/* followed by my snap list, then prior parent snap list */ - -#endif diff --git a/fs/ceph/ceph_hash.c b/fs/ceph/ceph_hash.c deleted file mode 100644 index bd570015d14..00000000000 --- a/fs/ceph/ceph_hash.c +++ /dev/null @@ -1,118 +0,0 @@ - -#include "types.h" - -/* - * Robert Jenkin's hash function. - * http://burtleburtle.net/bob/hash/evahash.html - * This is in the public domain. - */ -#define mix(a, b, c) \ - do { \ - a = a - b; a = a - c; a = a ^ (c >> 13); \ - b = b - c; b = b - a; b = b ^ (a << 8); \ - c = c - a; c = c - b; c = c ^ (b >> 13); \ - a = a - b; a = a - c; a = a ^ (c >> 12); \ - b = b - c; b = b - a; b = b ^ (a << 16); \ - c = c - a; c = c - b; c = c ^ (b >> 5); \ - a = a - b; a = a - c; a = a ^ (c >> 3); \ - b = b - c; b = b - a; b = b ^ (a << 10); \ - c = c - a; c = c - b; c = c ^ (b >> 15); \ - } while (0) - -unsigned ceph_str_hash_rjenkins(const char *str, unsigned length) -{ - const unsigned char *k = (const unsigned char *)str; - __u32 a, b, c; /* the internal state */ - __u32 len; /* how many key bytes still need mixing */ - - /* Set up the internal state */ - len = length; - a = 0x9e3779b9; /* the golden ratio; an arbitrary value */ - b = a; - c = 0; /* variable initialization of internal state */ - - /* handle most of the key */ - while (len >= 12) { - a = a + (k[0] + ((__u32)k[1] << 8) + ((__u32)k[2] << 16) + - ((__u32)k[3] << 24)); - b = b + (k[4] + ((__u32)k[5] << 8) + ((__u32)k[6] << 16) + - ((__u32)k[7] << 24)); - c = c + (k[8] + ((__u32)k[9] << 8) + ((__u32)k[10] << 16) + - ((__u32)k[11] << 24)); - mix(a, b, c); - k = k + 12; - len = len - 12; - } - - /* handle the last 11 bytes */ - c = c + length; - switch (len) { /* all the case statements fall through */ - case 11: - c = c + ((__u32)k[10] << 24); - case 10: - c = c + ((__u32)k[9] << 16); - case 9: - c = c + ((__u32)k[8] << 8); - /* the first byte of c is reserved for the length */ - case 8: - b = b + ((__u32)k[7] << 24); - case 7: - b = b + ((__u32)k[6] << 16); - case 6: - b = b + ((__u32)k[5] << 8); - case 5: - b = b + k[4]; - case 4: - a = a + ((__u32)k[3] << 24); - case 3: - a = a + ((__u32)k[2] << 16); - case 2: - a = a + ((__u32)k[1] << 8); - case 1: - a = a + k[0]; - /* case 0: nothing left to add */ - } - mix(a, b, c); - - return c; -} - -/* - * linux dcache hash - */ -unsigned ceph_str_hash_linux(const char *str, unsigned length) -{ - unsigned long hash = 0; - unsigned char c; - - while (length--) { - c = *str++; - hash = (hash + (c << 4) + (c >> 4)) * 11; - } - return hash; -} - - -unsigned ceph_str_hash(int type, const char *s, unsigned len) -{ - switch (type) { - case CEPH_STR_HASH_LINUX: - return ceph_str_hash_linux(s, len); - case CEPH_STR_HASH_RJENKINS: - return ceph_str_hash_rjenkins(s, len); - default: - return -1; - } -} - -const char *ceph_str_hash_name(int type) -{ - switch (type) { - case CEPH_STR_HASH_LINUX: - return "linux"; - case CEPH_STR_HASH_RJENKINS: - return "rjenkins"; - default: - return "unknown"; - } -} diff --git a/fs/ceph/ceph_hash.h b/fs/ceph/ceph_hash.h deleted file mode 100644 index d099c3f9023..00000000000 --- a/fs/ceph/ceph_hash.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef FS_CEPH_HASH_H -#define FS_CEPH_HASH_H - -#define CEPH_STR_HASH_LINUX 0x1 /* linux dcache hash */ -#define CEPH_STR_HASH_RJENKINS 0x2 /* robert jenkins' */ - -extern unsigned ceph_str_hash_linux(const char *s, unsigned len); -extern unsigned ceph_str_hash_rjenkins(const char *s, unsigned len); - -extern unsigned ceph_str_hash(int type, const char *s, unsigned len); -extern const char *ceph_str_hash_name(int type); - -#endif diff --git a/fs/ceph/ceph_strings.c b/fs/ceph/ceph_strings.c deleted file mode 100644 index c6179d3a26a..00000000000 --- a/fs/ceph/ceph_strings.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Ceph string constants - */ -#include "types.h" - -const char *ceph_entity_type_name(int type) -{ - switch (type) { - case CEPH_ENTITY_TYPE_MDS: return "mds"; - case CEPH_ENTITY_TYPE_OSD: return "osd"; - case CEPH_ENTITY_TYPE_MON: return "mon"; - case CEPH_ENTITY_TYPE_CLIENT: return "client"; - case CEPH_ENTITY_TYPE_AUTH: return "auth"; - default: return "unknown"; - } -} - -const char *ceph_osd_op_name(int op) -{ - switch (op) { - case CEPH_OSD_OP_READ: return "read"; - case CEPH_OSD_OP_STAT: return "stat"; - - case CEPH_OSD_OP_MASKTRUNC: return "masktrunc"; - - case CEPH_OSD_OP_WRITE: return "write"; - case CEPH_OSD_OP_DELETE: return "delete"; - case CEPH_OSD_OP_TRUNCATE: return "truncate"; - case CEPH_OSD_OP_ZERO: return "zero"; - case CEPH_OSD_OP_WRITEFULL: return "writefull"; - case CEPH_OSD_OP_ROLLBACK: return "rollback"; - - case CEPH_OSD_OP_APPEND: return "append"; - case CEPH_OSD_OP_STARTSYNC: return "startsync"; - case CEPH_OSD_OP_SETTRUNC: return "settrunc"; - case CEPH_OSD_OP_TRIMTRUNC: return "trimtrunc"; - - case CEPH_OSD_OP_TMAPUP: return "tmapup"; - case CEPH_OSD_OP_TMAPGET: return "tmapget"; - case CEPH_OSD_OP_TMAPPUT: return "tmapput"; - - case CEPH_OSD_OP_GETXATTR: return "getxattr"; - case CEPH_OSD_OP_GETXATTRS: return "getxattrs"; - case CEPH_OSD_OP_SETXATTR: return "setxattr"; - case CEPH_OSD_OP_SETXATTRS: return "setxattrs"; - case CEPH_OSD_OP_RESETXATTRS: return "resetxattrs"; - case CEPH_OSD_OP_RMXATTR: return "rmxattr"; - case CEPH_OSD_OP_CMPXATTR: return "cmpxattr"; - - case CEPH_OSD_OP_PULL: return "pull"; - case CEPH_OSD_OP_PUSH: return "push"; - case CEPH_OSD_OP_BALANCEREADS: return "balance-reads"; - case CEPH_OSD_OP_UNBALANCEREADS: return "unbalance-reads"; - case CEPH_OSD_OP_SCRUB: return "scrub"; - - case CEPH_OSD_OP_WRLOCK: return "wrlock"; - case CEPH_OSD_OP_WRUNLOCK: return "wrunlock"; - case CEPH_OSD_OP_RDLOCK: return "rdlock"; - case CEPH_OSD_OP_RDUNLOCK: return "rdunlock"; - case CEPH_OSD_OP_UPLOCK: return "uplock"; - case CEPH_OSD_OP_DNLOCK: return "dnlock"; - - case CEPH_OSD_OP_CALL: return "call"; - - case CEPH_OSD_OP_PGLS: return "pgls"; - } - return "???"; -} - -const char *ceph_mds_state_name(int s) -{ - switch (s) { - /* down and out */ - case CEPH_MDS_STATE_DNE: return "down:dne"; - case CEPH_MDS_STATE_STOPPED: return "down:stopped"; - /* up and out */ - case CEPH_MDS_STATE_BOOT: return "up:boot"; - case CEPH_MDS_STATE_STANDBY: return "up:standby"; - case CEPH_MDS_STATE_STANDBY_REPLAY: return "up:standby-replay"; - case CEPH_MDS_STATE_CREATING: return "up:creating"; - case CEPH_MDS_STATE_STARTING: return "up:starting"; - /* up and in */ - case CEPH_MDS_STATE_REPLAY: return "up:replay"; - case CEPH_MDS_STATE_RESOLVE: return "up:resolve"; - case CEPH_MDS_STATE_RECONNECT: return "up:reconnect"; - case CEPH_MDS_STATE_REJOIN: return "up:rejoin"; - case CEPH_MDS_STATE_CLIENTREPLAY: return "up:clientreplay"; - case CEPH_MDS_STATE_ACTIVE: return "up:active"; - case CEPH_MDS_STATE_STOPPING: return "up:stopping"; - } - return "???"; -} - -const char *ceph_session_op_name(int op) -{ - switch (op) { - case CEPH_SESSION_REQUEST_OPEN: return "request_open"; - case CEPH_SESSION_OPEN: return "open"; - case CEPH_SESSION_REQUEST_CLOSE: return "request_close"; - case CEPH_SESSION_CLOSE: return "close"; - case CEPH_SESSION_REQUEST_RENEWCAPS: return "request_renewcaps"; - case CEPH_SESSION_RENEWCAPS: return "renewcaps"; - case CEPH_SESSION_STALE: return "stale"; - case CEPH_SESSION_RECALL_STATE: return "recall_state"; - } - return "???"; -} - -const char *ceph_mds_op_name(int op) -{ - switch (op) { - case CEPH_MDS_OP_LOOKUP: return "lookup"; - case CEPH_MDS_OP_LOOKUPHASH: return "lookuphash"; - case CEPH_MDS_OP_LOOKUPPARENT: return "lookupparent"; - case CEPH_MDS_OP_GETATTR: return "getattr"; - case CEPH_MDS_OP_SETXATTR: return "setxattr"; - case CEPH_MDS_OP_SETATTR: return "setattr"; - case CEPH_MDS_OP_RMXATTR: return "rmxattr"; - case CEPH_MDS_OP_READDIR: return "readdir"; - case CEPH_MDS_OP_MKNOD: return "mknod"; - case CEPH_MDS_OP_LINK: return "link"; - case CEPH_MDS_OP_UNLINK: return "unlink"; - case CEPH_MDS_OP_RENAME: return "rename"; - case CEPH_MDS_OP_MKDIR: return "mkdir"; - case CEPH_MDS_OP_RMDIR: return "rmdir"; - case CEPH_MDS_OP_SYMLINK: return "symlink"; - case CEPH_MDS_OP_CREATE: return "create"; - case CEPH_MDS_OP_OPEN: return "open"; - case CEPH_MDS_OP_LOOKUPSNAP: return "lookupsnap"; - case CEPH_MDS_OP_LSSNAP: return "lssnap"; - case CEPH_MDS_OP_MKSNAP: return "mksnap"; - case CEPH_MDS_OP_RMSNAP: return "rmsnap"; - case CEPH_MDS_OP_SETFILELOCK: return "setfilelock"; - case CEPH_MDS_OP_GETFILELOCK: return "getfilelock"; - } - return "???"; -} - -const char *ceph_cap_op_name(int op) -{ - switch (op) { - case CEPH_CAP_OP_GRANT: return "grant"; - case CEPH_CAP_OP_REVOKE: return "revoke"; - case CEPH_CAP_OP_TRUNC: return "trunc"; - case CEPH_CAP_OP_EXPORT: return "export"; - case CEPH_CAP_OP_IMPORT: return "import"; - case CEPH_CAP_OP_UPDATE: return "update"; - case CEPH_CAP_OP_DROP: return "drop"; - case CEPH_CAP_OP_FLUSH: return "flush"; - case CEPH_CAP_OP_FLUSH_ACK: return "flush_ack"; - case CEPH_CAP_OP_FLUSHSNAP: return "flushsnap"; - case CEPH_CAP_OP_FLUSHSNAP_ACK: return "flushsnap_ack"; - case CEPH_CAP_OP_RELEASE: return "release"; - case CEPH_CAP_OP_RENEW: return "renew"; - } - return "???"; -} - -const char *ceph_lease_op_name(int o) -{ - switch (o) { - case CEPH_MDS_LEASE_REVOKE: return "revoke"; - case CEPH_MDS_LEASE_RELEASE: return "release"; - case CEPH_MDS_LEASE_RENEW: return "renew"; - case CEPH_MDS_LEASE_REVOKE_ACK: return "revoke_ack"; - } - return "???"; -} - -const char *ceph_snap_op_name(int o) -{ - switch (o) { - case CEPH_SNAP_OP_UPDATE: return "update"; - case CEPH_SNAP_OP_CREATE: return "create"; - case CEPH_SNAP_OP_DESTROY: return "destroy"; - case CEPH_SNAP_OP_SPLIT: return "split"; - } - return "???"; -} - -const char *ceph_pool_op_name(int op) -{ - switch (op) { - case POOL_OP_CREATE: return "create"; - case POOL_OP_DELETE: return "delete"; - case POOL_OP_AUID_CHANGE: return "auid change"; - case POOL_OP_CREATE_SNAP: return "create snap"; - case POOL_OP_DELETE_SNAP: return "delete snap"; - case POOL_OP_CREATE_UNMANAGED_SNAP: return "create unmanaged snap"; - case POOL_OP_DELETE_UNMANAGED_SNAP: return "delete unmanaged snap"; - } - return "???"; -} diff --git a/fs/ceph/crush/crush.c b/fs/ceph/crush/crush.c deleted file mode 100644 index fabd302e577..00000000000 --- a/fs/ceph/crush/crush.c +++ /dev/null @@ -1,151 +0,0 @@ - -#ifdef __KERNEL__ -# include -#else -# include -# include -# define kfree(x) do { if (x) free(x); } while (0) -# define BUG_ON(x) assert(!(x)) -#endif - -#include "crush.h" - -const char *crush_bucket_alg_name(int alg) -{ - switch (alg) { - case CRUSH_BUCKET_UNIFORM: return "uniform"; - case CRUSH_BUCKET_LIST: return "list"; - case CRUSH_BUCKET_TREE: return "tree"; - case CRUSH_BUCKET_STRAW: return "straw"; - default: return "unknown"; - } -} - -/** - * crush_get_bucket_item_weight - Get weight of an item in given bucket - * @b: bucket pointer - * @p: item index in bucket - */ -int crush_get_bucket_item_weight(struct crush_bucket *b, int p) -{ - if (p >= b->size) - return 0; - - switch (b->alg) { - case CRUSH_BUCKET_UNIFORM: - return ((struct crush_bucket_uniform *)b)->item_weight; - case CRUSH_BUCKET_LIST: - return ((struct crush_bucket_list *)b)->item_weights[p]; - case CRUSH_BUCKET_TREE: - if (p & 1) - return ((struct crush_bucket_tree *)b)->node_weights[p]; - return 0; - case CRUSH_BUCKET_STRAW: - return ((struct crush_bucket_straw *)b)->item_weights[p]; - } - return 0; -} - -/** - * crush_calc_parents - Calculate parent vectors for the given crush map. - * @map: crush_map pointer - */ -void crush_calc_parents(struct crush_map *map) -{ - int i, b, c; - - for (b = 0; b < map->max_buckets; b++) { - if (map->buckets[b] == NULL) - continue; - for (i = 0; i < map->buckets[b]->size; i++) { - c = map->buckets[b]->items[i]; - BUG_ON(c >= map->max_devices || - c < -map->max_buckets); - if (c >= 0) - map->device_parents[c] = map->buckets[b]->id; - else - map->bucket_parents[-1-c] = map->buckets[b]->id; - } - } -} - -void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b) -{ - kfree(b->h.perm); - kfree(b->h.items); - kfree(b); -} - -void crush_destroy_bucket_list(struct crush_bucket_list *b) -{ - kfree(b->item_weights); - kfree(b->sum_weights); - kfree(b->h.perm); - kfree(b->h.items); - kfree(b); -} - -void crush_destroy_bucket_tree(struct crush_bucket_tree *b) -{ - kfree(b->node_weights); - kfree(b); -} - -void crush_destroy_bucket_straw(struct crush_bucket_straw *b) -{ - kfree(b->straws); - kfree(b->item_weights); - kfree(b->h.perm); - kfree(b->h.items); - kfree(b); -} - -void crush_destroy_bucket(struct crush_bucket *b) -{ - switch (b->alg) { - case CRUSH_BUCKET_UNIFORM: - crush_destroy_bucket_uniform((struct crush_bucket_uniform *)b); - break; - case CRUSH_BUCKET_LIST: - crush_destroy_bucket_list((struct crush_bucket_list *)b); - break; - case CRUSH_BUCKET_TREE: - crush_destroy_bucket_tree((struct crush_bucket_tree *)b); - break; - case CRUSH_BUCKET_STRAW: - crush_destroy_bucket_straw((struct crush_bucket_straw *)b); - break; - } -} - -/** - * crush_destroy - Destroy a crush_map - * @map: crush_map pointer - */ -void crush_destroy(struct crush_map *map) -{ - int b; - - /* buckets */ - if (map->buckets) { - for (b = 0; b < map->max_buckets; b++) { - if (map->buckets[b] == NULL) - continue; - crush_destroy_bucket(map->buckets[b]); - } - kfree(map->buckets); - } - - /* rules */ - if (map->rules) { - for (b = 0; b < map->max_rules; b++) - kfree(map->rules[b]); - kfree(map->rules); - } - - kfree(map->bucket_parents); - kfree(map->device_parents); - kfree(map); -} - - diff --git a/fs/ceph/crush/crush.h b/fs/ceph/crush/crush.h deleted file mode 100644 index 97e435b191f..00000000000 --- a/fs/ceph/crush/crush.h +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef CEPH_CRUSH_CRUSH_H -#define CEPH_CRUSH_CRUSH_H - -#include - -/* - * CRUSH is a pseudo-random data distribution algorithm that - * efficiently distributes input values (typically, data objects) - * across a heterogeneous, structured storage cluster. - * - * The algorithm was originally described in detail in this paper - * (although the algorithm has evolved somewhat since then): - * - * http://www.ssrc.ucsc.edu/Papers/weil-sc06.pdf - * - * LGPL2 - */ - - -#define CRUSH_MAGIC 0x00010000ul /* for detecting algorithm revisions */ - - -#define CRUSH_MAX_DEPTH 10 /* max crush hierarchy depth */ -#define CRUSH_MAX_SET 10 /* max size of a mapping result */ - - -/* - * CRUSH uses user-defined "rules" to describe how inputs should be - * mapped to devices. A rule consists of sequence of steps to perform - * to generate the set of output devices. - */ -struct crush_rule_step { - __u32 op; - __s32 arg1; - __s32 arg2; -}; - -/* step op codes */ -enum { - CRUSH_RULE_NOOP = 0, - CRUSH_RULE_TAKE = 1, /* arg1 = value to start with */ - CRUSH_RULE_CHOOSE_FIRSTN = 2, /* arg1 = num items to pick */ - /* arg2 = type */ - CRUSH_RULE_CHOOSE_INDEP = 3, /* same */ - CRUSH_RULE_EMIT = 4, /* no args */ - CRUSH_RULE_CHOOSE_LEAF_FIRSTN = 6, - CRUSH_RULE_CHOOSE_LEAF_INDEP = 7, -}; - -/* - * for specifying choose num (arg1) relative to the max parameter - * passed to do_rule - */ -#define CRUSH_CHOOSE_N 0 -#define CRUSH_CHOOSE_N_MINUS(x) (-(x)) - -/* - * The rule mask is used to describe what the rule is intended for. - * Given a ruleset and size of output set, we search through the - * rule list for a matching rule_mask. - */ -struct crush_rule_mask { - __u8 ruleset; - __u8 type; - __u8 min_size; - __u8 max_size; -}; - -struct crush_rule { - __u32 len; - struct crush_rule_mask mask; - struct crush_rule_step steps[0]; -}; - -#define crush_rule_size(len) (sizeof(struct crush_rule) + \ - (len)*sizeof(struct crush_rule_step)) - - - -/* - * A bucket is a named container of other items (either devices or - * other buckets). Items within a bucket are chosen using one of a - * few different algorithms. The table summarizes how the speed of - * each option measures up against mapping stability when items are - * added or removed. - * - * Bucket Alg Speed Additions Removals - * ------------------------------------------------ - * uniform O(1) poor poor - * list O(n) optimal poor - * tree O(log n) good good - * straw O(n) optimal optimal - */ -enum { - CRUSH_BUCKET_UNIFORM = 1, - CRUSH_BUCKET_LIST = 2, - CRUSH_BUCKET_TREE = 3, - CRUSH_BUCKET_STRAW = 4 -}; -extern const char *crush_bucket_alg_name(int alg); - -struct crush_bucket { - __s32 id; /* this'll be negative */ - __u16 type; /* non-zero; type=0 is reserved for devices */ - __u8 alg; /* one of CRUSH_BUCKET_* */ - __u8 hash; /* which hash function to use, CRUSH_HASH_* */ - __u32 weight; /* 16-bit fixed point */ - __u32 size; /* num items */ - __s32 *items; - - /* - * cached random permutation: used for uniform bucket and for - * the linear search fallback for the other bucket types. - */ - __u32 perm_x; /* @x for which *perm is defined */ - __u32 perm_n; /* num elements of *perm that are permuted/defined */ - __u32 *perm; -}; - -struct crush_bucket_uniform { - struct crush_bucket h; - __u32 item_weight; /* 16-bit fixed point; all items equally weighted */ -}; - -struct crush_bucket_list { - struct crush_bucket h; - __u32 *item_weights; /* 16-bit fixed point */ - __u32 *sum_weights; /* 16-bit fixed point. element i is sum - of weights 0..i, inclusive */ -}; - -struct crush_bucket_tree { - struct crush_bucket h; /* note: h.size is _tree_ size, not number of - actual items */ - __u8 num_nodes; - __u32 *node_weights; -}; - -struct crush_bucket_straw { - struct crush_bucket h; - __u32 *item_weights; /* 16-bit fixed point */ - __u32 *straws; /* 16-bit fixed point */ -}; - - - -/* - * CRUSH map includes all buckets, rules, etc. - */ -struct crush_map { - struct crush_bucket **buckets; - struct crush_rule **rules; - - /* - * Parent pointers to identify the parent bucket a device or - * bucket in the hierarchy. If an item appears more than - * once, this is the _last_ time it appeared (where buckets - * are processed in bucket id order, from -1 on down to - * -max_buckets. - */ - __u32 *bucket_parents; - __u32 *device_parents; - - __s32 max_buckets; - __u32 max_rules; - __s32 max_devices; -}; - - -/* crush.c */ -extern int crush_get_bucket_item_weight(struct crush_bucket *b, int pos); -extern void crush_calc_parents(struct crush_map *map); -extern void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b); -extern void crush_destroy_bucket_list(struct crush_bucket_list *b); -extern void crush_destroy_bucket_tree(struct crush_bucket_tree *b); -extern void crush_destroy_bucket_straw(struct crush_bucket_straw *b); -extern void crush_destroy_bucket(struct crush_bucket *b); -extern void crush_destroy(struct crush_map *map); - -#endif diff --git a/fs/ceph/crush/hash.c b/fs/ceph/crush/hash.c deleted file mode 100644 index 5873aed694b..00000000000 --- a/fs/ceph/crush/hash.c +++ /dev/null @@ -1,149 +0,0 @@ - -#include -#include "hash.h" - -/* - * Robert Jenkins' function for mixing 32-bit values - * http://burtleburtle.net/bob/hash/evahash.html - * a, b = random bits, c = input and output - */ -#define crush_hashmix(a, b, c) do { \ - a = a-b; a = a-c; a = a^(c>>13); \ - b = b-c; b = b-a; b = b^(a<<8); \ - c = c-a; c = c-b; c = c^(b>>13); \ - a = a-b; a = a-c; a = a^(c>>12); \ - b = b-c; b = b-a; b = b^(a<<16); \ - c = c-a; c = c-b; c = c^(b>>5); \ - a = a-b; a = a-c; a = a^(c>>3); \ - b = b-c; b = b-a; b = b^(a<<10); \ - c = c-a; c = c-b; c = c^(b>>15); \ - } while (0) - -#define crush_hash_seed 1315423911 - -static __u32 crush_hash32_rjenkins1(__u32 a) -{ - __u32 hash = crush_hash_seed ^ a; - __u32 b = a; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(b, x, hash); - crush_hashmix(y, a, hash); - return hash; -} - -static __u32 crush_hash32_rjenkins1_2(__u32 a, __u32 b) -{ - __u32 hash = crush_hash_seed ^ a ^ b; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(a, b, hash); - crush_hashmix(x, a, hash); - crush_hashmix(b, y, hash); - return hash; -} - -static __u32 crush_hash32_rjenkins1_3(__u32 a, __u32 b, __u32 c) -{ - __u32 hash = crush_hash_seed ^ a ^ b ^ c; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(a, b, hash); - crush_hashmix(c, x, hash); - crush_hashmix(y, a, hash); - crush_hashmix(b, x, hash); - crush_hashmix(y, c, hash); - return hash; -} - -static __u32 crush_hash32_rjenkins1_4(__u32 a, __u32 b, __u32 c, __u32 d) -{ - __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(a, b, hash); - crush_hashmix(c, d, hash); - crush_hashmix(a, x, hash); - crush_hashmix(y, b, hash); - crush_hashmix(c, x, hash); - crush_hashmix(y, d, hash); - return hash; -} - -static __u32 crush_hash32_rjenkins1_5(__u32 a, __u32 b, __u32 c, __u32 d, - __u32 e) -{ - __u32 hash = crush_hash_seed ^ a ^ b ^ c ^ d ^ e; - __u32 x = 231232; - __u32 y = 1232; - crush_hashmix(a, b, hash); - crush_hashmix(c, d, hash); - crush_hashmix(e, x, hash); - crush_hashmix(y, a, hash); - crush_hashmix(b, x, hash); - crush_hashmix(y, c, hash); - crush_hashmix(d, x, hash); - crush_hashmix(y, e, hash); - return hash; -} - - -__u32 crush_hash32(int type, __u32 a) -{ - switch (type) { - case CRUSH_HASH_RJENKINS1: - return crush_hash32_rjenkins1(a); - default: - return 0; - } -} - -__u32 crush_hash32_2(int type, __u32 a, __u32 b) -{ - switch (type) { - case CRUSH_HASH_RJENKINS1: - return crush_hash32_rjenkins1_2(a, b); - default: - return 0; - } -} - -__u32 crush_hash32_3(int type, __u32 a, __u32 b, __u32 c) -{ - switch (type) { - case CRUSH_HASH_RJENKINS1: - return crush_hash32_rjenkins1_3(a, b, c); - default: - return 0; - } -} - -__u32 crush_hash32_4(int type, __u32 a, __u32 b, __u32 c, __u32 d) -{ - switch (type) { - case CRUSH_HASH_RJENKINS1: - return crush_hash32_rjenkins1_4(a, b, c, d); - default: - return 0; - } -} - -__u32 crush_hash32_5(int type, __u32 a, __u32 b, __u32 c, __u32 d, __u32 e) -{ - switch (type) { - case CRUSH_HASH_RJENKINS1: - return crush_hash32_rjenkins1_5(a, b, c, d, e); - default: - return 0; - } -} - -const char *crush_hash_name(int type) -{ - switch (type) { - case CRUSH_HASH_RJENKINS1: - return "rjenkins1"; - default: - return "unknown"; - } -} diff --git a/fs/ceph/crush/hash.h b/fs/ceph/crush/hash.h deleted file mode 100644 index 91e884230d5..00000000000 --- a/fs/ceph/crush/hash.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef CEPH_CRUSH_HASH_H -#define CEPH_CRUSH_HASH_H - -#define CRUSH_HASH_RJENKINS1 0 - -#define CRUSH_HASH_DEFAULT CRUSH_HASH_RJENKINS1 - -extern const char *crush_hash_name(int type); - -extern __u32 crush_hash32(int type, __u32 a); -extern __u32 crush_hash32_2(int type, __u32 a, __u32 b); -extern __u32 crush_hash32_3(int type, __u32 a, __u32 b, __u32 c); -extern __u32 crush_hash32_4(int type, __u32 a, __u32 b, __u32 c, __u32 d); -extern __u32 crush_hash32_5(int type, __u32 a, __u32 b, __u32 c, __u32 d, - __u32 e); - -#endif diff --git a/fs/ceph/crush/mapper.c b/fs/ceph/crush/mapper.c deleted file mode 100644 index a4eec133258..00000000000 --- a/fs/ceph/crush/mapper.c +++ /dev/null @@ -1,609 +0,0 @@ - -#ifdef __KERNEL__ -# include -# include -# include -# include -# ifndef dprintk -# define dprintk(args...) -# endif -#else -# include -# include -# include -# include -# define BUG_ON(x) assert(!(x)) -# define dprintk(args...) /* printf(args) */ -# define kmalloc(x, f) malloc(x) -# define kfree(x) free(x) -#endif - -#include "crush.h" -#include "hash.h" - -/* - * Implement the core CRUSH mapping algorithm. - */ - -/** - * crush_find_rule - find a crush_rule id for a given ruleset, type, and size. - * @map: the crush_map - * @ruleset: the storage ruleset id (user defined) - * @type: storage ruleset type (user defined) - * @size: output set size - */ -int crush_find_rule(struct crush_map *map, int ruleset, int type, int size) -{ - int i; - - for (i = 0; i < map->max_rules; i++) { - if (map->rules[i] && - map->rules[i]->mask.ruleset == ruleset && - map->rules[i]->mask.type == type && - map->rules[i]->mask.min_size <= size && - map->rules[i]->mask.max_size >= size) - return i; - } - return -1; -} - - -/* - * bucket choose methods - * - * For each bucket algorithm, we have a "choose" method that, given a - * crush input @x and replica position (usually, position in output set) @r, - * will produce an item in the bucket. - */ - -/* - * Choose based on a random permutation of the bucket. - * - * We used to use some prime number arithmetic to do this, but it - * wasn't very random, and had some other bad behaviors. Instead, we - * calculate an actual random permutation of the bucket members. - * Since this is expensive, we optimize for the r=0 case, which - * captures the vast majority of calls. - */ -static int bucket_perm_choose(struct crush_bucket *bucket, - int x, int r) -{ - unsigned pr = r % bucket->size; - unsigned i, s; - - /* start a new permutation if @x has changed */ - if (bucket->perm_x != x || bucket->perm_n == 0) { - dprintk("bucket %d new x=%d\n", bucket->id, x); - bucket->perm_x = x; - - /* optimize common r=0 case */ - if (pr == 0) { - s = crush_hash32_3(bucket->hash, x, bucket->id, 0) % - bucket->size; - bucket->perm[0] = s; - bucket->perm_n = 0xffff; /* magic value, see below */ - goto out; - } - - for (i = 0; i < bucket->size; i++) - bucket->perm[i] = i; - bucket->perm_n = 0; - } else if (bucket->perm_n == 0xffff) { - /* clean up after the r=0 case above */ - for (i = 1; i < bucket->size; i++) - bucket->perm[i] = i; - bucket->perm[bucket->perm[0]] = 0; - bucket->perm_n = 1; - } - - /* calculate permutation up to pr */ - for (i = 0; i < bucket->perm_n; i++) - dprintk(" perm_choose have %d: %d\n", i, bucket->perm[i]); - while (bucket->perm_n <= pr) { - unsigned p = bucket->perm_n; - /* no point in swapping the final entry */ - if (p < bucket->size - 1) { - i = crush_hash32_3(bucket->hash, x, bucket->id, p) % - (bucket->size - p); - if (i) { - unsigned t = bucket->perm[p + i]; - bucket->perm[p + i] = bucket->perm[p]; - bucket->perm[p] = t; - } - dprintk(" perm_choose swap %d with %d\n", p, p+i); - } - bucket->perm_n++; - } - for (i = 0; i < bucket->size; i++) - dprintk(" perm_choose %d: %d\n", i, bucket->perm[i]); - - s = bucket->perm[pr]; -out: - dprintk(" perm_choose %d sz=%d x=%d r=%d (%d) s=%d\n", bucket->id, - bucket->size, x, r, pr, s); - return bucket->items[s]; -} - -/* uniform */ -static int bucket_uniform_choose(struct crush_bucket_uniform *bucket, - int x, int r) -{ - return bucket_perm_choose(&bucket->h, x, r); -} - -/* list */ -static int bucket_list_choose(struct crush_bucket_list *bucket, - int x, int r) -{ - int i; - - for (i = bucket->h.size-1; i >= 0; i--) { - __u64 w = crush_hash32_4(bucket->h.hash,x, bucket->h.items[i], - r, bucket->h.id); - w &= 0xffff; - dprintk("list_choose i=%d x=%d r=%d item %d weight %x " - "sw %x rand %llx", - i, x, r, bucket->h.items[i], bucket->item_weights[i], - bucket->sum_weights[i], w); - w *= bucket->sum_weights[i]; - w = w >> 16; - /*dprintk(" scaled %llx\n", w);*/ - if (w < bucket->item_weights[i]) - return bucket->h.items[i]; - } - - BUG_ON(1); - return 0; -} - - -/* (binary) tree */ -static int height(int n) -{ - int h = 0; - while ((n & 1) == 0) { - h++; - n = n >> 1; - } - return h; -} - -static int left(int x) -{ - int h = height(x); - return x - (1 << (h-1)); -} - -static int right(int x) -{ - int h = height(x); - return x + (1 << (h-1)); -} - -static int terminal(int x) -{ - return x & 1; -} - -static int bucket_tree_choose(struct crush_bucket_tree *bucket, - int x, int r) -{ - int n, l; - __u32 w; - __u64 t; - - /* start at root */ - n = bucket->num_nodes >> 1; - - while (!terminal(n)) { - /* pick point in [0, w) */ - w = bucket->node_weights[n]; - t = (__u64)crush_hash32_4(bucket->h.hash, x, n, r, - bucket->h.id) * (__u64)w; - t = t >> 32; - - /* descend to the left or right? */ - l = left(n); - if (t < bucket->node_weights[l]) - n = l; - else - n = right(n); - } - - return bucket->h.items[n >> 1]; -} - - -/* straw */ - -static int bucket_straw_choose(struct crush_bucket_straw *bucket, - int x, int r) -{ - int i; - int high = 0; - __u64 high_draw = 0; - __u64 draw; - - for (i = 0; i < bucket->h.size; i++) { - draw = crush_hash32_3(bucket->h.hash, x, bucket->h.items[i], r); - draw &= 0xffff; - draw *= bucket->straws[i]; - if (i == 0 || draw > high_draw) { - high = i; - high_draw = draw; - } - } - return bucket->h.items[high]; -} - -static int crush_bucket_choose(struct crush_bucket *in, int x, int r) -{ - dprintk(" crush_bucket_choose %d x=%d r=%d\n", in->id, x, r); - switch (in->alg) { - case CRUSH_BUCKET_UNIFORM: - return bucket_uniform_choose((struct crush_bucket_uniform *)in, - x, r); - case CRUSH_BUCKET_LIST: - return bucket_list_choose((struct crush_bucket_list *)in, - x, r); - case CRUSH_BUCKET_TREE: - return bucket_tree_choose((struct crush_bucket_tree *)in, - x, r); - case CRUSH_BUCKET_STRAW: - return bucket_straw_choose((struct crush_bucket_straw *)in, - x, r); - default: - BUG_ON(1); - return in->items[0]; - } -} - -/* - * true if device is marked "out" (failed, fully offloaded) - * of the cluster - */ -static int is_out(struct crush_map *map, __u32 *weight, int item, int x) -{ - if (weight[item] >= 0x10000) - return 0; - if (weight[item] == 0) - return 1; - if ((crush_hash32_2(CRUSH_HASH_RJENKINS1, x, item) & 0xffff) - < weight[item]) - return 0; - return 1; -} - -/** - * crush_choose - choose numrep distinct items of given type - * @map: the crush_map - * @bucket: the bucket we are choose an item from - * @x: crush input value - * @numrep: the number of items to choose - * @type: the type of item to choose - * @out: pointer to output vector - * @outpos: our position in that vector - * @firstn: true if choosing "first n" items, false if choosing "indep" - * @recurse_to_leaf: true if we want one device under each item of given type - * @out2: second output vector for leaf items (if @recurse_to_leaf) - */ -static int crush_choose(struct crush_map *map, - struct crush_bucket *bucket, - __u32 *weight, - int x, int numrep, int type, - int *out, int outpos, - int firstn, int recurse_to_leaf, - int *out2) -{ - int rep; - int ftotal, flocal; - int retry_descent, retry_bucket, skip_rep; - struct crush_bucket *in = bucket; - int r; - int i; - int item = 0; - int itemtype; - int collide, reject; - const int orig_tries = 5; /* attempts before we fall back to search */ - - dprintk("CHOOSE%s bucket %d x %d outpos %d numrep %d\n", recurse_to_leaf ? "_LEAF" : "", - bucket->id, x, outpos, numrep); - - for (rep = outpos; rep < numrep; rep++) { - /* keep trying until we get a non-out, non-colliding item */ - ftotal = 0; - skip_rep = 0; - do { - retry_descent = 0; - in = bucket; /* initial bucket */ - - /* choose through intervening buckets */ - flocal = 0; - do { - collide = 0; - retry_bucket = 0; - r = rep; - if (in->alg == CRUSH_BUCKET_UNIFORM) { - /* be careful */ - if (firstn || numrep >= in->size) - /* r' = r + f_total */ - r += ftotal; - else if (in->size % numrep == 0) - /* r'=r+(n+1)*f_local */ - r += (numrep+1) * - (flocal+ftotal); - else - /* r' = r + n*f_local */ - r += numrep * (flocal+ftotal); - } else { - if (firstn) - /* r' = r + f_total */ - r += ftotal; - else - /* r' = r + n*f_local */ - r += numrep * (flocal+ftotal); - } - - /* bucket choose */ - if (in->size == 0) { - reject = 1; - goto reject; - } - if (flocal >= (in->size>>1) && - flocal > orig_tries) - item = bucket_perm_choose(in, x, r); - else - item = crush_bucket_choose(in, x, r); - BUG_ON(item >= map->max_devices); - - /* desired type? */ - if (item < 0) - itemtype = map->buckets[-1-item]->type; - else - itemtype = 0; - dprintk(" item %d type %d\n", item, itemtype); - - /* keep going? */ - if (itemtype != type) { - BUG_ON(item >= 0 || - (-1-item) >= map->max_buckets); - in = map->buckets[-1-item]; - retry_bucket = 1; - continue; - } - - /* collision? */ - for (i = 0; i < outpos; i++) { - if (out[i] == item) { - collide = 1; - break; - } - } - - reject = 0; - if (recurse_to_leaf) { - if (item < 0) { - if (crush_choose(map, - map->buckets[-1-item], - weight, - x, outpos+1, 0, - out2, outpos, - firstn, 0, - NULL) <= outpos) - /* didn't get leaf */ - reject = 1; - } else { - /* we already have a leaf! */ - out2[outpos] = item; - } - } - - if (!reject) { - /* out? */ - if (itemtype == 0) - reject = is_out(map, weight, - item, x); - else - reject = 0; - } - -reject: - if (reject || collide) { - ftotal++; - flocal++; - - if (collide && flocal < 3) - /* retry locally a few times */ - retry_bucket = 1; - else if (flocal < in->size + orig_tries) - /* exhaustive bucket search */ - retry_bucket = 1; - else if (ftotal < 20) - /* then retry descent */ - retry_descent = 1; - else - /* else give up */ - skip_rep = 1; - dprintk(" reject %d collide %d " - "ftotal %d flocal %d\n", - reject, collide, ftotal, - flocal); - } - } while (retry_bucket); - } while (retry_descent); - - if (skip_rep) { - dprintk("skip rep\n"); - continue; - } - - dprintk("CHOOSE got %d\n", item); - out[outpos] = item; - outpos++; - } - - dprintk("CHOOSE returns %d\n", outpos); - return outpos; -} - - -/** - * crush_do_rule - calculate a mapping with the given input and rule - * @map: the crush_map - * @ruleno: the rule id - * @x: hash input - * @result: pointer to result vector - * @result_max: maximum result size - * @force: force initial replica choice; -1 for none - */ -int crush_do_rule(struct crush_map *map, - int ruleno, int x, int *result, int result_max, - int force, __u32 *weight) -{ - int result_len; - int force_context[CRUSH_MAX_DEPTH]; - int force_pos = -1; - int a[CRUSH_MAX_SET]; - int b[CRUSH_MAX_SET]; - int c[CRUSH_MAX_SET]; - int recurse_to_leaf; - int *w; - int wsize = 0; - int *o; - int osize; - int *tmp; - struct crush_rule *rule; - int step; - int i, j; - int numrep; - int firstn; - int rc = -1; - - BUG_ON(ruleno >= map->max_rules); - - rule = map->rules[ruleno]; - result_len = 0; - w = a; - o = b; - - /* - * determine hierarchical context of force, if any. note - * that this may or may not correspond to the specific types - * referenced by the crush rule. - */ - if (force >= 0) { - if (force >= map->max_devices || - map->device_parents[force] == 0) { - /*dprintk("CRUSH: forcefed device dne\n");*/ - rc = -1; /* force fed device dne */ - goto out; - } - if (!is_out(map, weight, force, x)) { - while (1) { - force_context[++force_pos] = force; - if (force >= 0) - force = map->device_parents[force]; - else - force = map->bucket_parents[-1-force]; - if (force == 0) - break; - } - } - } - - for (step = 0; step < rule->len; step++) { - firstn = 0; - switch (rule->steps[step].op) { - case CRUSH_RULE_TAKE: - w[0] = rule->steps[step].arg1; - if (force_pos >= 0) { - BUG_ON(force_context[force_pos] != w[0]); - force_pos--; - } - wsize = 1; - break; - - case CRUSH_RULE_CHOOSE_LEAF_FIRSTN: - case CRUSH_RULE_CHOOSE_FIRSTN: - firstn = 1; - case CRUSH_RULE_CHOOSE_LEAF_INDEP: - case CRUSH_RULE_CHOOSE_INDEP: - BUG_ON(wsize == 0); - - recurse_to_leaf = - rule->steps[step].op == - CRUSH_RULE_CHOOSE_LEAF_FIRSTN || - rule->steps[step].op == - CRUSH_RULE_CHOOSE_LEAF_INDEP; - - /* reset output */ - osize = 0; - - for (i = 0; i < wsize; i++) { - /* - * see CRUSH_N, CRUSH_N_MINUS macros. - * basically, numrep <= 0 means relative to - * the provided result_max - */ - numrep = rule->steps[step].arg1; - if (numrep <= 0) { - numrep += result_max; - if (numrep <= 0) - continue; - } - j = 0; - if (osize == 0 && force_pos >= 0) { - /* skip any intermediate types */ - while (force_pos && - force_context[force_pos] < 0 && - rule->steps[step].arg2 != - map->buckets[-1 - - force_context[force_pos]]->type) - force_pos--; - o[osize] = force_context[force_pos]; - if (recurse_to_leaf) - c[osize] = force_context[0]; - j++; - force_pos--; - } - osize += crush_choose(map, - map->buckets[-1-w[i]], - weight, - x, numrep, - rule->steps[step].arg2, - o+osize, j, - firstn, - recurse_to_leaf, c+osize); - } - - if (recurse_to_leaf) - /* copy final _leaf_ values to output set */ - memcpy(o, c, osize*sizeof(*o)); - - /* swap t and w arrays */ - tmp = o; - o = w; - w = tmp; - wsize = osize; - break; - - - case CRUSH_RULE_EMIT: - for (i = 0; i < wsize && result_len < result_max; i++) { - result[result_len] = w[i]; - result_len++; - } - wsize = 0; - break; - - default: - BUG_ON(1); - } - } - rc = result_len; - -out: - return rc; -} - - diff --git a/fs/ceph/crush/mapper.h b/fs/ceph/crush/mapper.h deleted file mode 100644 index c46b99c18bb..00000000000 --- a/fs/ceph/crush/mapper.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef CEPH_CRUSH_MAPPER_H -#define CEPH_CRUSH_MAPPER_H - -/* - * CRUSH functions for find rules and then mapping an input to an - * output set. - * - * LGPL2 - */ - -#include "crush.h" - -extern int crush_find_rule(struct crush_map *map, int pool, int type, int size); -extern int crush_do_rule(struct crush_map *map, - int ruleno, - int x, int *result, int result_max, - int forcefeed, /* -1 for none */ - __u32 *weights); - -#endif diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c deleted file mode 100644 index a3e627f6329..00000000000 --- a/fs/ceph/crypto.c +++ /dev/null @@ -1,412 +0,0 @@ - -#include "ceph_debug.h" - -#include -#include -#include -#include - -#include "crypto.h" -#include "decode.h" - -int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end) -{ - if (*p + sizeof(u16) + sizeof(key->created) + - sizeof(u16) + key->len > end) - return -ERANGE; - ceph_encode_16(p, key->type); - ceph_encode_copy(p, &key->created, sizeof(key->created)); - ceph_encode_16(p, key->len); - ceph_encode_copy(p, key->key, key->len); - return 0; -} - -int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end) -{ - ceph_decode_need(p, end, 2*sizeof(u16) + sizeof(key->created), bad); - key->type = ceph_decode_16(p); - ceph_decode_copy(p, &key->created, sizeof(key->created)); - key->len = ceph_decode_16(p); - ceph_decode_need(p, end, key->len, bad); - key->key = kmalloc(key->len, GFP_NOFS); - if (!key->key) - return -ENOMEM; - ceph_decode_copy(p, key->key, key->len); - return 0; - -bad: - dout("failed to decode crypto key\n"); - return -EINVAL; -} - -int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *inkey) -{ - int inlen = strlen(inkey); - int blen = inlen * 3 / 4; - void *buf, *p; - int ret; - - dout("crypto_key_unarmor %s\n", inkey); - buf = kmalloc(blen, GFP_NOFS); - if (!buf) - return -ENOMEM; - blen = ceph_unarmor(buf, inkey, inkey+inlen); - if (blen < 0) { - kfree(buf); - return blen; - } - - p = buf; - ret = ceph_crypto_key_decode(key, &p, p + blen); - kfree(buf); - if (ret) - return ret; - dout("crypto_key_unarmor key %p type %d len %d\n", key, - key->type, key->len); - return 0; -} - - - -#define AES_KEY_SIZE 16 - -static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void) -{ - return crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); -} - -static const u8 *aes_iv = (u8 *)CEPH_AES_IV; - -static int ceph_aes_encrypt(const void *key, int key_len, - void *dst, size_t *dst_len, - const void *src, size_t src_len) -{ - struct scatterlist sg_in[2], sg_out[1]; - struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); - struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 }; - int ret; - void *iv; - int ivsize; - size_t zero_padding = (0x10 - (src_len & 0x0f)); - char pad[16]; - - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - - memset(pad, zero_padding, zero_padding); - - *dst_len = src_len + zero_padding; - - crypto_blkcipher_setkey((void *)tfm, key, key_len); - sg_init_table(sg_in, 2); - sg_set_buf(&sg_in[0], src, src_len); - sg_set_buf(&sg_in[1], pad, zero_padding); - sg_init_table(sg_out, 1); - sg_set_buf(sg_out, dst, *dst_len); - iv = crypto_blkcipher_crt(tfm)->iv; - ivsize = crypto_blkcipher_ivsize(tfm); - - memcpy(iv, aes_iv, ivsize); - /* - print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1, - key, key_len, 1); - print_hex_dump(KERN_ERR, "enc src: ", DUMP_PREFIX_NONE, 16, 1, - src, src_len, 1); - print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1, - pad, zero_padding, 1); - */ - ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, - src_len + zero_padding); - crypto_free_blkcipher(tfm); - if (ret < 0) - pr_err("ceph_aes_crypt failed %d\n", ret); - /* - print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1, - dst, *dst_len, 1); - */ - return 0; -} - -static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, - size_t *dst_len, - const void *src1, size_t src1_len, - const void *src2, size_t src2_len) -{ - struct scatterlist sg_in[3], sg_out[1]; - struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); - struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 }; - int ret; - void *iv; - int ivsize; - size_t zero_padding = (0x10 - ((src1_len + src2_len) & 0x0f)); - char pad[16]; - - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - - memset(pad, zero_padding, zero_padding); - - *dst_len = src1_len + src2_len + zero_padding; - - crypto_blkcipher_setkey((void *)tfm, key, key_len); - sg_init_table(sg_in, 3); - sg_set_buf(&sg_in[0], src1, src1_len); - sg_set_buf(&sg_in[1], src2, src2_len); - sg_set_buf(&sg_in[2], pad, zero_padding); - sg_init_table(sg_out, 1); - sg_set_buf(sg_out, dst, *dst_len); - iv = crypto_blkcipher_crt(tfm)->iv; - ivsize = crypto_blkcipher_ivsize(tfm); - - memcpy(iv, aes_iv, ivsize); - /* - print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1, - key, key_len, 1); - print_hex_dump(KERN_ERR, "enc src1: ", DUMP_PREFIX_NONE, 16, 1, - src1, src1_len, 1); - print_hex_dump(KERN_ERR, "enc src2: ", DUMP_PREFIX_NONE, 16, 1, - src2, src2_len, 1); - print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1, - pad, zero_padding, 1); - */ - ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, - src1_len + src2_len + zero_padding); - crypto_free_blkcipher(tfm); - if (ret < 0) - pr_err("ceph_aes_crypt2 failed %d\n", ret); - /* - print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1, - dst, *dst_len, 1); - */ - return 0; -} - -static int ceph_aes_decrypt(const void *key, int key_len, - void *dst, size_t *dst_len, - const void *src, size_t src_len) -{ - struct scatterlist sg_in[1], sg_out[2]; - struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); - struct blkcipher_desc desc = { .tfm = tfm }; - char pad[16]; - void *iv; - int ivsize; - int ret; - int last_byte; - - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - - crypto_blkcipher_setkey((void *)tfm, key, key_len); - sg_init_table(sg_in, 1); - sg_init_table(sg_out, 2); - sg_set_buf(sg_in, src, src_len); - sg_set_buf(&sg_out[0], dst, *dst_len); - sg_set_buf(&sg_out[1], pad, sizeof(pad)); - - iv = crypto_blkcipher_crt(tfm)->iv; - ivsize = crypto_blkcipher_ivsize(tfm); - - memcpy(iv, aes_iv, ivsize); - - /* - print_hex_dump(KERN_ERR, "dec key: ", DUMP_PREFIX_NONE, 16, 1, - key, key_len, 1); - print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1, - src, src_len, 1); - */ - - ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len); - crypto_free_blkcipher(tfm); - if (ret < 0) { - pr_err("ceph_aes_decrypt failed %d\n", ret); - return ret; - } - - if (src_len <= *dst_len) - last_byte = ((char *)dst)[src_len - 1]; - else - last_byte = pad[src_len - *dst_len - 1]; - if (last_byte <= 16 && src_len >= last_byte) { - *dst_len = src_len - last_byte; - } else { - pr_err("ceph_aes_decrypt got bad padding %d on src len %d\n", - last_byte, (int)src_len); - return -EPERM; /* bad padding */ - } - /* - print_hex_dump(KERN_ERR, "dec out: ", DUMP_PREFIX_NONE, 16, 1, - dst, *dst_len, 1); - */ - return 0; -} - -static int ceph_aes_decrypt2(const void *key, int key_len, - void *dst1, size_t *dst1_len, - void *dst2, size_t *dst2_len, - const void *src, size_t src_len) -{ - struct scatterlist sg_in[1], sg_out[3]; - struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); - struct blkcipher_desc desc = { .tfm = tfm }; - char pad[16]; - void *iv; - int ivsize; - int ret; - int last_byte; - - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - - sg_init_table(sg_in, 1); - sg_set_buf(sg_in, src, src_len); - sg_init_table(sg_out, 3); - sg_set_buf(&sg_out[0], dst1, *dst1_len); - sg_set_buf(&sg_out[1], dst2, *dst2_len); - sg_set_buf(&sg_out[2], pad, sizeof(pad)); - - crypto_blkcipher_setkey((void *)tfm, key, key_len); - iv = crypto_blkcipher_crt(tfm)->iv; - ivsize = crypto_blkcipher_ivsize(tfm); - - memcpy(iv, aes_iv, ivsize); - - /* - print_hex_dump(KERN_ERR, "dec key: ", DUMP_PREFIX_NONE, 16, 1, - key, key_len, 1); - print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1, - src, src_len, 1); - */ - - ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len); - crypto_free_blkcipher(tfm); - if (ret < 0) { - pr_err("ceph_aes_decrypt failed %d\n", ret); - return ret; - } - - if (src_len <= *dst1_len) - last_byte = ((char *)dst1)[src_len - 1]; - else if (src_len <= *dst1_len + *dst2_len) - last_byte = ((char *)dst2)[src_len - *dst1_len - 1]; - else - last_byte = pad[src_len - *dst1_len - *dst2_len - 1]; - if (last_byte <= 16 && src_len >= last_byte) { - src_len -= last_byte; - } else { - pr_err("ceph_aes_decrypt got bad padding %d on src len %d\n", - last_byte, (int)src_len); - return -EPERM; /* bad padding */ - } - - if (src_len < *dst1_len) { - *dst1_len = src_len; - *dst2_len = 0; - } else { - *dst2_len = src_len - *dst1_len; - } - /* - print_hex_dump(KERN_ERR, "dec out1: ", DUMP_PREFIX_NONE, 16, 1, - dst1, *dst1_len, 1); - print_hex_dump(KERN_ERR, "dec out2: ", DUMP_PREFIX_NONE, 16, 1, - dst2, *dst2_len, 1); - */ - - return 0; -} - - -int ceph_decrypt(struct ceph_crypto_key *secret, void *dst, size_t *dst_len, - const void *src, size_t src_len) -{ - switch (secret->type) { - case CEPH_CRYPTO_NONE: - if (*dst_len < src_len) - return -ERANGE; - memcpy(dst, src, src_len); - *dst_len = src_len; - return 0; - - case CEPH_CRYPTO_AES: - return ceph_aes_decrypt(secret->key, secret->len, dst, - dst_len, src, src_len); - - default: - return -EINVAL; - } -} - -int ceph_decrypt2(struct ceph_crypto_key *secret, - void *dst1, size_t *dst1_len, - void *dst2, size_t *dst2_len, - const void *src, size_t src_len) -{ - size_t t; - - switch (secret->type) { - case CEPH_CRYPTO_NONE: - if (*dst1_len + *dst2_len < src_len) - return -ERANGE; - t = min(*dst1_len, src_len); - memcpy(dst1, src, t); - *dst1_len = t; - src += t; - src_len -= t; - if (src_len) { - t = min(*dst2_len, src_len); - memcpy(dst2, src, t); - *dst2_len = t; - } - return 0; - - case CEPH_CRYPTO_AES: - return ceph_aes_decrypt2(secret->key, secret->len, - dst1, dst1_len, dst2, dst2_len, - src, src_len); - - default: - return -EINVAL; - } -} - -int ceph_encrypt(struct ceph_crypto_key *secret, void *dst, size_t *dst_len, - const void *src, size_t src_len) -{ - switch (secret->type) { - case CEPH_CRYPTO_NONE: - if (*dst_len < src_len) - return -ERANGE; - memcpy(dst, src, src_len); - *dst_len = src_len; - return 0; - - case CEPH_CRYPTO_AES: - return ceph_aes_encrypt(secret->key, secret->len, dst, - dst_len, src, src_len); - - default: - return -EINVAL; - } -} - -int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len, - const void *src1, size_t src1_len, - const void *src2, size_t src2_len) -{ - switch (secret->type) { - case CEPH_CRYPTO_NONE: - if (*dst_len < src1_len + src2_len) - return -ERANGE; - memcpy(dst, src1, src1_len); - memcpy(dst + src1_len, src2, src2_len); - *dst_len = src1_len + src2_len; - return 0; - - case CEPH_CRYPTO_AES: - return ceph_aes_encrypt2(secret->key, secret->len, dst, dst_len, - src1, src1_len, src2, src2_len); - - default: - return -EINVAL; - } -} diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h deleted file mode 100644 index bdf38607323..00000000000 --- a/fs/ceph/crypto.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _FS_CEPH_CRYPTO_H -#define _FS_CEPH_CRYPTO_H - -#include "types.h" -#include "buffer.h" - -/* - * cryptographic secret - */ -struct ceph_crypto_key { - int type; - struct ceph_timespec created; - int len; - void *key; -}; - -static inline void ceph_crypto_key_destroy(struct ceph_crypto_key *key) -{ - kfree(key->key); -} - -extern int ceph_crypto_key_encode(struct ceph_crypto_key *key, - void **p, void *end); -extern int ceph_crypto_key_decode(struct ceph_crypto_key *key, - void **p, void *end); -extern int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in); - -/* crypto.c */ -extern int ceph_decrypt(struct ceph_crypto_key *secret, - void *dst, size_t *dst_len, - const void *src, size_t src_len); -extern int ceph_encrypt(struct ceph_crypto_key *secret, - void *dst, size_t *dst_len, - const void *src, size_t src_len); -extern int ceph_decrypt2(struct ceph_crypto_key *secret, - void *dst1, size_t *dst1_len, - void *dst2, size_t *dst2_len, - const void *src, size_t src_len); -extern int ceph_encrypt2(struct ceph_crypto_key *secret, - void *dst, size_t *dst_len, - const void *src1, size_t src1_len, - const void *src2, size_t src2_len); - -/* armor.c */ -extern int ceph_armor(char *dst, const char *src, const char *end); -extern int ceph_unarmor(char *dst, const char *src, const char *end); - -#endif diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 6fd8b20a861..94fe1d284c3 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -1,4 +1,4 @@ -#include "ceph_debug.h" +#include #include #include @@ -7,143 +7,48 @@ #include #include -#include "super.h" -#include "mds_client.h" -#include "mon_client.h" -#include "auth.h" +#include +#include +#include +#include #ifdef CONFIG_DEBUG_FS -/* - * Implement /sys/kernel/debug/ceph fun - * - * /sys/kernel/debug/ceph/client* - an instance of the ceph client - * .../osdmap - current osdmap - * .../mdsmap - current mdsmap - * .../monmap - current monmap - * .../osdc - active osd requests - * .../mdsc - active mds requests - * .../monc - mon client state - * .../dentry_lru - dump contents of dentry lru - * .../caps - expose cap (reservation) stats - * .../bdi - symlink to ../../bdi/something - */ - -static struct dentry *ceph_debugfs_dir; - -static int monmap_show(struct seq_file *s, void *p) -{ - int i; - struct ceph_client *client = s->private; - - if (client->monc.monmap == NULL) - return 0; - - seq_printf(s, "epoch %d\n", client->monc.monmap->epoch); - for (i = 0; i < client->monc.monmap->num_mon; i++) { - struct ceph_entity_inst *inst = - &client->monc.monmap->mon_inst[i]; - - seq_printf(s, "\t%s%lld\t%s\n", - ENTITY_NAME(inst->name), - pr_addr(&inst->addr.in_addr)); - } - return 0; -} +#include "super.h" +#include "mds_client.h" static int mdsmap_show(struct seq_file *s, void *p) { int i; - struct ceph_client *client = s->private; + struct ceph_fs_client *fsc = s->private; - if (client->mdsc.mdsmap == NULL) + if (fsc->mdsc == NULL || fsc->mdsc->mdsmap == NULL) return 0; - seq_printf(s, "epoch %d\n", client->mdsc.mdsmap->m_epoch); - seq_printf(s, "root %d\n", client->mdsc.mdsmap->m_root); + seq_printf(s, "epoch %d\n", fsc->mdsc->mdsmap->m_epoch); + seq_printf(s, "root %d\n", fsc->mdsc->mdsmap->m_root); seq_printf(s, "session_timeout %d\n", - client->mdsc.mdsmap->m_session_timeout); + fsc->mdsc->mdsmap->m_session_timeout); seq_printf(s, "session_autoclose %d\n", - client->mdsc.mdsmap->m_session_autoclose); - for (i = 0; i < client->mdsc.mdsmap->m_max_mds; i++) { + fsc->mdsc->mdsmap->m_session_autoclose); + for (i = 0; i < fsc->mdsc->mdsmap->m_max_mds; i++) { struct ceph_entity_addr *addr = - &client->mdsc.mdsmap->m_info[i].addr; - int state = client->mdsc.mdsmap->m_info[i].state; + &fsc->mdsc->mdsmap->m_info[i].addr; + int state = fsc->mdsc->mdsmap->m_info[i].state; - seq_printf(s, "\tmds%d\t%s\t(%s)\n", i, pr_addr(&addr->in_addr), + seq_printf(s, "\tmds%d\t%s\t(%s)\n", i, + ceph_pr_addr(&addr->in_addr), ceph_mds_state_name(state)); } return 0; } -static int osdmap_show(struct seq_file *s, void *p) -{ - int i; - struct ceph_client *client = s->private; - struct rb_node *n; - - if (client->osdc.osdmap == NULL) - return 0; - seq_printf(s, "epoch %d\n", client->osdc.osdmap->epoch); - seq_printf(s, "flags%s%s\n", - (client->osdc.osdmap->flags & CEPH_OSDMAP_NEARFULL) ? - " NEARFULL" : "", - (client->osdc.osdmap->flags & CEPH_OSDMAP_FULL) ? - " FULL" : ""); - for (n = rb_first(&client->osdc.osdmap->pg_pools); n; n = rb_next(n)) { - struct ceph_pg_pool_info *pool = - rb_entry(n, struct ceph_pg_pool_info, node); - seq_printf(s, "pg_pool %d pg_num %d / %d, lpg_num %d / %d\n", - pool->id, pool->v.pg_num, pool->pg_num_mask, - pool->v.lpg_num, pool->lpg_num_mask); - } - for (i = 0; i < client->osdc.osdmap->max_osd; i++) { - struct ceph_entity_addr *addr = - &client->osdc.osdmap->osd_addr[i]; - int state = client->osdc.osdmap->osd_state[i]; - char sb[64]; - - seq_printf(s, "\tosd%d\t%s\t%3d%%\t(%s)\n", - i, pr_addr(&addr->in_addr), - ((client->osdc.osdmap->osd_weight[i]*100) >> 16), - ceph_osdmap_state_str(sb, sizeof(sb), state)); - } - return 0; -} - -static int monc_show(struct seq_file *s, void *p) -{ - struct ceph_client *client = s->private; - struct ceph_mon_generic_request *req; - struct ceph_mon_client *monc = &client->monc; - struct rb_node *rp; - - mutex_lock(&monc->mutex); - - if (monc->have_mdsmap) - seq_printf(s, "have mdsmap %u\n", (unsigned)monc->have_mdsmap); - if (monc->have_osdmap) - seq_printf(s, "have osdmap %u\n", (unsigned)monc->have_osdmap); - if (monc->want_next_osdmap) - seq_printf(s, "want next osdmap\n"); - - for (rp = rb_first(&monc->generic_request_tree); rp; rp = rb_next(rp)) { - __u16 op; - req = rb_entry(rp, struct ceph_mon_generic_request, node); - op = le16_to_cpu(req->request->hdr.type); - if (op == CEPH_MSG_STATFS) - seq_printf(s, "%lld statfs\n", req->tid); - else - seq_printf(s, "%lld unknown\n", req->tid); - } - - mutex_unlock(&monc->mutex); - return 0; -} - +/* + * mdsc debugfs + */ static int mdsc_show(struct seq_file *s, void *p) { - struct ceph_client *client = s->private; - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = s->private; + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; struct rb_node *rp; int pathlen; @@ -214,61 +119,12 @@ static int mdsc_show(struct seq_file *s, void *p) return 0; } -static int osdc_show(struct seq_file *s, void *pp) -{ - struct ceph_client *client = s->private; - struct ceph_osd_client *osdc = &client->osdc; - struct rb_node *p; - - mutex_lock(&osdc->request_mutex); - for (p = rb_first(&osdc->requests); p; p = rb_next(p)) { - struct ceph_osd_request *req; - struct ceph_osd_request_head *head; - struct ceph_osd_op *op; - int num_ops; - int opcode, olen; - int i; - - req = rb_entry(p, struct ceph_osd_request, r_node); - - seq_printf(s, "%lld\tosd%d\t%d.%x\t", req->r_tid, - req->r_osd ? req->r_osd->o_osd : -1, - le32_to_cpu(req->r_pgid.pool), - le16_to_cpu(req->r_pgid.ps)); - - head = req->r_request->front.iov_base; - op = (void *)(head + 1); - - num_ops = le16_to_cpu(head->num_ops); - olen = le32_to_cpu(head->object_len); - seq_printf(s, "%.*s", olen, - (const char *)(head->ops + num_ops)); - - if (req->r_reassert_version.epoch) - seq_printf(s, "\t%u'%llu", - (unsigned)le32_to_cpu(req->r_reassert_version.epoch), - le64_to_cpu(req->r_reassert_version.version)); - else - seq_printf(s, "\t"); - - for (i = 0; i < num_ops; i++) { - opcode = le16_to_cpu(op->op); - seq_printf(s, "\t%s", ceph_osd_op_name(opcode)); - op++; - } - - seq_printf(s, "\n"); - } - mutex_unlock(&osdc->request_mutex); - return 0; -} - static int caps_show(struct seq_file *s, void *p) { - struct ceph_client *client = s->private; + struct ceph_fs_client *fsc = s->private; int total, avail, used, reserved, min; - ceph_reservation_status(client, &total, &avail, &used, &reserved, &min); + ceph_reservation_status(fsc, &total, &avail, &used, &reserved, &min); seq_printf(s, "total\t\t%d\n" "avail\t\t%d\n" "used\t\t%d\n" @@ -280,8 +136,8 @@ static int caps_show(struct seq_file *s, void *p) static int dentry_lru_show(struct seq_file *s, void *ptr) { - struct ceph_client *client = s->private; - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = s->private; + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_dentry_info *di; spin_lock(&mdsc->dentry_lru_lock); @@ -295,199 +151,124 @@ static int dentry_lru_show(struct seq_file *s, void *ptr) return 0; } -#define DEFINE_SHOW_FUNC(name) \ -static int name##_open(struct inode *inode, struct file *file) \ -{ \ - struct seq_file *sf; \ - int ret; \ - \ - ret = single_open(file, name, NULL); \ - sf = file->private_data; \ - sf->private = inode->i_private; \ - return ret; \ -} \ - \ -static const struct file_operations name##_fops = { \ - .open = name##_open, \ - .read = seq_read, \ - .llseek = seq_lseek, \ - .release = single_release, \ -}; - -DEFINE_SHOW_FUNC(monmap_show) -DEFINE_SHOW_FUNC(mdsmap_show) -DEFINE_SHOW_FUNC(osdmap_show) -DEFINE_SHOW_FUNC(monc_show) -DEFINE_SHOW_FUNC(mdsc_show) -DEFINE_SHOW_FUNC(osdc_show) -DEFINE_SHOW_FUNC(dentry_lru_show) -DEFINE_SHOW_FUNC(caps_show) +CEPH_DEFINE_SHOW_FUNC(mdsmap_show) +CEPH_DEFINE_SHOW_FUNC(mdsc_show) +CEPH_DEFINE_SHOW_FUNC(caps_show) +CEPH_DEFINE_SHOW_FUNC(dentry_lru_show) + +/* + * debugfs + */ static int congestion_kb_set(void *data, u64 val) { - struct ceph_client *client = (struct ceph_client *)data; - - if (client) - client->mount_args->congestion_kb = (int)val; + struct ceph_fs_client *fsc = (struct ceph_fs_client *)data; + fsc->mount_options->congestion_kb = (int)val; return 0; } static int congestion_kb_get(void *data, u64 *val) { - struct ceph_client *client = (struct ceph_client *)data; - - if (client) - *val = (u64)client->mount_args->congestion_kb; + struct ceph_fs_client *fsc = (struct ceph_fs_client *)data; + *val = (u64)fsc->mount_options->congestion_kb; return 0; } - DEFINE_SIMPLE_ATTRIBUTE(congestion_kb_fops, congestion_kb_get, congestion_kb_set, "%llu\n"); -int __init ceph_debugfs_init(void) -{ - ceph_debugfs_dir = debugfs_create_dir("ceph", NULL); - if (!ceph_debugfs_dir) - return -ENOMEM; - return 0; -} -void ceph_debugfs_cleanup(void) +void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc) { - debugfs_remove(ceph_debugfs_dir); + dout("ceph_fs_debugfs_cleanup\n"); + debugfs_remove(fsc->debugfs_bdi); + debugfs_remove(fsc->debugfs_congestion_kb); + debugfs_remove(fsc->debugfs_mdsmap); + debugfs_remove(fsc->debugfs_caps); + debugfs_remove(fsc->debugfs_mdsc); + debugfs_remove(fsc->debugfs_dentry_lru); } -int ceph_debugfs_client_init(struct ceph_client *client) +int ceph_fs_debugfs_init(struct ceph_fs_client *fsc) { - int ret = 0; - char name[80]; - - snprintf(name, sizeof(name), "%pU.client%lld", &client->fsid, - client->monc.auth->global_id); - - client->debugfs_dir = debugfs_create_dir(name, ceph_debugfs_dir); - if (!client->debugfs_dir) - goto out; + char name[100]; + int err = -ENOMEM; - client->monc.debugfs_file = debugfs_create_file("monc", - 0600, - client->debugfs_dir, - client, - &monc_show_fops); - if (!client->monc.debugfs_file) - goto out; - - client->mdsc.debugfs_file = debugfs_create_file("mdsc", - 0600, - client->debugfs_dir, - client, - &mdsc_show_fops); - if (!client->mdsc.debugfs_file) + dout("ceph_fs_debugfs_init\n"); + fsc->debugfs_congestion_kb = + debugfs_create_file("writeback_congestion_kb", + 0600, + fsc->client->debugfs_dir, + fsc, + &congestion_kb_fops); + if (!fsc->debugfs_congestion_kb) goto out; - client->osdc.debugfs_file = debugfs_create_file("osdc", - 0600, - client->debugfs_dir, - client, - &osdc_show_fops); - if (!client->osdc.debugfs_file) - goto out; + dout("a\n"); - client->debugfs_monmap = debugfs_create_file("monmap", - 0600, - client->debugfs_dir, - client, - &monmap_show_fops); - if (!client->debugfs_monmap) + snprintf(name, sizeof(name), "../../bdi/%s", + dev_name(fsc->backing_dev_info.dev)); + fsc->debugfs_bdi = + debugfs_create_symlink("bdi", + fsc->client->debugfs_dir, + name); + if (!fsc->debugfs_bdi) goto out; - client->debugfs_mdsmap = debugfs_create_file("mdsmap", + dout("b\n"); + fsc->debugfs_mdsmap = debugfs_create_file("mdsmap", 0600, - client->debugfs_dir, - client, + fsc->client->debugfs_dir, + fsc, &mdsmap_show_fops); - if (!client->debugfs_mdsmap) + if (!fsc->debugfs_mdsmap) goto out; - client->debugfs_osdmap = debugfs_create_file("osdmap", - 0600, - client->debugfs_dir, - client, - &osdmap_show_fops); - if (!client->debugfs_osdmap) + dout("ca\n"); + fsc->debugfs_mdsc = debugfs_create_file("mdsc", + 0600, + fsc->client->debugfs_dir, + fsc, + &mdsc_show_fops); + if (!fsc->debugfs_mdsc) goto out; - client->debugfs_dentry_lru = debugfs_create_file("dentry_lru", - 0600, - client->debugfs_dir, - client, - &dentry_lru_show_fops); - if (!client->debugfs_dentry_lru) - goto out; - - client->debugfs_caps = debugfs_create_file("caps", + dout("da\n"); + fsc->debugfs_caps = debugfs_create_file("caps", 0400, - client->debugfs_dir, - client, + fsc->client->debugfs_dir, + fsc, &caps_show_fops); - if (!client->debugfs_caps) + if (!fsc->debugfs_caps) goto out; - client->debugfs_congestion_kb = - debugfs_create_file("writeback_congestion_kb", - 0600, - client->debugfs_dir, - client, - &congestion_kb_fops); - if (!client->debugfs_congestion_kb) + dout("ea\n"); + fsc->debugfs_dentry_lru = debugfs_create_file("dentry_lru", + 0600, + fsc->client->debugfs_dir, + fsc, + &dentry_lru_show_fops); + if (!fsc->debugfs_dentry_lru) goto out; - sprintf(name, "../../bdi/%s", dev_name(client->sb->s_bdi->dev)); - client->debugfs_bdi = debugfs_create_symlink("bdi", client->debugfs_dir, - name); - return 0; out: - ceph_debugfs_client_cleanup(client); - return ret; + ceph_fs_debugfs_cleanup(fsc); + return err; } -void ceph_debugfs_client_cleanup(struct ceph_client *client) -{ - debugfs_remove(client->debugfs_bdi); - debugfs_remove(client->debugfs_caps); - debugfs_remove(client->debugfs_dentry_lru); - debugfs_remove(client->debugfs_osdmap); - debugfs_remove(client->debugfs_mdsmap); - debugfs_remove(client->debugfs_monmap); - debugfs_remove(client->osdc.debugfs_file); - debugfs_remove(client->mdsc.debugfs_file); - debugfs_remove(client->monc.debugfs_file); - debugfs_remove(client->debugfs_congestion_kb); - debugfs_remove(client->debugfs_dir); -} #else /* CONFIG_DEBUG_FS */ -int __init ceph_debugfs_init(void) -{ - return 0; -} - -void ceph_debugfs_cleanup(void) -{ -} - -int ceph_debugfs_client_init(struct ceph_client *client) +int ceph_fs_debugfs_init(struct ceph_fs_client *fsc) { return 0; } -void ceph_debugfs_client_cleanup(struct ceph_client *client) +void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc) { } diff --git a/fs/ceph/decode.h b/fs/ceph/decode.h deleted file mode 100644 index c5b6939fb32..00000000000 --- a/fs/ceph/decode.h +++ /dev/null @@ -1,201 +0,0 @@ -#ifndef __CEPH_DECODE_H -#define __CEPH_DECODE_H - -#include -#include - -#include "types.h" - -/* - * in all cases, - * void **p pointer to position pointer - * void *end pointer to end of buffer (last byte + 1) - */ - -static inline u64 ceph_decode_64(void **p) -{ - u64 v = get_unaligned_le64(*p); - *p += sizeof(u64); - return v; -} -static inline u32 ceph_decode_32(void **p) -{ - u32 v = get_unaligned_le32(*p); - *p += sizeof(u32); - return v; -} -static inline u16 ceph_decode_16(void **p) -{ - u16 v = get_unaligned_le16(*p); - *p += sizeof(u16); - return v; -} -static inline u8 ceph_decode_8(void **p) -{ - u8 v = *(u8 *)*p; - (*p)++; - return v; -} -static inline void ceph_decode_copy(void **p, void *pv, size_t n) -{ - memcpy(pv, *p, n); - *p += n; -} - -/* - * bounds check input. - */ -#define ceph_decode_need(p, end, n, bad) \ - do { \ - if (unlikely(*(p) + (n) > (end))) \ - goto bad; \ - } while (0) - -#define ceph_decode_64_safe(p, end, v, bad) \ - do { \ - ceph_decode_need(p, end, sizeof(u64), bad); \ - v = ceph_decode_64(p); \ - } while (0) -#define ceph_decode_32_safe(p, end, v, bad) \ - do { \ - ceph_decode_need(p, end, sizeof(u32), bad); \ - v = ceph_decode_32(p); \ - } while (0) -#define ceph_decode_16_safe(p, end, v, bad) \ - do { \ - ceph_decode_need(p, end, sizeof(u16), bad); \ - v = ceph_decode_16(p); \ - } while (0) -#define ceph_decode_8_safe(p, end, v, bad) \ - do { \ - ceph_decode_need(p, end, sizeof(u8), bad); \ - v = ceph_decode_8(p); \ - } while (0) - -#define ceph_decode_copy_safe(p, end, pv, n, bad) \ - do { \ - ceph_decode_need(p, end, n, bad); \ - ceph_decode_copy(p, pv, n); \ - } while (0) - -/* - * struct ceph_timespec <-> struct timespec - */ -static inline void ceph_decode_timespec(struct timespec *ts, - const struct ceph_timespec *tv) -{ - ts->tv_sec = le32_to_cpu(tv->tv_sec); - ts->tv_nsec = le32_to_cpu(tv->tv_nsec); -} -static inline void ceph_encode_timespec(struct ceph_timespec *tv, - const struct timespec *ts) -{ - tv->tv_sec = cpu_to_le32(ts->tv_sec); - tv->tv_nsec = cpu_to_le32(ts->tv_nsec); -} - -/* - * sockaddr_storage <-> ceph_sockaddr - */ -static inline void ceph_encode_addr(struct ceph_entity_addr *a) -{ - __be16 ss_family = htons(a->in_addr.ss_family); - a->in_addr.ss_family = *(__u16 *)&ss_family; -} -static inline void ceph_decode_addr(struct ceph_entity_addr *a) -{ - __be16 ss_family = *(__be16 *)&a->in_addr.ss_family; - a->in_addr.ss_family = ntohs(ss_family); - WARN_ON(a->in_addr.ss_family == 512); -} - -/* - * encoders - */ -static inline void ceph_encode_64(void **p, u64 v) -{ - put_unaligned_le64(v, (__le64 *)*p); - *p += sizeof(u64); -} -static inline void ceph_encode_32(void **p, u32 v) -{ - put_unaligned_le32(v, (__le32 *)*p); - *p += sizeof(u32); -} -static inline void ceph_encode_16(void **p, u16 v) -{ - put_unaligned_le16(v, (__le16 *)*p); - *p += sizeof(u16); -} -static inline void ceph_encode_8(void **p, u8 v) -{ - *(u8 *)*p = v; - (*p)++; -} -static inline void ceph_encode_copy(void **p, const void *s, int len) -{ - memcpy(*p, s, len); - *p += len; -} - -/* - * filepath, string encoders - */ -static inline void ceph_encode_filepath(void **p, void *end, - u64 ino, const char *path) -{ - u32 len = path ? strlen(path) : 0; - BUG_ON(*p + sizeof(ino) + sizeof(len) + len > end); - ceph_encode_8(p, 1); - ceph_encode_64(p, ino); - ceph_encode_32(p, len); - if (len) - memcpy(*p, path, len); - *p += len; -} - -static inline void ceph_encode_string(void **p, void *end, - const char *s, u32 len) -{ - BUG_ON(*p + sizeof(len) + len > end); - ceph_encode_32(p, len); - if (len) - memcpy(*p, s, len); - *p += len; -} - -#define ceph_encode_need(p, end, n, bad) \ - do { \ - if (unlikely(*(p) + (n) > (end))) \ - goto bad; \ - } while (0) - -#define ceph_encode_64_safe(p, end, v, bad) \ - do { \ - ceph_encode_need(p, end, sizeof(u64), bad); \ - ceph_encode_64(p, v); \ - } while (0) -#define ceph_encode_32_safe(p, end, v, bad) \ - do { \ - ceph_encode_need(p, end, sizeof(u32), bad); \ - ceph_encode_32(p, v); \ - } while (0) -#define ceph_encode_16_safe(p, end, v, bad) \ - do { \ - ceph_encode_need(p, end, sizeof(u16), bad); \ - ceph_encode_16(p, v); \ - } while (0) - -#define ceph_encode_copy_safe(p, end, pv, n, bad) \ - do { \ - ceph_encode_need(p, end, n, bad); \ - ceph_encode_copy(p, pv, n); \ - } while (0) -#define ceph_encode_string_safe(p, end, s, n, bad) \ - do { \ - ceph_encode_need(p, end, n, bad); \ - ceph_encode_string(p, end, s, n); \ - } while (0) - - -#endif diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index a1986eb5204..a8d2aacc612 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1,4 +1,4 @@ -#include "ceph_debug.h" +#include #include #include @@ -7,6 +7,7 @@ #include #include "super.h" +#include "mds_client.h" /* * Directory operations: readdir, lookup, create, link, unlink, @@ -227,15 +228,15 @@ static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir) struct ceph_file_info *fi = filp->private_data; struct inode *inode = filp->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_client *client = ceph_inode_to_client(inode); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_inode_to_client(inode); + struct ceph_mds_client *mdsc = fsc->mdsc; unsigned frag = fpos_frag(filp->f_pos); int off = fpos_off(filp->f_pos); int err; u32 ftype; struct ceph_mds_reply_info_parsed *rinfo; - const int max_entries = client->mount_args->max_readdir; - const int max_bytes = client->mount_args->max_readdir_bytes; + const int max_entries = fsc->mount_options->max_readdir; + const int max_bytes = fsc->mount_options->max_readdir_bytes; dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off); if (fi->at_end) @@ -267,7 +268,7 @@ static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir) /* can we use the dcache? */ spin_lock(&inode->i_lock); if ((filp->f_pos == 2 || fi->dentry) && - !ceph_test_opt(client, NOASYNCREADDIR) && + !ceph_test_mount_opt(fsc, NOASYNCREADDIR) && ceph_snap(inode) != CEPH_SNAPDIR && (ci->i_ceph_flags & CEPH_I_COMPLETE) && __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) { @@ -487,14 +488,14 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin) struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, struct dentry *dentry, int err) { - struct ceph_client *client = ceph_sb_to_client(dentry->d_sb); + struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); struct inode *parent = dentry->d_parent->d_inode; /* .snap dir? */ if (err == -ENOENT && ceph_vino(parent).ino != CEPH_INO_ROOT && /* no .snap in root dir */ strcmp(dentry->d_name.name, - client->mount_args->snapdir_name) == 0) { + fsc->mount_options->snapdir_name) == 0) { struct inode *inode = ceph_get_snapdir(parent); dout("ENOENT on snapdir %p '%.*s', linking to snapdir %p\n", dentry, dentry->d_name.len, dentry->d_name.name, inode); @@ -539,8 +540,8 @@ static int is_root_ceph_dentry(struct inode *inode, struct dentry *dentry) static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { - struct ceph_client *client = ceph_sb_to_client(dir->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; int op; int err; @@ -572,7 +573,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, spin_lock(&dir->i_lock); dout(" dir %p flags are %d\n", dir, ci->i_ceph_flags); if (strncmp(dentry->d_name.name, - client->mount_args->snapdir_name, + fsc->mount_options->snapdir_name, dentry->d_name.len) && !is_root_ceph_dentry(dir, dentry) && (ci->i_ceph_flags & CEPH_I_COMPLETE) && @@ -629,8 +630,8 @@ int ceph_handle_notrace_create(struct inode *dir, struct dentry *dentry) static int ceph_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { - struct ceph_client *client = ceph_sb_to_client(dir->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; int err; @@ -685,8 +686,8 @@ static int ceph_create(struct inode *dir, struct dentry *dentry, int mode, static int ceph_symlink(struct inode *dir, struct dentry *dentry, const char *dest) { - struct ceph_client *client = ceph_sb_to_client(dir->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; int err; @@ -716,8 +717,8 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry, static int ceph_mkdir(struct inode *dir, struct dentry *dentry, int mode) { - struct ceph_client *client = ceph_sb_to_client(dir->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; int err = -EROFS; int op; @@ -758,8 +759,8 @@ out: static int ceph_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { - struct ceph_client *client = ceph_sb_to_client(dir->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; int err; @@ -813,8 +814,8 @@ static int drop_caps_for_unlink(struct inode *inode) */ static int ceph_unlink(struct inode *dir, struct dentry *dentry) { - struct ceph_client *client = ceph_sb_to_client(dir->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct inode *inode = dentry->d_inode; struct ceph_mds_request *req; int err = -EROFS; @@ -854,8 +855,8 @@ out: static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { - struct ceph_client *client = ceph_sb_to_client(old_dir->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(old_dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; int err; @@ -1076,7 +1077,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size, struct ceph_inode_info *ci = ceph_inode(inode); int left; - if (!ceph_test_opt(ceph_sb_to_client(inode->i_sb), DIRSTAT)) + if (!ceph_test_mount_opt(ceph_sb_to_client(inode->i_sb), DIRSTAT)) return -EISDIR; if (!cf->dir_info) { @@ -1177,7 +1178,7 @@ void ceph_dentry_lru_add(struct dentry *dn) dout("dentry_lru_add %p %p '%.*s'\n", di, dn, dn->d_name.len, dn->d_name.name); if (di) { - mdsc = &ceph_sb_to_client(dn->d_sb)->mdsc; + mdsc = ceph_sb_to_client(dn->d_sb)->mdsc; spin_lock(&mdsc->dentry_lru_lock); list_add_tail(&di->lru, &mdsc->dentry_lru); mdsc->num_dentry++; @@ -1193,7 +1194,7 @@ void ceph_dentry_lru_touch(struct dentry *dn) dout("dentry_lru_touch %p %p '%.*s' (offset %lld)\n", di, dn, dn->d_name.len, dn->d_name.name, di->offset); if (di) { - mdsc = &ceph_sb_to_client(dn->d_sb)->mdsc; + mdsc = ceph_sb_to_client(dn->d_sb)->mdsc; spin_lock(&mdsc->dentry_lru_lock); list_move_tail(&di->lru, &mdsc->dentry_lru); spin_unlock(&mdsc->dentry_lru_lock); @@ -1208,7 +1209,7 @@ void ceph_dentry_lru_del(struct dentry *dn) dout("dentry_lru_del %p %p '%.*s'\n", di, dn, dn->d_name.len, dn->d_name.name); if (di) { - mdsc = &ceph_sb_to_client(dn->d_sb)->mdsc; + mdsc = ceph_sb_to_client(dn->d_sb)->mdsc; spin_lock(&mdsc->dentry_lru_lock); list_del_init(&di->lru); mdsc->num_dentry--; diff --git a/fs/ceph/export.c b/fs/ceph/export.c index e38423e82f2..2297d942699 100644 --- a/fs/ceph/export.c +++ b/fs/ceph/export.c @@ -1,10 +1,11 @@ -#include "ceph_debug.h" +#include #include #include #include #include "super.h" +#include "mds_client.h" /* * NFS export support @@ -120,7 +121,7 @@ static struct dentry *__fh_to_dentry(struct super_block *sb, static struct dentry *__cfh_to_dentry(struct super_block *sb, struct ceph_nfs_confh *cfh) { - struct ceph_mds_client *mdsc = &ceph_sb_to_client(sb)->mdsc; + struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; struct inode *inode; struct dentry *dentry; struct ceph_vino vino; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 66e4da6dba2..e77c28cf369 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -1,5 +1,6 @@ -#include "ceph_debug.h" +#include +#include #include #include #include @@ -38,8 +39,8 @@ static struct ceph_mds_request * prepare_open_request(struct super_block *sb, int flags, int create_mode) { - struct ceph_client *client = ceph_sb_to_client(sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; int want_auth = USE_ANY_MDS; int op = (flags & O_CREAT) ? CEPH_MDS_OP_CREATE : CEPH_MDS_OP_OPEN; @@ -117,8 +118,8 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode) int ceph_open(struct inode *inode, struct file *file) { struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_client *client = ceph_sb_to_client(inode->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(inode->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; struct ceph_file_info *cf = file->private_data; struct inode *parent_inode = file->f_dentry->d_parent->d_inode; @@ -216,8 +217,8 @@ struct dentry *ceph_lookup_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd, int mode, int locked_dir) { - struct ceph_client *client = ceph_sb_to_client(dir->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct file *file = nd->intent.open.file; struct inode *parent_inode = get_dentry_parent_inode(file->f_dentry); struct ceph_mds_request *req; @@ -269,163 +270,6 @@ int ceph_release(struct inode *inode, struct file *file) return 0; } -/* - * build a vector of user pages - */ -static struct page **get_direct_page_vector(const char __user *data, - int num_pages, - loff_t off, size_t len) -{ - struct page **pages; - int rc; - - pages = kmalloc(sizeof(*pages) * num_pages, GFP_NOFS); - if (!pages) - return ERR_PTR(-ENOMEM); - - down_read(¤t->mm->mmap_sem); - rc = get_user_pages(current, current->mm, (unsigned long)data, - num_pages, 0, 0, pages, NULL); - up_read(¤t->mm->mmap_sem); - if (rc < 0) - goto fail; - return pages; - -fail: - kfree(pages); - return ERR_PTR(rc); -} - -static void put_page_vector(struct page **pages, int num_pages) -{ - int i; - - for (i = 0; i < num_pages; i++) - put_page(pages[i]); - kfree(pages); -} - -void ceph_release_page_vector(struct page **pages, int num_pages) -{ - int i; - - for (i = 0; i < num_pages; i++) - __free_pages(pages[i], 0); - kfree(pages); -} - -/* - * allocate a vector new pages - */ -static struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags) -{ - struct page **pages; - int i; - - pages = kmalloc(sizeof(*pages) * num_pages, flags); - if (!pages) - return ERR_PTR(-ENOMEM); - for (i = 0; i < num_pages; i++) { - pages[i] = __page_cache_alloc(flags); - if (pages[i] == NULL) { - ceph_release_page_vector(pages, i); - return ERR_PTR(-ENOMEM); - } - } - return pages; -} - -/* - * copy user data into a page vector - */ -static int copy_user_to_page_vector(struct page **pages, - const char __user *data, - loff_t off, size_t len) -{ - int i = 0; - int po = off & ~PAGE_CACHE_MASK; - int left = len; - int l, bad; - - while (left > 0) { - l = min_t(int, PAGE_CACHE_SIZE-po, left); - bad = copy_from_user(page_address(pages[i]) + po, data, l); - if (bad == l) - return -EFAULT; - data += l - bad; - left -= l - bad; - po += l - bad; - if (po == PAGE_CACHE_SIZE) { - po = 0; - i++; - } - } - return len; -} - -/* - * copy user data from a page vector into a user pointer - */ -static int copy_page_vector_to_user(struct page **pages, char __user *data, - loff_t off, size_t len) -{ - int i = 0; - int po = off & ~PAGE_CACHE_MASK; - int left = len; - int l, bad; - - while (left > 0) { - l = min_t(int, left, PAGE_CACHE_SIZE-po); - bad = copy_to_user(data, page_address(pages[i]) + po, l); - if (bad == l) - return -EFAULT; - data += l - bad; - left -= l - bad; - if (po) { - po += l - bad; - if (po == PAGE_CACHE_SIZE) - po = 0; - } - i++; - } - return len; -} - -/* - * Zero an extent within a page vector. Offset is relative to the - * start of the first page. - */ -static void zero_page_vector_range(int off, int len, struct page **pages) -{ - int i = off >> PAGE_CACHE_SHIFT; - - off &= ~PAGE_CACHE_MASK; - - dout("zero_page_vector_page %u~%u\n", off, len); - - /* leading partial page? */ - if (off) { - int end = min((int)PAGE_CACHE_SIZE, off + len); - dout("zeroing %d %p head from %d\n", i, pages[i], - (int)off); - zero_user_segment(pages[i], off, end); - len -= (end - off); - i++; - } - while (len >= PAGE_CACHE_SIZE) { - dout("zeroing %d %p len=%d\n", i, pages[i], len); - zero_user_segment(pages[i], 0, PAGE_CACHE_SIZE); - len -= PAGE_CACHE_SIZE; - i++; - } - /* trailing partial page? */ - if (len) { - dout("zeroing %d %p tail to %d\n", i, pages[i], (int)len); - zero_user_segment(pages[i], 0, len); - } -} - - /* * Read a range of bytes striped over one or more objects. Iterate over * objects we stripe over. (That's not atomic, but good enough for now.) @@ -438,7 +282,7 @@ static int striped_read(struct inode *inode, struct page **pages, int num_pages, int *checkeof) { - struct ceph_client *client = ceph_inode_to_client(inode); + struct ceph_fs_client *fsc = ceph_inode_to_client(inode); struct ceph_inode_info *ci = ceph_inode(inode); u64 pos, this_len; int page_off = off & ~PAGE_CACHE_MASK; /* first byte's offset in page */ @@ -459,7 +303,7 @@ static int striped_read(struct inode *inode, more: this_len = left; - ret = ceph_osdc_readpages(&client->osdc, ceph_vino(inode), + ret = ceph_osdc_readpages(&fsc->client->osdc, ceph_vino(inode), &ci->i_layout, pos, &this_len, ci->i_truncate_seq, ci->i_truncate_size, @@ -477,8 +321,8 @@ more: if (read < pos - off) { dout(" zero gap %llu to %llu\n", off + read, pos); - zero_page_vector_range(page_off + read, - pos - off - read, pages); + ceph_zero_page_vector_range(page_off + read, + pos - off - read, pages); } pos += ret; read = pos - off; @@ -495,8 +339,8 @@ more: /* was original extent fully inside i_size? */ if (pos + left <= inode->i_size) { dout("zero tail\n"); - zero_page_vector_range(page_off + read, len - read, - pages); + ceph_zero_page_vector_range(page_off + read, len - read, + pages); read = len; goto out; } @@ -531,7 +375,7 @@ static ssize_t ceph_sync_read(struct file *file, char __user *data, (file->f_flags & O_DIRECT) ? "O_DIRECT" : ""); if (file->f_flags & O_DIRECT) { - pages = get_direct_page_vector(data, num_pages, off, len); + pages = ceph_get_direct_page_vector(data, num_pages, off, len); /* * flush any page cache pages in this range. this @@ -552,13 +396,13 @@ static ssize_t ceph_sync_read(struct file *file, char __user *data, ret = striped_read(inode, off, len, pages, num_pages, checkeof); if (ret >= 0 && (file->f_flags & O_DIRECT) == 0) - ret = copy_page_vector_to_user(pages, data, off, ret); + ret = ceph_copy_page_vector_to_user(pages, data, off, ret); if (ret >= 0) *poff = off + ret; done: if (file->f_flags & O_DIRECT) - put_page_vector(pages, num_pages); + ceph_put_page_vector(pages, num_pages); else ceph_release_page_vector(pages, num_pages); dout("sync_read result %d\n", ret); @@ -594,7 +438,7 @@ static ssize_t ceph_sync_write(struct file *file, const char __user *data, { struct inode *inode = file->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_client *client = ceph_inode_to_client(inode); + struct ceph_fs_client *fsc = ceph_inode_to_client(inode); struct ceph_osd_request *req; struct page **pages; int num_pages; @@ -642,7 +486,7 @@ static ssize_t ceph_sync_write(struct file *file, const char __user *data, */ more: len = left; - req = ceph_osdc_new_request(&client->osdc, &ci->i_layout, + req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, ceph_vino(inode), pos, &len, CEPH_OSD_OP_WRITE, flags, ci->i_snap_realm->cached_context, @@ -655,7 +499,7 @@ more: num_pages = calc_pages_for(pos, len); if (file->f_flags & O_DIRECT) { - pages = get_direct_page_vector(data, num_pages, pos, len); + pages = ceph_get_direct_page_vector(data, num_pages, pos, len); if (IS_ERR(pages)) { ret = PTR_ERR(pages); goto out; @@ -673,7 +517,7 @@ more: ret = PTR_ERR(pages); goto out; } - ret = copy_user_to_page_vector(pages, data, pos, len); + ret = ceph_copy_user_to_page_vector(pages, data, pos, len); if (ret < 0) { ceph_release_page_vector(pages, num_pages); goto out; @@ -689,7 +533,7 @@ more: req->r_num_pages = num_pages; req->r_inode = inode; - ret = ceph_osdc_start_request(&client->osdc, req, false); + ret = ceph_osdc_start_request(&fsc->client->osdc, req, false); if (!ret) { if (req->r_safe_callback) { /* @@ -701,11 +545,11 @@ more: spin_unlock(&ci->i_unsafe_lock); ceph_get_cap_refs(ci, CEPH_CAP_FILE_WR); } - ret = ceph_osdc_wait_request(&client->osdc, req); + ret = ceph_osdc_wait_request(&fsc->client->osdc, req); } if (file->f_flags & O_DIRECT) - put_page_vector(pages, num_pages); + ceph_put_page_vector(pages, num_pages); else if (file->f_flags & O_SYNC) ceph_release_page_vector(pages, num_pages); @@ -814,7 +658,8 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov, struct ceph_file_info *fi = file->private_data; struct inode *inode = file->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_osd_client *osdc = &ceph_sb_to_client(inode->i_sb)->osdc; + struct ceph_osd_client *osdc = + &ceph_sb_to_client(inode->i_sb)->client->osdc; loff_t endoff = pos + iov->iov_len; int want, got = 0; int ret, err; diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 62377ec37ed..1d6a45b5a04 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1,4 +1,4 @@ -#include "ceph_debug.h" +#include #include #include @@ -13,7 +13,8 @@ #include #include "super.h" -#include "decode.h" +#include "mds_client.h" +#include /* * Ceph inode operations @@ -384,7 +385,7 @@ void ceph_destroy_inode(struct inode *inode) */ if (ci->i_snap_realm) { struct ceph_mds_client *mdsc = - &ceph_sb_to_client(ci->vfs_inode.i_sb)->mdsc; + ceph_sb_to_client(ci->vfs_inode.i_sb)->mdsc; struct ceph_snap_realm *realm = ci->i_snap_realm; dout(" dropping residual ref to snap realm %p\n", realm); @@ -685,7 +686,7 @@ static int fill_inode(struct inode *inode, } /* it may be better to set st_size in getattr instead? */ - if (ceph_test_opt(ceph_sb_to_client(inode->i_sb), RBYTES)) + if (ceph_test_mount_opt(ceph_sb_to_client(inode->i_sb), RBYTES)) inode->i_size = ci->i_rbytes; break; default: @@ -901,7 +902,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req, struct inode *in = NULL; struct ceph_mds_reply_inode *ininfo; struct ceph_vino vino; - struct ceph_client *client = ceph_sb_to_client(sb); + struct ceph_fs_client *fsc = ceph_sb_to_client(sb); int i = 0; int err = 0; @@ -965,7 +966,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req, */ if (rinfo->head->is_dentry && !req->r_aborted && (rinfo->head->is_target || strncmp(req->r_dentry->d_name.name, - client->mount_args->snapdir_name, + fsc->mount_options->snapdir_name, req->r_dentry->d_name.len))) { /* * lookup link rename : null -> possibly existing inode @@ -1533,7 +1534,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) struct inode *parent_inode = dentry->d_parent->d_inode; const unsigned int ia_valid = attr->ia_valid; struct ceph_mds_request *req; - struct ceph_mds_client *mdsc = &ceph_sb_to_client(dentry->d_sb)->mdsc; + struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc; int issued; int release = 0, dirtied = 0; int mask = 0; @@ -1728,8 +1729,8 @@ out: */ int ceph_do_getattr(struct inode *inode, int mask) { - struct ceph_client *client = ceph_sb_to_client(inode->i_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(inode->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; int err; diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index 76e307d2aba..899578b0c46 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -1,8 +1,10 @@ #include -#include "ioctl.h" #include "super.h" -#include "ceph_debug.h" +#include "mds_client.h" +#include + +#include "ioctl.h" /* @@ -37,7 +39,7 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg) { struct inode *inode = file->f_dentry->d_inode; struct inode *parent_inode = file->f_dentry->d_parent->d_inode; - struct ceph_mds_client *mdsc = &ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; struct ceph_mds_request *req; struct ceph_ioctl_layout l; int err, i; @@ -98,7 +100,8 @@ static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg) struct ceph_ioctl_dataloc dl; struct inode *inode = file->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_osd_client *osdc = &ceph_sb_to_client(inode->i_sb)->osdc; + struct ceph_osd_client *osdc = + &ceph_sb_to_client(inode->i_sb)->client->osdc; u64 len = 1, olen; u64 tmp; struct ceph_object_layout ol; diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c index ff4e753aae9..087afe9ca36 100644 --- a/fs/ceph/locks.c +++ b/fs/ceph/locks.c @@ -1,11 +1,11 @@ -#include "ceph_debug.h" +#include #include #include #include "super.h" #include "mds_client.h" -#include "pagelist.h" +#include /** * Implement fcntl and flock locking functions. @@ -16,7 +16,7 @@ static int ceph_lock_message(u8 lock_type, u16 operation, struct file *file, { struct inode *inode = file->f_dentry->d_inode; struct ceph_mds_client *mdsc = - &ceph_sb_to_client(inode->i_sb)->mdsc; + ceph_sb_to_client(inode->i_sb)->mdsc; struct ceph_mds_request *req; int err; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index fad95f8f260..33568239a08 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1,17 +1,20 @@ -#include "ceph_debug.h" +#include #include #include #include #include +#include +#include -#include "mds_client.h" -#include "mon_client.h" #include "super.h" -#include "messenger.h" -#include "decode.h" -#include "auth.h" -#include "pagelist.h" +#include "mds_client.h" + +#include +#include +#include +#include +#include /* * A cluster of MDS (metadata server) daemons is responsible for @@ -286,8 +289,9 @@ void ceph_put_mds_session(struct ceph_mds_session *s) atomic_read(&s->s_ref), atomic_read(&s->s_ref)-1); if (atomic_dec_and_test(&s->s_ref)) { if (s->s_authorizer) - s->s_mdsc->client->monc.auth->ops->destroy_authorizer( - s->s_mdsc->client->monc.auth, s->s_authorizer); + s->s_mdsc->fsc->client->monc.auth->ops->destroy_authorizer( + s->s_mdsc->fsc->client->monc.auth, + s->s_authorizer); kfree(s); } } @@ -344,7 +348,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_seq = 0; mutex_init(&s->s_mutex); - ceph_con_init(mdsc->client->msgr, &s->s_con); + ceph_con_init(mdsc->fsc->client->msgr, &s->s_con); s->s_con.private = s; s->s_con.ops = &mds_con_ops; s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS; @@ -599,7 +603,7 @@ static int __choose_mds(struct ceph_mds_client *mdsc, } else if (req->r_dentry) { struct inode *dir = req->r_dentry->d_parent->d_inode; - if (dir->i_sb != mdsc->client->sb) { + if (dir->i_sb != mdsc->fsc->sb) { /* not this fs! */ inode = req->r_dentry->d_inode; } else if (ceph_snap(dir) != CEPH_NOSNAP) { @@ -884,7 +888,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, __ceph_remove_cap(cap); if (!__ceph_is_any_real_caps(ci)) { struct ceph_mds_client *mdsc = - &ceph_sb_to_client(inode->i_sb)->mdsc; + ceph_sb_to_client(inode->i_sb)->mdsc; spin_lock(&mdsc->cap_dirty_lock); if (!list_empty(&ci->i_dirty_item)) { @@ -1146,7 +1150,7 @@ int ceph_add_cap_releases(struct ceph_mds_client *mdsc, struct ceph_msg *msg, *partial = NULL; struct ceph_mds_cap_release *head; int err = -ENOMEM; - int extra = mdsc->client->mount_args->cap_release_safety; + int extra = mdsc->fsc->mount_options->cap_release_safety; int num; dout("add_cap_releases %p mds%d extra %d\n", session, session->s_mds, @@ -2085,7 +2089,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) /* insert trace into our cache */ mutex_lock(&req->r_fill_mutex); - err = ceph_fill_trace(mdsc->client->sb, req, req->r_session); + err = ceph_fill_trace(mdsc->fsc->sb, req, req->r_session); if (err == 0) { if (result == 0 && rinfo->dir_nr) ceph_readdir_prepopulate(req, req->r_session); @@ -2613,7 +2617,7 @@ static void handle_lease(struct ceph_mds_client *mdsc, struct ceph_mds_session *session, struct ceph_msg *msg) { - struct super_block *sb = mdsc->client->sb; + struct super_block *sb = mdsc->fsc->sb; struct inode *inode; struct ceph_inode_info *ci; struct dentry *parent, *dentry; @@ -2891,10 +2895,16 @@ static void delayed_work(struct work_struct *work) schedule_delayed(mdsc); } +int ceph_mdsc_init(struct ceph_fs_client *fsc) -int ceph_mdsc_init(struct ceph_mds_client *mdsc, struct ceph_client *client) { - mdsc->client = client; + struct ceph_mds_client *mdsc; + + mdsc = kzalloc(sizeof(struct ceph_mds_client), GFP_NOFS); + if (!mdsc) + return -ENOMEM; + mdsc->fsc = fsc; + fsc->mdsc = mdsc; mutex_init(&mdsc->mutex); mdsc->mdsmap = kzalloc(sizeof(*mdsc->mdsmap), GFP_NOFS); if (mdsc->mdsmap == NULL) @@ -2927,7 +2937,7 @@ int ceph_mdsc_init(struct ceph_mds_client *mdsc, struct ceph_client *client) INIT_LIST_HEAD(&mdsc->dentry_lru); ceph_caps_init(mdsc); - ceph_adjust_min_caps(mdsc, client->min_caps); + ceph_adjust_min_caps(mdsc, fsc->min_caps); return 0; } @@ -2939,7 +2949,7 @@ int ceph_mdsc_init(struct ceph_mds_client *mdsc, struct ceph_client *client) static void wait_requests(struct ceph_mds_client *mdsc) { struct ceph_mds_request *req; - struct ceph_client *client = mdsc->client; + struct ceph_fs_client *fsc = mdsc->fsc; mutex_lock(&mdsc->mutex); if (__get_oldest_req(mdsc)) { @@ -2947,7 +2957,7 @@ static void wait_requests(struct ceph_mds_client *mdsc) dout("wait_requests waiting for requests\n"); wait_for_completion_timeout(&mdsc->safe_umount_waiters, - client->mount_args->mount_timeout * HZ); + fsc->client->options->mount_timeout * HZ); /* tear down remaining requests */ mutex_lock(&mdsc->mutex); @@ -3030,7 +3040,7 @@ void ceph_mdsc_sync(struct ceph_mds_client *mdsc) { u64 want_tid, want_flush; - if (mdsc->client->mount_state == CEPH_MOUNT_SHUTDOWN) + if (mdsc->fsc->mount_state == CEPH_MOUNT_SHUTDOWN) return; dout("sync\n"); @@ -3053,7 +3063,7 @@ bool done_closing_sessions(struct ceph_mds_client *mdsc) { int i, n = 0; - if (mdsc->client->mount_state == CEPH_MOUNT_SHUTDOWN) + if (mdsc->fsc->mount_state == CEPH_MOUNT_SHUTDOWN) return true; mutex_lock(&mdsc->mutex); @@ -3071,8 +3081,8 @@ void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc) { struct ceph_mds_session *session; int i; - struct ceph_client *client = mdsc->client; - unsigned long timeout = client->mount_args->mount_timeout * HZ; + struct ceph_fs_client *fsc = mdsc->fsc; + unsigned long timeout = fsc->client->options->mount_timeout * HZ; dout("close_sessions\n"); @@ -3119,7 +3129,7 @@ void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc) dout("stopped\n"); } -void ceph_mdsc_stop(struct ceph_mds_client *mdsc) +static void ceph_mdsc_stop(struct ceph_mds_client *mdsc) { dout("stop\n"); cancel_delayed_work_sync(&mdsc->delayed_work); /* cancel timer */ @@ -3129,6 +3139,15 @@ void ceph_mdsc_stop(struct ceph_mds_client *mdsc) ceph_caps_finalize(mdsc); } +void ceph_mdsc_destroy(struct ceph_fs_client *fsc) +{ + struct ceph_mds_client *mdsc = fsc->mdsc; + + ceph_mdsc_stop(mdsc); + fsc->mdsc = NULL; + kfree(mdsc); +} + /* * handle mds map update. @@ -3145,14 +3164,14 @@ void ceph_mdsc_handle_map(struct ceph_mds_client *mdsc, struct ceph_msg *msg) ceph_decode_need(&p, end, sizeof(fsid)+2*sizeof(u32), bad); ceph_decode_copy(&p, &fsid, sizeof(fsid)); - if (ceph_check_fsid(mdsc->client, &fsid) < 0) + if (ceph_check_fsid(mdsc->fsc->client, &fsid) < 0) return; epoch = ceph_decode_32(&p); maplen = ceph_decode_32(&p); dout("handle_map epoch %u len %d\n", epoch, (int)maplen); /* do we need it? */ - ceph_monc_got_mdsmap(&mdsc->client->monc, epoch); + ceph_monc_got_mdsmap(&mdsc->fsc->client->monc, epoch); mutex_lock(&mdsc->mutex); if (mdsc->mdsmap && epoch <= mdsc->mdsmap->m_epoch) { dout("handle_map epoch %u <= our %u\n", @@ -3176,7 +3195,7 @@ void ceph_mdsc_handle_map(struct ceph_mds_client *mdsc, struct ceph_msg *msg) } else { mdsc->mdsmap = newmap; /* first mds map */ } - mdsc->client->sb->s_maxbytes = mdsc->mdsmap->m_max_file_size; + mdsc->fsc->sb->s_maxbytes = mdsc->mdsmap->m_max_file_size; __wake_requests(mdsc, &mdsc->waiting_for_map); @@ -3277,7 +3296,7 @@ static int get_authorizer(struct ceph_connection *con, { struct ceph_mds_session *s = con->private; struct ceph_mds_client *mdsc = s->s_mdsc; - struct ceph_auth_client *ac = mdsc->client->monc.auth; + struct ceph_auth_client *ac = mdsc->fsc->client->monc.auth; int ret = 0; if (force_new && s->s_authorizer) { @@ -3311,7 +3330,7 @@ static int verify_authorizer_reply(struct ceph_connection *con, int len) { struct ceph_mds_session *s = con->private; struct ceph_mds_client *mdsc = s->s_mdsc; - struct ceph_auth_client *ac = mdsc->client->monc.auth; + struct ceph_auth_client *ac = mdsc->fsc->client->monc.auth; return ac->ops->verify_authorizer_reply(ac, s->s_authorizer, len); } @@ -3320,12 +3339,12 @@ static int invalidate_authorizer(struct ceph_connection *con) { struct ceph_mds_session *s = con->private; struct ceph_mds_client *mdsc = s->s_mdsc; - struct ceph_auth_client *ac = mdsc->client->monc.auth; + struct ceph_auth_client *ac = mdsc->fsc->client->monc.auth; if (ac->ops->invalidate_authorizer) ac->ops->invalidate_authorizer(ac, CEPH_ENTITY_TYPE_MDS); - return ceph_monc_validate_auth(&mdsc->client->monc); + return ceph_monc_validate_auth(&mdsc->fsc->client->monc); } static const struct ceph_connection_operations mds_con_ops = { @@ -3338,7 +3357,4 @@ static const struct ceph_connection_operations mds_con_ops = { .peer_reset = peer_reset, }; - - - /* eof */ diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index c98267ce6d2..d66d63c7235 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -8,9 +8,9 @@ #include #include -#include "types.h" -#include "messenger.h" -#include "mdsmap.h" +#include +#include +#include /* * Some lock dependencies: @@ -26,7 +26,7 @@ * */ -struct ceph_client; +struct ceph_fs_client; struct ceph_cap; /* @@ -230,7 +230,7 @@ struct ceph_mds_request { * mds client state */ struct ceph_mds_client { - struct ceph_client *client; + struct ceph_fs_client *fsc; struct mutex mutex; /* all nested structures */ struct ceph_mdsmap *mdsmap; @@ -289,11 +289,6 @@ struct ceph_mds_client { int caps_avail_count; /* unused, unreserved */ int caps_min_count; /* keep at least this many (unreserved) */ - -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_file; -#endif - spinlock_t dentry_lru_lock; struct list_head dentry_lru; int num_dentry; @@ -316,10 +311,9 @@ extern void ceph_put_mds_session(struct ceph_mds_session *s); extern int ceph_send_msg_mds(struct ceph_mds_client *mdsc, struct ceph_msg *msg, int mds); -extern int ceph_mdsc_init(struct ceph_mds_client *mdsc, - struct ceph_client *client); +extern int ceph_mdsc_init(struct ceph_fs_client *fsc); extern void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc); -extern void ceph_mdsc_stop(struct ceph_mds_client *mdsc); +extern void ceph_mdsc_destroy(struct ceph_fs_client *fsc); extern void ceph_mdsc_sync(struct ceph_mds_client *mdsc); diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index 040be6d1150..73b7d44e8a3 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -1,4 +1,4 @@ -#include "ceph_debug.h" +#include #include #include @@ -6,9 +6,9 @@ #include #include -#include "mdsmap.h" -#include "messenger.h" -#include "decode.h" +#include +#include +#include #include "super.h" @@ -117,7 +117,8 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) } dout("mdsmap_decode %d/%d %lld mds%d.%d %s %s\n", - i+1, n, global_id, mds, inc, pr_addr(&addr.in_addr), + i+1, n, global_id, mds, inc, + ceph_pr_addr(&addr.in_addr), ceph_mds_state_name(state)); if (mds >= 0 && mds < m->m_max_mds && state > 0) { m->m_info[mds].global_id = global_id; diff --git a/fs/ceph/mdsmap.h b/fs/ceph/mdsmap.h deleted file mode 100644 index 4c5cb0880bb..00000000000 --- a/fs/ceph/mdsmap.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef _FS_CEPH_MDSMAP_H -#define _FS_CEPH_MDSMAP_H - -#include "types.h" - -/* - * mds map - describe servers in the mds cluster. - * - * we limit fields to those the client actually xcares about - */ -struct ceph_mds_info { - u64 global_id; - struct ceph_entity_addr addr; - s32 state; - int num_export_targets; - bool laggy; - u32 *export_targets; -}; - -struct ceph_mdsmap { - u32 m_epoch, m_client_epoch, m_last_failure; - u32 m_root; - u32 m_session_timeout; /* seconds */ - u32 m_session_autoclose; /* seconds */ - u64 m_max_file_size; - u32 m_max_mds; /* size of m_addr, m_state arrays */ - struct ceph_mds_info *m_info; - - /* which object pools file data can be stored in */ - int m_num_data_pg_pools; - u32 *m_data_pg_pools; - u32 m_cas_pg_pool; -}; - -static inline struct ceph_entity_addr * -ceph_mdsmap_get_addr(struct ceph_mdsmap *m, int w) -{ - if (w >= m->m_max_mds) - return NULL; - return &m->m_info[w].addr; -} - -static inline int ceph_mdsmap_get_state(struct ceph_mdsmap *m, int w) -{ - BUG_ON(w < 0); - if (w >= m->m_max_mds) - return CEPH_MDS_STATE_DNE; - return m->m_info[w].state; -} - -static inline bool ceph_mdsmap_is_laggy(struct ceph_mdsmap *m, int w) -{ - if (w >= 0 && w < m->m_max_mds) - return m->m_info[w].laggy; - return false; -} - -extern int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m); -extern struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end); -extern void ceph_mdsmap_destroy(struct ceph_mdsmap *m); - -#endif diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c deleted file mode 100644 index 17a09b32a59..00000000000 --- a/fs/ceph/messenger.c +++ /dev/null @@ -1,2432 +0,0 @@ -#include "ceph_debug.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "super.h" -#include "messenger.h" -#include "decode.h" -#include "pagelist.h" - -/* - * Ceph uses the messenger to exchange ceph_msg messages with other - * hosts in the system. The messenger provides ordered and reliable - * delivery. We tolerate TCP disconnects by reconnecting (with - * exponential backoff) in the case of a fault (disconnection, bad - * crc, protocol error). Acks allow sent messages to be discarded by - * the sender. - */ - -/* static tag bytes (protocol control messages) */ -static char tag_msg = CEPH_MSGR_TAG_MSG; -static char tag_ack = CEPH_MSGR_TAG_ACK; -static char tag_keepalive = CEPH_MSGR_TAG_KEEPALIVE; - -#ifdef CONFIG_LOCKDEP -static struct lock_class_key socket_class; -#endif - - -static void queue_con(struct ceph_connection *con); -static void con_work(struct work_struct *); -static void ceph_fault(struct ceph_connection *con); - -/* - * nicely render a sockaddr as a string. - */ -#define MAX_ADDR_STR 20 -#define MAX_ADDR_STR_LEN 60 -static char addr_str[MAX_ADDR_STR][MAX_ADDR_STR_LEN]; -static DEFINE_SPINLOCK(addr_str_lock); -static int last_addr_str; - -const char *pr_addr(const struct sockaddr_storage *ss) -{ - int i; - char *s; - struct sockaddr_in *in4 = (void *)ss; - struct sockaddr_in6 *in6 = (void *)ss; - - spin_lock(&addr_str_lock); - i = last_addr_str++; - if (last_addr_str == MAX_ADDR_STR) - last_addr_str = 0; - spin_unlock(&addr_str_lock); - s = addr_str[i]; - - switch (ss->ss_family) { - case AF_INET: - snprintf(s, MAX_ADDR_STR_LEN, "%pI4:%u", &in4->sin_addr, - (unsigned int)ntohs(in4->sin_port)); - break; - - case AF_INET6: - snprintf(s, MAX_ADDR_STR_LEN, "[%pI6c]:%u", &in6->sin6_addr, - (unsigned int)ntohs(in6->sin6_port)); - break; - - default: - sprintf(s, "(unknown sockaddr family %d)", (int)ss->ss_family); - } - - return s; -} - -static void encode_my_addr(struct ceph_messenger *msgr) -{ - memcpy(&msgr->my_enc_addr, &msgr->inst.addr, sizeof(msgr->my_enc_addr)); - ceph_encode_addr(&msgr->my_enc_addr); -} - -/* - * work queue for all reading and writing to/from the socket. - */ -struct workqueue_struct *ceph_msgr_wq; - -int __init ceph_msgr_init(void) -{ - ceph_msgr_wq = create_workqueue("ceph-msgr"); - if (IS_ERR(ceph_msgr_wq)) { - int ret = PTR_ERR(ceph_msgr_wq); - pr_err("msgr_init failed to create workqueue: %d\n", ret); - ceph_msgr_wq = NULL; - return ret; - } - return 0; -} - -void ceph_msgr_exit(void) -{ - destroy_workqueue(ceph_msgr_wq); -} - -void ceph_msgr_flush(void) -{ - flush_workqueue(ceph_msgr_wq); -} - - -/* - * socket callback functions - */ - -/* data available on socket, or listen socket received a connect */ -static void ceph_data_ready(struct sock *sk, int count_unused) -{ - struct ceph_connection *con = - (struct ceph_connection *)sk->sk_user_data; - if (sk->sk_state != TCP_CLOSE_WAIT) { - dout("ceph_data_ready on %p state = %lu, queueing work\n", - con, con->state); - queue_con(con); - } -} - -/* socket has buffer space for writing */ -static void ceph_write_space(struct sock *sk) -{ - struct ceph_connection *con = - (struct ceph_connection *)sk->sk_user_data; - - /* only queue to workqueue if there is data we want to write. */ - if (test_bit(WRITE_PENDING, &con->state)) { - dout("ceph_write_space %p queueing write work\n", con); - queue_con(con); - } else { - dout("ceph_write_space %p nothing to write\n", con); - } - - /* since we have our own write_space, clear the SOCK_NOSPACE flag */ - clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); -} - -/* socket's state has changed */ -static void ceph_state_change(struct sock *sk) -{ - struct ceph_connection *con = - (struct ceph_connection *)sk->sk_user_data; - - dout("ceph_state_change %p state = %lu sk_state = %u\n", - con, con->state, sk->sk_state); - - if (test_bit(CLOSED, &con->state)) - return; - - switch (sk->sk_state) { - case TCP_CLOSE: - dout("ceph_state_change TCP_CLOSE\n"); - case TCP_CLOSE_WAIT: - dout("ceph_state_change TCP_CLOSE_WAIT\n"); - if (test_and_set_bit(SOCK_CLOSED, &con->state) == 0) { - if (test_bit(CONNECTING, &con->state)) - con->error_msg = "connection failed"; - else - con->error_msg = "socket closed"; - queue_con(con); - } - break; - case TCP_ESTABLISHED: - dout("ceph_state_change TCP_ESTABLISHED\n"); - queue_con(con); - break; - } -} - -/* - * set up socket callbacks - */ -static void set_sock_callbacks(struct socket *sock, - struct ceph_connection *con) -{ - struct sock *sk = sock->sk; - sk->sk_user_data = (void *)con; - sk->sk_data_ready = ceph_data_ready; - sk->sk_write_space = ceph_write_space; - sk->sk_state_change = ceph_state_change; -} - - -/* - * socket helpers - */ - -/* - * initiate connection to a remote socket. - */ -static struct socket *ceph_tcp_connect(struct ceph_connection *con) -{ - struct sockaddr_storage *paddr = &con->peer_addr.in_addr; - struct socket *sock; - int ret; - - BUG_ON(con->sock); - ret = sock_create_kern(con->peer_addr.in_addr.ss_family, SOCK_STREAM, - IPPROTO_TCP, &sock); - if (ret) - return ERR_PTR(ret); - con->sock = sock; - sock->sk->sk_allocation = GFP_NOFS; - -#ifdef CONFIG_LOCKDEP - lockdep_set_class(&sock->sk->sk_lock, &socket_class); -#endif - - set_sock_callbacks(sock, con); - - dout("connect %s\n", pr_addr(&con->peer_addr.in_addr)); - - ret = sock->ops->connect(sock, (struct sockaddr *)paddr, sizeof(*paddr), - O_NONBLOCK); - if (ret == -EINPROGRESS) { - dout("connect %s EINPROGRESS sk_state = %u\n", - pr_addr(&con->peer_addr.in_addr), - sock->sk->sk_state); - ret = 0; - } - if (ret < 0) { - pr_err("connect %s error %d\n", - pr_addr(&con->peer_addr.in_addr), ret); - sock_release(sock); - con->sock = NULL; - con->error_msg = "connect error"; - } - - if (ret < 0) - return ERR_PTR(ret); - return sock; -} - -static int ceph_tcp_recvmsg(struct socket *sock, void *buf, size_t len) -{ - struct kvec iov = {buf, len}; - struct msghdr msg = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL }; - - return kernel_recvmsg(sock, &msg, &iov, 1, len, msg.msg_flags); -} - -/* - * write something. @more is true if caller will be sending more data - * shortly. - */ -static int ceph_tcp_sendmsg(struct socket *sock, struct kvec *iov, - size_t kvlen, size_t len, int more) -{ - struct msghdr msg = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL }; - - if (more) - msg.msg_flags |= MSG_MORE; - else - msg.msg_flags |= MSG_EOR; /* superfluous, but what the hell */ - - return kernel_sendmsg(sock, &msg, iov, kvlen, len); -} - - -/* - * Shutdown/close the socket for the given connection. - */ -static int con_close_socket(struct ceph_connection *con) -{ - int rc; - - dout("con_close_socket on %p sock %p\n", con, con->sock); - if (!con->sock) - return 0; - set_bit(SOCK_CLOSED, &con->state); - rc = con->sock->ops->shutdown(con->sock, SHUT_RDWR); - sock_release(con->sock); - con->sock = NULL; - clear_bit(SOCK_CLOSED, &con->state); - return rc; -} - -/* - * Reset a connection. Discard all incoming and outgoing messages - * and clear *_seq state. - */ -static void ceph_msg_remove(struct ceph_msg *msg) -{ - list_del_init(&msg->list_head); - ceph_msg_put(msg); -} -static void ceph_msg_remove_list(struct list_head *head) -{ - while (!list_empty(head)) { - struct ceph_msg *msg = list_first_entry(head, struct ceph_msg, - list_head); - ceph_msg_remove(msg); - } -} - -static void reset_connection(struct ceph_connection *con) -{ - /* reset connection, out_queue, msg_ and connect_seq */ - /* discard existing out_queue and msg_seq */ - ceph_msg_remove_list(&con->out_queue); - ceph_msg_remove_list(&con->out_sent); - - if (con->in_msg) { - ceph_msg_put(con->in_msg); - con->in_msg = NULL; - } - - con->connect_seq = 0; - con->out_seq = 0; - if (con->out_msg) { - ceph_msg_put(con->out_msg); - con->out_msg = NULL; - } - con->out_keepalive_pending = false; - con->in_seq = 0; - con->in_seq_acked = 0; -} - -/* - * mark a peer down. drop any open connections. - */ -void ceph_con_close(struct ceph_connection *con) -{ - dout("con_close %p peer %s\n", con, pr_addr(&con->peer_addr.in_addr)); - set_bit(CLOSED, &con->state); /* in case there's queued work */ - clear_bit(STANDBY, &con->state); /* avoid connect_seq bump */ - clear_bit(LOSSYTX, &con->state); /* so we retry next connect */ - clear_bit(KEEPALIVE_PENDING, &con->state); - clear_bit(WRITE_PENDING, &con->state); - mutex_lock(&con->mutex); - reset_connection(con); - con->peer_global_seq = 0; - cancel_delayed_work(&con->work); - mutex_unlock(&con->mutex); - queue_con(con); -} - -/* - * Reopen a closed connection, with a new peer address. - */ -void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr) -{ - dout("con_open %p %s\n", con, pr_addr(&addr->in_addr)); - set_bit(OPENING, &con->state); - clear_bit(CLOSED, &con->state); - memcpy(&con->peer_addr, addr, sizeof(*addr)); - con->delay = 0; /* reset backoff memory */ - queue_con(con); -} - -/* - * return true if this connection ever successfully opened - */ -bool ceph_con_opened(struct ceph_connection *con) -{ - return con->connect_seq > 0; -} - -/* - * generic get/put - */ -struct ceph_connection *ceph_con_get(struct ceph_connection *con) -{ - dout("con_get %p nref = %d -> %d\n", con, - atomic_read(&con->nref), atomic_read(&con->nref) + 1); - if (atomic_inc_not_zero(&con->nref)) - return con; - return NULL; -} - -void ceph_con_put(struct ceph_connection *con) -{ - dout("con_put %p nref = %d -> %d\n", con, - atomic_read(&con->nref), atomic_read(&con->nref) - 1); - BUG_ON(atomic_read(&con->nref) == 0); - if (atomic_dec_and_test(&con->nref)) { - BUG_ON(con->sock); - kfree(con); - } -} - -/* - * initialize a new connection. - */ -void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con) -{ - dout("con_init %p\n", con); - memset(con, 0, sizeof(*con)); - atomic_set(&con->nref, 1); - con->msgr = msgr; - mutex_init(&con->mutex); - INIT_LIST_HEAD(&con->out_queue); - INIT_LIST_HEAD(&con->out_sent); - INIT_DELAYED_WORK(&con->work, con_work); -} - - -/* - * We maintain a global counter to order connection attempts. Get - * a unique seq greater than @gt. - */ -static u32 get_global_seq(struct ceph_messenger *msgr, u32 gt) -{ - u32 ret; - - spin_lock(&msgr->global_seq_lock); - if (msgr->global_seq < gt) - msgr->global_seq = gt; - ret = ++msgr->global_seq; - spin_unlock(&msgr->global_seq_lock); - return ret; -} - - -/* - * Prepare footer for currently outgoing message, and finish things - * off. Assumes out_kvec* are already valid.. we just add on to the end. - */ -static void prepare_write_message_footer(struct ceph_connection *con, int v) -{ - struct ceph_msg *m = con->out_msg; - - dout("prepare_write_message_footer %p\n", con); - con->out_kvec_is_msg = true; - con->out_kvec[v].iov_base = &m->footer; - con->out_kvec[v].iov_len = sizeof(m->footer); - con->out_kvec_bytes += sizeof(m->footer); - con->out_kvec_left++; - con->out_more = m->more_to_follow; - con->out_msg_done = true; -} - -/* - * Prepare headers for the next outgoing message. - */ -static void prepare_write_message(struct ceph_connection *con) -{ - struct ceph_msg *m; - int v = 0; - - con->out_kvec_bytes = 0; - con->out_kvec_is_msg = true; - con->out_msg_done = false; - - /* Sneak an ack in there first? If we can get it into the same - * TCP packet that's a good thing. */ - if (con->in_seq > con->in_seq_acked) { - con->in_seq_acked = con->in_seq; - con->out_kvec[v].iov_base = &tag_ack; - con->out_kvec[v++].iov_len = 1; - con->out_temp_ack = cpu_to_le64(con->in_seq_acked); - con->out_kvec[v].iov_base = &con->out_temp_ack; - con->out_kvec[v++].iov_len = sizeof(con->out_temp_ack); - con->out_kvec_bytes = 1 + sizeof(con->out_temp_ack); - } - - m = list_first_entry(&con->out_queue, - struct ceph_msg, list_head); - con->out_msg = m; - if (test_bit(LOSSYTX, &con->state)) { - list_del_init(&m->list_head); - } else { - /* put message on sent list */ - ceph_msg_get(m); - list_move_tail(&m->list_head, &con->out_sent); - } - - /* - * only assign outgoing seq # if we haven't sent this message - * yet. if it is requeued, resend with it's original seq. - */ - if (m->needs_out_seq) { - m->hdr.seq = cpu_to_le64(++con->out_seq); - m->needs_out_seq = false; - } - - dout("prepare_write_message %p seq %lld type %d len %d+%d+%d %d pgs\n", - m, con->out_seq, le16_to_cpu(m->hdr.type), - le32_to_cpu(m->hdr.front_len), le32_to_cpu(m->hdr.middle_len), - le32_to_cpu(m->hdr.data_len), - m->nr_pages); - BUG_ON(le32_to_cpu(m->hdr.front_len) != m->front.iov_len); - - /* tag + hdr + front + middle */ - con->out_kvec[v].iov_base = &tag_msg; - con->out_kvec[v++].iov_len = 1; - con->out_kvec[v].iov_base = &m->hdr; - con->out_kvec[v++].iov_len = sizeof(m->hdr); - con->out_kvec[v++] = m->front; - if (m->middle) - con->out_kvec[v++] = m->middle->vec; - con->out_kvec_left = v; - con->out_kvec_bytes += 1 + sizeof(m->hdr) + m->front.iov_len + - (m->middle ? m->middle->vec.iov_len : 0); - con->out_kvec_cur = con->out_kvec; - - /* fill in crc (except data pages), footer */ - con->out_msg->hdr.crc = - cpu_to_le32(crc32c(0, (void *)&m->hdr, - sizeof(m->hdr) - sizeof(m->hdr.crc))); - con->out_msg->footer.flags = CEPH_MSG_FOOTER_COMPLETE; - con->out_msg->footer.front_crc = - cpu_to_le32(crc32c(0, m->front.iov_base, m->front.iov_len)); - if (m->middle) - con->out_msg->footer.middle_crc = - cpu_to_le32(crc32c(0, m->middle->vec.iov_base, - m->middle->vec.iov_len)); - else - con->out_msg->footer.middle_crc = 0; - con->out_msg->footer.data_crc = 0; - dout("prepare_write_message front_crc %u data_crc %u\n", - le32_to_cpu(con->out_msg->footer.front_crc), - le32_to_cpu(con->out_msg->footer.middle_crc)); - - /* is there a data payload? */ - if (le32_to_cpu(m->hdr.data_len) > 0) { - /* initialize page iterator */ - con->out_msg_pos.page = 0; - if (m->pages) - con->out_msg_pos.page_pos = - le16_to_cpu(m->hdr.data_off) & ~PAGE_MASK; - else - con->out_msg_pos.page_pos = 0; - con->out_msg_pos.data_pos = 0; - con->out_msg_pos.did_page_crc = 0; - con->out_more = 1; /* data + footer will follow */ - } else { - /* no, queue up footer too and be done */ - prepare_write_message_footer(con, v); - } - - set_bit(WRITE_PENDING, &con->state); -} - -/* - * Prepare an ack. - */ -static void prepare_write_ack(struct ceph_connection *con) -{ - dout("prepare_write_ack %p %llu -> %llu\n", con, - con->in_seq_acked, con->in_seq); - con->in_seq_acked = con->in_seq; - - con->out_kvec[0].iov_base = &tag_ack; - con->out_kvec[0].iov_len = 1; - con->out_temp_ack = cpu_to_le64(con->in_seq_acked); - con->out_kvec[1].iov_base = &con->out_temp_ack; - con->out_kvec[1].iov_len = sizeof(con->out_temp_ack); - con->out_kvec_left = 2; - con->out_kvec_bytes = 1 + sizeof(con->out_temp_ack); - con->out_kvec_cur = con->out_kvec; - con->out_more = 1; /* more will follow.. eventually.. */ - set_bit(WRITE_PENDING, &con->state); -} - -/* - * Prepare to write keepalive byte. - */ -static void prepare_write_keepalive(struct ceph_connection *con) -{ - dout("prepare_write_keepalive %p\n", con); - con->out_kvec[0].iov_base = &tag_keepalive; - con->out_kvec[0].iov_len = 1; - con->out_kvec_left = 1; - con->out_kvec_bytes = 1; - con->out_kvec_cur = con->out_kvec; - set_bit(WRITE_PENDING, &con->state); -} - -/* - * Connection negotiation. - */ - -static void prepare_connect_authorizer(struct ceph_connection *con) -{ - void *auth_buf; - int auth_len = 0; - int auth_protocol = 0; - - mutex_unlock(&con->mutex); - if (con->ops->get_authorizer) - con->ops->get_authorizer(con, &auth_buf, &auth_len, - &auth_protocol, &con->auth_reply_buf, - &con->auth_reply_buf_len, - con->auth_retry); - mutex_lock(&con->mutex); - - con->out_connect.authorizer_protocol = cpu_to_le32(auth_protocol); - con->out_connect.authorizer_len = cpu_to_le32(auth_len); - - con->out_kvec[con->out_kvec_left].iov_base = auth_buf; - con->out_kvec[con->out_kvec_left].iov_len = auth_len; - con->out_kvec_left++; - con->out_kvec_bytes += auth_len; -} - -/* - * We connected to a peer and are saying hello. - */ -static void prepare_write_banner(struct ceph_messenger *msgr, - struct ceph_connection *con) -{ - int len = strlen(CEPH_BANNER); - - con->out_kvec[0].iov_base = CEPH_BANNER; - con->out_kvec[0].iov_len = len; - con->out_kvec[1].iov_base = &msgr->my_enc_addr; - con->out_kvec[1].iov_len = sizeof(msgr->my_enc_addr); - con->out_kvec_left = 2; - con->out_kvec_bytes = len + sizeof(msgr->my_enc_addr); - con->out_kvec_cur = con->out_kvec; - con->out_more = 0; - set_bit(WRITE_PENDING, &con->state); -} - -static void prepare_write_connect(struct ceph_messenger *msgr, - struct ceph_connection *con, - int after_banner) -{ - unsigned global_seq = get_global_seq(con->msgr, 0); - int proto; - - switch (con->peer_name.type) { - case CEPH_ENTITY_TYPE_MON: - proto = CEPH_MONC_PROTOCOL; - break; - case CEPH_ENTITY_TYPE_OSD: - proto = CEPH_OSDC_PROTOCOL; - break; - case CEPH_ENTITY_TYPE_MDS: - proto = CEPH_MDSC_PROTOCOL; - break; - default: - BUG(); - } - - dout("prepare_write_connect %p cseq=%d gseq=%d proto=%d\n", con, - con->connect_seq, global_seq, proto); - - con->out_connect.features = cpu_to_le64(CEPH_FEATURE_SUPPORTED); - con->out_connect.host_type = cpu_to_le32(CEPH_ENTITY_TYPE_CLIENT); - con->out_connect.connect_seq = cpu_to_le32(con->connect_seq); - con->out_connect.global_seq = cpu_to_le32(global_seq); - con->out_connect.protocol_version = cpu_to_le32(proto); - con->out_connect.flags = 0; - - if (!after_banner) { - con->out_kvec_left = 0; - con->out_kvec_bytes = 0; - } - con->out_kvec[con->out_kvec_left].iov_base = &con->out_connect; - con->out_kvec[con->out_kvec_left].iov_len = sizeof(con->out_connect); - con->out_kvec_left++; - con->out_kvec_bytes += sizeof(con->out_connect); - con->out_kvec_cur = con->out_kvec; - con->out_more = 0; - set_bit(WRITE_PENDING, &con->state); - - prepare_connect_authorizer(con); -} - - -/* - * write as much of pending kvecs to the socket as we can. - * 1 -> done - * 0 -> socket full, but more to do - * <0 -> error - */ -static int write_partial_kvec(struct ceph_connection *con) -{ - int ret; - - dout("write_partial_kvec %p %d left\n", con, con->out_kvec_bytes); - while (con->out_kvec_bytes > 0) { - ret = ceph_tcp_sendmsg(con->sock, con->out_kvec_cur, - con->out_kvec_left, con->out_kvec_bytes, - con->out_more); - if (ret <= 0) - goto out; - con->out_kvec_bytes -= ret; - if (con->out_kvec_bytes == 0) - break; /* done */ - while (ret > 0) { - if (ret >= con->out_kvec_cur->iov_len) { - ret -= con->out_kvec_cur->iov_len; - con->out_kvec_cur++; - con->out_kvec_left--; - } else { - con->out_kvec_cur->iov_len -= ret; - con->out_kvec_cur->iov_base += ret; - ret = 0; - break; - } - } - } - con->out_kvec_left = 0; - con->out_kvec_is_msg = false; - ret = 1; -out: - dout("write_partial_kvec %p %d left in %d kvecs ret = %d\n", con, - con->out_kvec_bytes, con->out_kvec_left, ret); - return ret; /* done! */ -} - -#ifdef CONFIG_BLOCK -static void init_bio_iter(struct bio *bio, struct bio **iter, int *seg) -{ - if (!bio) { - *iter = NULL; - *seg = 0; - return; - } - *iter = bio; - *seg = bio->bi_idx; -} - -static void iter_bio_next(struct bio **bio_iter, int *seg) -{ - if (*bio_iter == NULL) - return; - - BUG_ON(*seg >= (*bio_iter)->bi_vcnt); - - (*seg)++; - if (*seg == (*bio_iter)->bi_vcnt) - init_bio_iter((*bio_iter)->bi_next, bio_iter, seg); -} -#endif - -/* - * Write as much message data payload as we can. If we finish, queue - * up the footer. - * 1 -> done, footer is now queued in out_kvec[]. - * 0 -> socket full, but more to do - * <0 -> error - */ -static int write_partial_msg_pages(struct ceph_connection *con) -{ - struct ceph_msg *msg = con->out_msg; - unsigned data_len = le32_to_cpu(msg->hdr.data_len); - size_t len; - int crc = con->msgr->nocrc; - int ret; - int total_max_write; - int in_trail = 0; - size_t trail_len = (msg->trail ? msg->trail->length : 0); - - dout("write_partial_msg_pages %p msg %p page %d/%d offset %d\n", - con, con->out_msg, con->out_msg_pos.page, con->out_msg->nr_pages, - con->out_msg_pos.page_pos); - -#ifdef CONFIG_BLOCK - if (msg->bio && !msg->bio_iter) - init_bio_iter(msg->bio, &msg->bio_iter, &msg->bio_seg); -#endif - - while (data_len > con->out_msg_pos.data_pos) { - struct page *page = NULL; - void *kaddr = NULL; - int max_write = PAGE_SIZE; - int page_shift = 0; - - total_max_write = data_len - trail_len - - con->out_msg_pos.data_pos; - - /* - * if we are calculating the data crc (the default), we need - * to map the page. if our pages[] has been revoked, use the - * zero page. - */ - - /* have we reached the trail part of the data? */ - if (con->out_msg_pos.data_pos >= data_len - trail_len) { - in_trail = 1; - - total_max_write = data_len - con->out_msg_pos.data_pos; - - page = list_first_entry(&msg->trail->head, - struct page, lru); - if (crc) - kaddr = kmap(page); - max_write = PAGE_SIZE; - } else if (msg->pages) { - page = msg->pages[con->out_msg_pos.page]; - if (crc) - kaddr = kmap(page); - } else if (msg->pagelist) { - page = list_first_entry(&msg->pagelist->head, - struct page, lru); - if (crc) - kaddr = kmap(page); -#ifdef CONFIG_BLOCK - } else if (msg->bio) { - struct bio_vec *bv; - - bv = bio_iovec_idx(msg->bio_iter, msg->bio_seg); - page = bv->bv_page; - page_shift = bv->bv_offset; - if (crc) - kaddr = kmap(page) + page_shift; - max_write = bv->bv_len; -#endif - } else { - page = con->msgr->zero_page; - if (crc) - kaddr = page_address(con->msgr->zero_page); - } - len = min_t(int, max_write - con->out_msg_pos.page_pos, - total_max_write); - - if (crc && !con->out_msg_pos.did_page_crc) { - void *base = kaddr + con->out_msg_pos.page_pos; - u32 tmpcrc = le32_to_cpu(con->out_msg->footer.data_crc); - - BUG_ON(kaddr == NULL); - con->out_msg->footer.data_crc = - cpu_to_le32(crc32c(tmpcrc, base, len)); - con->out_msg_pos.did_page_crc = 1; - } - ret = kernel_sendpage(con->sock, page, - con->out_msg_pos.page_pos + page_shift, - len, - MSG_DONTWAIT | MSG_NOSIGNAL | - MSG_MORE); - - if (crc && - (msg->pages || msg->pagelist || msg->bio || in_trail)) - kunmap(page); - - if (ret <= 0) - goto out; - - con->out_msg_pos.data_pos += ret; - con->out_msg_pos.page_pos += ret; - if (ret == len) { - con->out_msg_pos.page_pos = 0; - con->out_msg_pos.page++; - con->out_msg_pos.did_page_crc = 0; - if (in_trail) - list_move_tail(&page->lru, - &msg->trail->head); - else if (msg->pagelist) - list_move_tail(&page->lru, - &msg->pagelist->head); -#ifdef CONFIG_BLOCK - else if (msg->bio) - iter_bio_next(&msg->bio_iter, &msg->bio_seg); -#endif - } - } - - dout("write_partial_msg_pages %p msg %p done\n", con, msg); - - /* prepare and queue up footer, too */ - if (!crc) - con->out_msg->footer.flags |= CEPH_MSG_FOOTER_NOCRC; - con->out_kvec_bytes = 0; - con->out_kvec_left = 0; - con->out_kvec_cur = con->out_kvec; - prepare_write_message_footer(con, 0); - ret = 1; -out: - return ret; -} - -/* - * write some zeros - */ -static int write_partial_skip(struct ceph_connection *con) -{ - int ret; - - while (con->out_skip > 0) { - struct kvec iov = { - .iov_base = page_address(con->msgr->zero_page), - .iov_len = min(con->out_skip, (int)PAGE_CACHE_SIZE) - }; - - ret = ceph_tcp_sendmsg(con->sock, &iov, 1, iov.iov_len, 1); - if (ret <= 0) - goto out; - con->out_skip -= ret; - } - ret = 1; -out: - return ret; -} - -/* - * Prepare to read connection handshake, or an ack. - */ -static void prepare_read_banner(struct ceph_connection *con) -{ - dout("prepare_read_banner %p\n", con); - con->in_base_pos = 0; -} - -static void prepare_read_connect(struct ceph_connection *con) -{ - dout("prepare_read_connect %p\n", con); - con->in_base_pos = 0; -} - -static void prepare_read_ack(struct ceph_connection *con) -{ - dout("prepare_read_ack %p\n", con); - con->in_base_pos = 0; -} - -static void prepare_read_tag(struct ceph_connection *con) -{ - dout("prepare_read_tag %p\n", con); - con->in_base_pos = 0; - con->in_tag = CEPH_MSGR_TAG_READY; -} - -/* - * Prepare to read a message. - */ -static int prepare_read_message(struct ceph_connection *con) -{ - dout("prepare_read_message %p\n", con); - BUG_ON(con->in_msg != NULL); - con->in_base_pos = 0; - con->in_front_crc = con->in_middle_crc = con->in_data_crc = 0; - return 0; -} - - -static int read_partial(struct ceph_connection *con, - int *to, int size, void *object) -{ - *to += size; - while (con->in_base_pos < *to) { - int left = *to - con->in_base_pos; - int have = size - left; - int ret = ceph_tcp_recvmsg(con->sock, object + have, left); - if (ret <= 0) - return ret; - con->in_base_pos += ret; - } - return 1; -} - - -/* - * Read all or part of the connect-side handshake on a new connection - */ -static int read_partial_banner(struct ceph_connection *con) -{ - int ret, to = 0; - - dout("read_partial_banner %p at %d\n", con, con->in_base_pos); - - /* peer's banner */ - ret = read_partial(con, &to, strlen(CEPH_BANNER), con->in_banner); - if (ret <= 0) - goto out; - ret = read_partial(con, &to, sizeof(con->actual_peer_addr), - &con->actual_peer_addr); - if (ret <= 0) - goto out; - ret = read_partial(con, &to, sizeof(con->peer_addr_for_me), - &con->peer_addr_for_me); - if (ret <= 0) - goto out; -out: - return ret; -} - -static int read_partial_connect(struct ceph_connection *con) -{ - int ret, to = 0; - - dout("read_partial_connect %p at %d\n", con, con->in_base_pos); - - ret = read_partial(con, &to, sizeof(con->in_reply), &con->in_reply); - if (ret <= 0) - goto out; - ret = read_partial(con, &to, le32_to_cpu(con->in_reply.authorizer_len), - con->auth_reply_buf); - if (ret <= 0) - goto out; - - dout("read_partial_connect %p tag %d, con_seq = %u, g_seq = %u\n", - con, (int)con->in_reply.tag, - le32_to_cpu(con->in_reply.connect_seq), - le32_to_cpu(con->in_reply.global_seq)); -out: - return ret; - -} - -/* - * Verify the hello banner looks okay. - */ -static int verify_hello(struct ceph_connection *con) -{ - if (memcmp(con->in_banner, CEPH_BANNER, strlen(CEPH_BANNER))) { - pr_err("connect to %s got bad banner\n", - pr_addr(&con->peer_addr.in_addr)); - con->error_msg = "protocol error, bad banner"; - return -1; - } - return 0; -} - -static bool addr_is_blank(struct sockaddr_storage *ss) -{ - switch (ss->ss_family) { - case AF_INET: - return ((struct sockaddr_in *)ss)->sin_addr.s_addr == 0; - case AF_INET6: - return - ((struct sockaddr_in6 *)ss)->sin6_addr.s6_addr32[0] == 0 && - ((struct sockaddr_in6 *)ss)->sin6_addr.s6_addr32[1] == 0 && - ((struct sockaddr_in6 *)ss)->sin6_addr.s6_addr32[2] == 0 && - ((struct sockaddr_in6 *)ss)->sin6_addr.s6_addr32[3] == 0; - } - return false; -} - -static int addr_port(struct sockaddr_storage *ss) -{ - switch (ss->ss_family) { - case AF_INET: - return ntohs(((struct sockaddr_in *)ss)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6 *)ss)->sin6_port); - } - return 0; -} - -static void addr_set_port(struct sockaddr_storage *ss, int p) -{ - switch (ss->ss_family) { - case AF_INET: - ((struct sockaddr_in *)ss)->sin_port = htons(p); - case AF_INET6: - ((struct sockaddr_in6 *)ss)->sin6_port = htons(p); - } -} - -/* - * Parse an ip[:port] list into an addr array. Use the default - * monitor port if a port isn't specified. - */ -int ceph_parse_ips(const char *c, const char *end, - struct ceph_entity_addr *addr, - int max_count, int *count) -{ - int i; - const char *p = c; - - dout("parse_ips on '%.*s'\n", (int)(end-c), c); - for (i = 0; i < max_count; i++) { - const char *ipend; - struct sockaddr_storage *ss = &addr[i].in_addr; - struct sockaddr_in *in4 = (void *)ss; - struct sockaddr_in6 *in6 = (void *)ss; - int port; - char delim = ','; - - if (*p == '[') { - delim = ']'; - p++; - } - - memset(ss, 0, sizeof(*ss)); - if (in4_pton(p, end - p, (u8 *)&in4->sin_addr.s_addr, - delim, &ipend)) - ss->ss_family = AF_INET; - else if (in6_pton(p, end - p, (u8 *)&in6->sin6_addr.s6_addr, - delim, &ipend)) - ss->ss_family = AF_INET6; - else - goto bad; - p = ipend; - - if (delim == ']') { - if (*p != ']') { - dout("missing matching ']'\n"); - goto bad; - } - p++; - } - - /* port? */ - if (p < end && *p == ':') { - port = 0; - p++; - while (p < end && *p >= '0' && *p <= '9') { - port = (port * 10) + (*p - '0'); - p++; - } - if (port > 65535 || port == 0) - goto bad; - } else { - port = CEPH_MON_PORT; - } - - addr_set_port(ss, port); - - dout("parse_ips got %s\n", pr_addr(ss)); - - if (p == end) - break; - if (*p != ',') - goto bad; - p++; - } - - if (p != end) - goto bad; - - if (count) - *count = i + 1; - return 0; - -bad: - pr_err("parse_ips bad ip '%.*s'\n", (int)(end - c), c); - return -EINVAL; -} - -static int process_banner(struct ceph_connection *con) -{ - dout("process_banner on %p\n", con); - - if (verify_hello(con) < 0) - return -1; - - ceph_decode_addr(&con->actual_peer_addr); - ceph_decode_addr(&con->peer_addr_for_me); - - /* - * Make sure the other end is who we wanted. note that the other - * end may not yet know their ip address, so if it's 0.0.0.0, give - * them the benefit of the doubt. - */ - if (memcmp(&con->peer_addr, &con->actual_peer_addr, - sizeof(con->peer_addr)) != 0 && - !(addr_is_blank(&con->actual_peer_addr.in_addr) && - con->actual_peer_addr.nonce == con->peer_addr.nonce)) { - pr_warning("wrong peer, want %s/%d, got %s/%d\n", - pr_addr(&con->peer_addr.in_addr), - (int)le32_to_cpu(con->peer_addr.nonce), - pr_addr(&con->actual_peer_addr.in_addr), - (int)le32_to_cpu(con->actual_peer_addr.nonce)); - con->error_msg = "wrong peer at address"; - return -1; - } - - /* - * did we learn our address? - */ - if (addr_is_blank(&con->msgr->inst.addr.in_addr)) { - int port = addr_port(&con->msgr->inst.addr.in_addr); - - memcpy(&con->msgr->inst.addr.in_addr, - &con->peer_addr_for_me.in_addr, - sizeof(con->peer_addr_for_me.in_addr)); - addr_set_port(&con->msgr->inst.addr.in_addr, port); - encode_my_addr(con->msgr); - dout("process_banner learned my addr is %s\n", - pr_addr(&con->msgr->inst.addr.in_addr)); - } - - set_bit(NEGOTIATING, &con->state); - prepare_read_connect(con); - return 0; -} - -static void fail_protocol(struct ceph_connection *con) -{ - reset_connection(con); - set_bit(CLOSED, &con->state); /* in case there's queued work */ - - mutex_unlock(&con->mutex); - if (con->ops->bad_proto) - con->ops->bad_proto(con); - mutex_lock(&con->mutex); -} - -static int process_connect(struct ceph_connection *con) -{ - u64 sup_feat = CEPH_FEATURE_SUPPORTED; - u64 req_feat = CEPH_FEATURE_REQUIRED; - u64 server_feat = le64_to_cpu(con->in_reply.features); - - dout("process_connect on %p tag %d\n", con, (int)con->in_tag); - - switch (con->in_reply.tag) { - case CEPH_MSGR_TAG_FEATURES: - pr_err("%s%lld %s feature set mismatch," - " my %llx < server's %llx, missing %llx\n", - ENTITY_NAME(con->peer_name), - pr_addr(&con->peer_addr.in_addr), - sup_feat, server_feat, server_feat & ~sup_feat); - con->error_msg = "missing required protocol features"; - fail_protocol(con); - return -1; - - case CEPH_MSGR_TAG_BADPROTOVER: - pr_err("%s%lld %s protocol version mismatch," - " my %d != server's %d\n", - ENTITY_NAME(con->peer_name), - pr_addr(&con->peer_addr.in_addr), - le32_to_cpu(con->out_connect.protocol_version), - le32_to_cpu(con->in_reply.protocol_version)); - con->error_msg = "protocol version mismatch"; - fail_protocol(con); - return -1; - - case CEPH_MSGR_TAG_BADAUTHORIZER: - con->auth_retry++; - dout("process_connect %p got BADAUTHORIZER attempt %d\n", con, - con->auth_retry); - if (con->auth_retry == 2) { - con->error_msg = "connect authorization failure"; - reset_connection(con); - set_bit(CLOSED, &con->state); - return -1; - } - con->auth_retry = 1; - prepare_write_connect(con->msgr, con, 0); - prepare_read_connect(con); - break; - - case CEPH_MSGR_TAG_RESETSESSION: - /* - * If we connected with a large connect_seq but the peer - * has no record of a session with us (no connection, or - * connect_seq == 0), they will send RESETSESION to indicate - * that they must have reset their session, and may have - * dropped messages. - */ - dout("process_connect got RESET peer seq %u\n", - le32_to_cpu(con->in_connect.connect_seq)); - pr_err("%s%lld %s connection reset\n", - ENTITY_NAME(con->peer_name), - pr_addr(&con->peer_addr.in_addr)); - reset_connection(con); - prepare_write_connect(con->msgr, con, 0); - prepare_read_connect(con); - - /* Tell ceph about it. */ - mutex_unlock(&con->mutex); - pr_info("reset on %s%lld\n", ENTITY_NAME(con->peer_name)); - if (con->ops->peer_reset) - con->ops->peer_reset(con); - mutex_lock(&con->mutex); - break; - - case CEPH_MSGR_TAG_RETRY_SESSION: - /* - * If we sent a smaller connect_seq than the peer has, try - * again with a larger value. - */ - dout("process_connect got RETRY my seq = %u, peer_seq = %u\n", - le32_to_cpu(con->out_connect.connect_seq), - le32_to_cpu(con->in_connect.connect_seq)); - con->connect_seq = le32_to_cpu(con->in_connect.connect_seq); - prepare_write_connect(con->msgr, con, 0); - prepare_read_connect(con); - break; - - case CEPH_MSGR_TAG_RETRY_GLOBAL: - /* - * If we sent a smaller global_seq than the peer has, try - * again with a larger value. - */ - dout("process_connect got RETRY_GLOBAL my %u peer_gseq %u\n", - con->peer_global_seq, - le32_to_cpu(con->in_connect.global_seq)); - get_global_seq(con->msgr, - le32_to_cpu(con->in_connect.global_seq)); - prepare_write_connect(con->msgr, con, 0); - prepare_read_connect(con); - break; - - case CEPH_MSGR_TAG_READY: - if (req_feat & ~server_feat) { - pr_err("%s%lld %s protocol feature mismatch," - " my required %llx > server's %llx, need %llx\n", - ENTITY_NAME(con->peer_name), - pr_addr(&con->peer_addr.in_addr), - req_feat, server_feat, req_feat & ~server_feat); - con->error_msg = "missing required protocol features"; - fail_protocol(con); - return -1; - } - clear_bit(CONNECTING, &con->state); - con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq); - con->connect_seq++; - con->peer_features = server_feat; - dout("process_connect got READY gseq %d cseq %d (%d)\n", - con->peer_global_seq, - le32_to_cpu(con->in_reply.connect_seq), - con->connect_seq); - WARN_ON(con->connect_seq != - le32_to_cpu(con->in_reply.connect_seq)); - - if (con->in_reply.flags & CEPH_MSG_CONNECT_LOSSY) - set_bit(LOSSYTX, &con->state); - - prepare_read_tag(con); - break; - - case CEPH_MSGR_TAG_WAIT: - /* - * If there is a connection race (we are opening - * connections to each other), one of us may just have - * to WAIT. This shouldn't happen if we are the - * client. - */ - pr_err("process_connect peer connecting WAIT\n"); - - default: - pr_err("connect protocol error, will retry\n"); - con->error_msg = "protocol error, garbage tag during connect"; - return -1; - } - return 0; -} - - -/* - * read (part of) an ack - */ -static int read_partial_ack(struct ceph_connection *con) -{ - int to = 0; - - return read_partial(con, &to, sizeof(con->in_temp_ack), - &con->in_temp_ack); -} - - -/* - * We can finally discard anything that's been acked. - */ -static void process_ack(struct ceph_connection *con) -{ - struct ceph_msg *m; - u64 ack = le64_to_cpu(con->in_temp_ack); - u64 seq; - - while (!list_empty(&con->out_sent)) { - m = list_first_entry(&con->out_sent, struct ceph_msg, - list_head); - seq = le64_to_cpu(m->hdr.seq); - if (seq > ack) - break; - dout("got ack for seq %llu type %d at %p\n", seq, - le16_to_cpu(m->hdr.type), m); - ceph_msg_remove(m); - } - prepare_read_tag(con); -} - - - - -static int read_partial_message_section(struct ceph_connection *con, - struct kvec *section, - unsigned int sec_len, u32 *crc) -{ - int ret, left; - - BUG_ON(!section); - - while (section->iov_len < sec_len) { - BUG_ON(section->iov_base == NULL); - left = sec_len - section->iov_len; - ret = ceph_tcp_recvmsg(con->sock, (char *)section->iov_base + - section->iov_len, left); - if (ret <= 0) - return ret; - section->iov_len += ret; - if (section->iov_len == sec_len) - *crc = crc32c(0, section->iov_base, - section->iov_len); - } - - return 1; -} - -static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, - struct ceph_msg_header *hdr, - int *skip); - - -static int read_partial_message_pages(struct ceph_connection *con, - struct page **pages, - unsigned data_len, int datacrc) -{ - void *p; - int ret; - int left; - - left = min((int)(data_len - con->in_msg_pos.data_pos), - (int)(PAGE_SIZE - con->in_msg_pos.page_pos)); - /* (page) data */ - BUG_ON(pages == NULL); - p = kmap(pages[con->in_msg_pos.page]); - ret = ceph_tcp_recvmsg(con->sock, p + con->in_msg_pos.page_pos, - left); - if (ret > 0 && datacrc) - con->in_data_crc = - crc32c(con->in_data_crc, - p + con->in_msg_pos.page_pos, ret); - kunmap(pages[con->in_msg_pos.page]); - if (ret <= 0) - return ret; - con->in_msg_pos.data_pos += ret; - con->in_msg_pos.page_pos += ret; - if (con->in_msg_pos.page_pos == PAGE_SIZE) { - con->in_msg_pos.page_pos = 0; - con->in_msg_pos.page++; - } - - return ret; -} - -#ifdef CONFIG_BLOCK -static int read_partial_message_bio(struct ceph_connection *con, - struct bio **bio_iter, int *bio_seg, - unsigned data_len, int datacrc) -{ - struct bio_vec *bv = bio_iovec_idx(*bio_iter, *bio_seg); - void *p; - int ret, left; - - if (IS_ERR(bv)) - return PTR_ERR(bv); - - left = min((int)(data_len - con->in_msg_pos.data_pos), - (int)(bv->bv_len - con->in_msg_pos.page_pos)); - - p = kmap(bv->bv_page) + bv->bv_offset; - - ret = ceph_tcp_recvmsg(con->sock, p + con->in_msg_pos.page_pos, - left); - if (ret > 0 && datacrc) - con->in_data_crc = - crc32c(con->in_data_crc, - p + con->in_msg_pos.page_pos, ret); - kunmap(bv->bv_page); - if (ret <= 0) - return ret; - con->in_msg_pos.data_pos += ret; - con->in_msg_pos.page_pos += ret; - if (con->in_msg_pos.page_pos == bv->bv_len) { - con->in_msg_pos.page_pos = 0; - iter_bio_next(bio_iter, bio_seg); - } - - return ret; -} -#endif - -/* - * read (part of) a message. - */ -static int read_partial_message(struct ceph_connection *con) -{ - struct ceph_msg *m = con->in_msg; - int ret; - int to, left; - unsigned front_len, middle_len, data_len, data_off; - int datacrc = con->msgr->nocrc; - int skip; - u64 seq; - - dout("read_partial_message con %p msg %p\n", con, m); - - /* header */ - while (con->in_base_pos < sizeof(con->in_hdr)) { - left = sizeof(con->in_hdr) - con->in_base_pos; - ret = ceph_tcp_recvmsg(con->sock, - (char *)&con->in_hdr + con->in_base_pos, - left); - if (ret <= 0) - return ret; - con->in_base_pos += ret; - if (con->in_base_pos == sizeof(con->in_hdr)) { - u32 crc = crc32c(0, (void *)&con->in_hdr, - sizeof(con->in_hdr) - sizeof(con->in_hdr.crc)); - if (crc != le32_to_cpu(con->in_hdr.crc)) { - pr_err("read_partial_message bad hdr " - " crc %u != expected %u\n", - crc, con->in_hdr.crc); - return -EBADMSG; - } - } - } - front_len = le32_to_cpu(con->in_hdr.front_len); - if (front_len > CEPH_MSG_MAX_FRONT_LEN) - return -EIO; - middle_len = le32_to_cpu(con->in_hdr.middle_len); - if (middle_len > CEPH_MSG_MAX_DATA_LEN) - return -EIO; - data_len = le32_to_cpu(con->in_hdr.data_len); - if (data_len > CEPH_MSG_MAX_DATA_LEN) - return -EIO; - data_off = le16_to_cpu(con->in_hdr.data_off); - - /* verify seq# */ - seq = le64_to_cpu(con->in_hdr.seq); - if ((s64)seq - (s64)con->in_seq < 1) { - pr_info("skipping %s%lld %s seq %lld, expected %lld\n", - ENTITY_NAME(con->peer_name), - pr_addr(&con->peer_addr.in_addr), - seq, con->in_seq + 1); - con->in_base_pos = -front_len - middle_len - data_len - - sizeof(m->footer); - con->in_tag = CEPH_MSGR_TAG_READY; - con->in_seq++; - return 0; - } else if ((s64)seq - (s64)con->in_seq > 1) { - pr_err("read_partial_message bad seq %lld expected %lld\n", - seq, con->in_seq + 1); - con->error_msg = "bad message sequence # for incoming message"; - return -EBADMSG; - } - - /* allocate message? */ - if (!con->in_msg) { - dout("got hdr type %d front %d data %d\n", con->in_hdr.type, - con->in_hdr.front_len, con->in_hdr.data_len); - skip = 0; - con->in_msg = ceph_alloc_msg(con, &con->in_hdr, &skip); - if (skip) { - /* skip this message */ - dout("alloc_msg said skip message\n"); - BUG_ON(con->in_msg); - con->in_base_pos = -front_len - middle_len - data_len - - sizeof(m->footer); - con->in_tag = CEPH_MSGR_TAG_READY; - con->in_seq++; - return 0; - } - if (!con->in_msg) { - con->error_msg = - "error allocating memory for incoming message"; - return -ENOMEM; - } - m = con->in_msg; - m->front.iov_len = 0; /* haven't read it yet */ - if (m->middle) - m->middle->vec.iov_len = 0; - - con->in_msg_pos.page = 0; - if (m->pages) - con->in_msg_pos.page_pos = data_off & ~PAGE_MASK; - else - con->in_msg_pos.page_pos = 0; - con->in_msg_pos.data_pos = 0; - } - - /* front */ - ret = read_partial_message_section(con, &m->front, front_len, - &con->in_front_crc); - if (ret <= 0) - return ret; - - /* middle */ - if (m->middle) { - ret = read_partial_message_section(con, &m->middle->vec, - middle_len, - &con->in_middle_crc); - if (ret <= 0) - return ret; - } -#ifdef CONFIG_BLOCK - if (m->bio && !m->bio_iter) - init_bio_iter(m->bio, &m->bio_iter, &m->bio_seg); -#endif - - /* (page) data */ - while (con->in_msg_pos.data_pos < data_len) { - if (m->pages) { - ret = read_partial_message_pages(con, m->pages, - data_len, datacrc); - if (ret <= 0) - return ret; -#ifdef CONFIG_BLOCK - } else if (m->bio) { - - ret = read_partial_message_bio(con, - &m->bio_iter, &m->bio_seg, - data_len, datacrc); - if (ret <= 0) - return ret; -#endif - } else { - BUG_ON(1); - } - } - - /* footer */ - to = sizeof(m->hdr) + sizeof(m->footer); - while (con->in_base_pos < to) { - left = to - con->in_base_pos; - ret = ceph_tcp_recvmsg(con->sock, (char *)&m->footer + - (con->in_base_pos - sizeof(m->hdr)), - left); - if (ret <= 0) - return ret; - con->in_base_pos += ret; - } - dout("read_partial_message got msg %p %d (%u) + %d (%u) + %d (%u)\n", - m, front_len, m->footer.front_crc, middle_len, - m->footer.middle_crc, data_len, m->footer.data_crc); - - /* crc ok? */ - if (con->in_front_crc != le32_to_cpu(m->footer.front_crc)) { - pr_err("read_partial_message %p front crc %u != exp. %u\n", - m, con->in_front_crc, m->footer.front_crc); - return -EBADMSG; - } - if (con->in_middle_crc != le32_to_cpu(m->footer.middle_crc)) { - pr_err("read_partial_message %p middle crc %u != exp %u\n", - m, con->in_middle_crc, m->footer.middle_crc); - return -EBADMSG; - } - if (datacrc && - (m->footer.flags & CEPH_MSG_FOOTER_NOCRC) == 0 && - con->in_data_crc != le32_to_cpu(m->footer.data_crc)) { - pr_err("read_partial_message %p data crc %u != exp. %u\n", m, - con->in_data_crc, le32_to_cpu(m->footer.data_crc)); - return -EBADMSG; - } - - return 1; /* done! */ -} - -/* - * Process message. This happens in the worker thread. The callback should - * be careful not to do anything that waits on other incoming messages or it - * may deadlock. - */ -static void process_message(struct ceph_connection *con) -{ - struct ceph_msg *msg; - - msg = con->in_msg; - con->in_msg = NULL; - - /* if first message, set peer_name */ - if (con->peer_name.type == 0) - con->peer_name = msg->hdr.src; - - con->in_seq++; - mutex_unlock(&con->mutex); - - dout("===== %p %llu from %s%lld %d=%s len %d+%d (%u %u %u) =====\n", - msg, le64_to_cpu(msg->hdr.seq), - ENTITY_NAME(msg->hdr.src), - le16_to_cpu(msg->hdr.type), - ceph_msg_type_name(le16_to_cpu(msg->hdr.type)), - le32_to_cpu(msg->hdr.front_len), - le32_to_cpu(msg->hdr.data_len), - con->in_front_crc, con->in_middle_crc, con->in_data_crc); - con->ops->dispatch(con, msg); - - mutex_lock(&con->mutex); - prepare_read_tag(con); -} - - -/* - * Write something to the socket. Called in a worker thread when the - * socket appears to be writeable and we have something ready to send. - */ -static int try_write(struct ceph_connection *con) -{ - struct ceph_messenger *msgr = con->msgr; - int ret = 1; - - dout("try_write start %p state %lu nref %d\n", con, con->state, - atomic_read(&con->nref)); - -more: - dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes); - - /* open the socket first? */ - if (con->sock == NULL) { - /* - * if we were STANDBY and are reconnecting _this_ - * connection, bump connect_seq now. Always bump - * global_seq. - */ - if (test_and_clear_bit(STANDBY, &con->state)) - con->connect_seq++; - - prepare_write_banner(msgr, con); - prepare_write_connect(msgr, con, 1); - prepare_read_banner(con); - set_bit(CONNECTING, &con->state); - clear_bit(NEGOTIATING, &con->state); - - BUG_ON(con->in_msg); - con->in_tag = CEPH_MSGR_TAG_READY; - dout("try_write initiating connect on %p new state %lu\n", - con, con->state); - con->sock = ceph_tcp_connect(con); - if (IS_ERR(con->sock)) { - con->sock = NULL; - con->error_msg = "connect error"; - ret = -1; - goto out; - } - } - -more_kvec: - /* kvec data queued? */ - if (con->out_skip) { - ret = write_partial_skip(con); - if (ret <= 0) - goto done; - if (ret < 0) { - dout("try_write write_partial_skip err %d\n", ret); - goto done; - } - } - if (con->out_kvec_left) { - ret = write_partial_kvec(con); - if (ret <= 0) - goto done; - } - - /* msg pages? */ - if (con->out_msg) { - if (con->out_msg_done) { - ceph_msg_put(con->out_msg); - con->out_msg = NULL; /* we're done with this one */ - goto do_next; - } - - ret = write_partial_msg_pages(con); - if (ret == 1) - goto more_kvec; /* we need to send the footer, too! */ - if (ret == 0) - goto done; - if (ret < 0) { - dout("try_write write_partial_msg_pages err %d\n", - ret); - goto done; - } - } - -do_next: - if (!test_bit(CONNECTING, &con->state)) { - /* is anything else pending? */ - if (!list_empty(&con->out_queue)) { - prepare_write_message(con); - goto more; - } - if (con->in_seq > con->in_seq_acked) { - prepare_write_ack(con); - goto more; - } - if (test_and_clear_bit(KEEPALIVE_PENDING, &con->state)) { - prepare_write_keepalive(con); - goto more; - } - } - - /* Nothing to do! */ - clear_bit(WRITE_PENDING, &con->state); - dout("try_write nothing else to write.\n"); -done: - ret = 0; -out: - dout("try_write done on %p\n", con); - return ret; -} - - - -/* - * Read what we can from the socket. - */ -static int try_read(struct ceph_connection *con) -{ - int ret = -1; - - if (!con->sock) - return 0; - - if (test_bit(STANDBY, &con->state)) - return 0; - - dout("try_read start on %p\n", con); - -more: - dout("try_read tag %d in_base_pos %d\n", (int)con->in_tag, - con->in_base_pos); - if (test_bit(CONNECTING, &con->state)) { - if (!test_bit(NEGOTIATING, &con->state)) { - dout("try_read connecting\n"); - ret = read_partial_banner(con); - if (ret <= 0) - goto done; - if (process_banner(con) < 0) { - ret = -1; - goto out; - } - } - ret = read_partial_connect(con); - if (ret <= 0) - goto done; - if (process_connect(con) < 0) { - ret = -1; - goto out; - } - goto more; - } - - if (con->in_base_pos < 0) { - /* - * skipping + discarding content. - * - * FIXME: there must be a better way to do this! - */ - static char buf[1024]; - int skip = min(1024, -con->in_base_pos); - dout("skipping %d / %d bytes\n", skip, -con->in_base_pos); - ret = ceph_tcp_recvmsg(con->sock, buf, skip); - if (ret <= 0) - goto done; - con->in_base_pos += ret; - if (con->in_base_pos) - goto more; - } - if (con->in_tag == CEPH_MSGR_TAG_READY) { - /* - * what's next? - */ - ret = ceph_tcp_recvmsg(con->sock, &con->in_tag, 1); - if (ret <= 0) - goto done; - dout("try_read got tag %d\n", (int)con->in_tag); - switch (con->in_tag) { - case CEPH_MSGR_TAG_MSG: - prepare_read_message(con); - break; - case CEPH_MSGR_TAG_ACK: - prepare_read_ack(con); - break; - case CEPH_MSGR_TAG_CLOSE: - set_bit(CLOSED, &con->state); /* fixme */ - goto done; - default: - goto bad_tag; - } - } - if (con->in_tag == CEPH_MSGR_TAG_MSG) { - ret = read_partial_message(con); - if (ret <= 0) { - switch (ret) { - case -EBADMSG: - con->error_msg = "bad crc"; - ret = -EIO; - goto out; - case -EIO: - con->error_msg = "io error"; - goto out; - default: - goto done; - } - } - if (con->in_tag == CEPH_MSGR_TAG_READY) - goto more; - process_message(con); - goto more; - } - if (con->in_tag == CEPH_MSGR_TAG_ACK) { - ret = read_partial_ack(con); - if (ret <= 0) - goto done; - process_ack(con); - goto more; - } - -done: - ret = 0; -out: - dout("try_read done on %p\n", con); - return ret; - -bad_tag: - pr_err("try_read bad con->in_tag = %d\n", (int)con->in_tag); - con->error_msg = "protocol error, garbage tag"; - ret = -1; - goto out; -} - - -/* - * Atomically queue work on a connection. Bump @con reference to - * avoid races with connection teardown. - * - * There is some trickery going on with QUEUED and BUSY because we - * only want a _single_ thread operating on each connection at any - * point in time, but we want to use all available CPUs. - * - * The worker thread only proceeds if it can atomically set BUSY. It - * clears QUEUED and does it's thing. When it thinks it's done, it - * clears BUSY, then rechecks QUEUED.. if it's set again, it loops - * (tries again to set BUSY). - * - * To queue work, we first set QUEUED, _then_ if BUSY isn't set, we - * try to queue work. If that fails (work is already queued, or BUSY) - * we give up (work also already being done or is queued) but leave QUEUED - * set so that the worker thread will loop if necessary. - */ -static void queue_con(struct ceph_connection *con) -{ - if (test_bit(DEAD, &con->state)) { - dout("queue_con %p ignoring: DEAD\n", - con); - return; - } - - if (!con->ops->get(con)) { - dout("queue_con %p ref count 0\n", con); - return; - } - - set_bit(QUEUED, &con->state); - if (test_bit(BUSY, &con->state)) { - dout("queue_con %p - already BUSY\n", con); - con->ops->put(con); - } else if (!queue_work(ceph_msgr_wq, &con->work.work)) { - dout("queue_con %p - already queued\n", con); - con->ops->put(con); - } else { - dout("queue_con %p\n", con); - } -} - -/* - * Do some work on a connection. Drop a connection ref when we're done. - */ -static void con_work(struct work_struct *work) -{ - struct ceph_connection *con = container_of(work, struct ceph_connection, - work.work); - int backoff = 0; - -more: - if (test_and_set_bit(BUSY, &con->state) != 0) { - dout("con_work %p BUSY already set\n", con); - goto out; - } - dout("con_work %p start, clearing QUEUED\n", con); - clear_bit(QUEUED, &con->state); - - mutex_lock(&con->mutex); - - if (test_bit(CLOSED, &con->state)) { /* e.g. if we are replaced */ - dout("con_work CLOSED\n"); - con_close_socket(con); - goto done; - } - if (test_and_clear_bit(OPENING, &con->state)) { - /* reopen w/ new peer */ - dout("con_work OPENING\n"); - con_close_socket(con); - } - - if (test_and_clear_bit(SOCK_CLOSED, &con->state) || - try_read(con) < 0 || - try_write(con) < 0) { - mutex_unlock(&con->mutex); - backoff = 1; - ceph_fault(con); /* error/fault path */ - goto done_unlocked; - } - -done: - mutex_unlock(&con->mutex); - -done_unlocked: - clear_bit(BUSY, &con->state); - dout("con->state=%lu\n", con->state); - if (test_bit(QUEUED, &con->state)) { - if (!backoff || test_bit(OPENING, &con->state)) { - dout("con_work %p QUEUED reset, looping\n", con); - goto more; - } - dout("con_work %p QUEUED reset, but just faulted\n", con); - clear_bit(QUEUED, &con->state); - } - dout("con_work %p done\n", con); - -out: - con->ops->put(con); -} - - -/* - * Generic error/fault handler. A retry mechanism is used with - * exponential backoff - */ -static void ceph_fault(struct ceph_connection *con) -{ - pr_err("%s%lld %s %s\n", ENTITY_NAME(con->peer_name), - pr_addr(&con->peer_addr.in_addr), con->error_msg); - dout("fault %p state %lu to peer %s\n", - con, con->state, pr_addr(&con->peer_addr.in_addr)); - - if (test_bit(LOSSYTX, &con->state)) { - dout("fault on LOSSYTX channel\n"); - goto out; - } - - mutex_lock(&con->mutex); - if (test_bit(CLOSED, &con->state)) - goto out_unlock; - - con_close_socket(con); - - if (con->in_msg) { - ceph_msg_put(con->in_msg); - con->in_msg = NULL; - } - - /* Requeue anything that hasn't been acked */ - list_splice_init(&con->out_sent, &con->out_queue); - - /* If there are no messages in the queue, place the connection - * in a STANDBY state (i.e., don't try to reconnect just yet). */ - if (list_empty(&con->out_queue) && !con->out_keepalive_pending) { - dout("fault setting STANDBY\n"); - set_bit(STANDBY, &con->state); - } else { - /* retry after a delay. */ - if (con->delay == 0) - con->delay = BASE_DELAY_INTERVAL; - else if (con->delay < MAX_DELAY_INTERVAL) - con->delay *= 2; - dout("fault queueing %p delay %lu\n", con, con->delay); - con->ops->get(con); - if (queue_delayed_work(ceph_msgr_wq, &con->work, - round_jiffies_relative(con->delay)) == 0) - con->ops->put(con); - } - -out_unlock: - mutex_unlock(&con->mutex); -out: - /* - * in case we faulted due to authentication, invalidate our - * current tickets so that we can get new ones. - */ - if (con->auth_retry && con->ops->invalidate_authorizer) { - dout("calling invalidate_authorizer()\n"); - con->ops->invalidate_authorizer(con); - } - - if (con->ops->fault) - con->ops->fault(con); -} - - - -/* - * create a new messenger instance - */ -struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr) -{ - struct ceph_messenger *msgr; - - msgr = kzalloc(sizeof(*msgr), GFP_KERNEL); - if (msgr == NULL) - return ERR_PTR(-ENOMEM); - - spin_lock_init(&msgr->global_seq_lock); - - /* the zero page is needed if a request is "canceled" while the message - * is being written over the socket */ - msgr->zero_page = __page_cache_alloc(GFP_KERNEL | __GFP_ZERO); - if (!msgr->zero_page) { - kfree(msgr); - return ERR_PTR(-ENOMEM); - } - kmap(msgr->zero_page); - - if (myaddr) - msgr->inst.addr = *myaddr; - - /* select a random nonce */ - msgr->inst.addr.type = 0; - get_random_bytes(&msgr->inst.addr.nonce, sizeof(msgr->inst.addr.nonce)); - encode_my_addr(msgr); - - dout("messenger_create %p\n", msgr); - return msgr; -} - -void ceph_messenger_destroy(struct ceph_messenger *msgr) -{ - dout("destroy %p\n", msgr); - kunmap(msgr->zero_page); - __free_page(msgr->zero_page); - kfree(msgr); - dout("destroyed messenger %p\n", msgr); -} - -/* - * Queue up an outgoing message on the given connection. - */ -void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) -{ - if (test_bit(CLOSED, &con->state)) { - dout("con_send %p closed, dropping %p\n", con, msg); - ceph_msg_put(msg); - return; - } - - /* set src+dst */ - msg->hdr.src = con->msgr->inst.name; - - BUG_ON(msg->front.iov_len != le32_to_cpu(msg->hdr.front_len)); - - msg->needs_out_seq = true; - - /* queue */ - mutex_lock(&con->mutex); - BUG_ON(!list_empty(&msg->list_head)); - list_add_tail(&msg->list_head, &con->out_queue); - dout("----- %p to %s%lld %d=%s len %d+%d+%d -----\n", msg, - ENTITY_NAME(con->peer_name), le16_to_cpu(msg->hdr.type), - ceph_msg_type_name(le16_to_cpu(msg->hdr.type)), - le32_to_cpu(msg->hdr.front_len), - le32_to_cpu(msg->hdr.middle_len), - le32_to_cpu(msg->hdr.data_len)); - mutex_unlock(&con->mutex); - - /* if there wasn't anything waiting to send before, queue - * new work */ - if (test_and_set_bit(WRITE_PENDING, &con->state) == 0) - queue_con(con); -} - -/* - * Revoke a message that was previously queued for send - */ -void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) -{ - mutex_lock(&con->mutex); - if (!list_empty(&msg->list_head)) { - dout("con_revoke %p msg %p - was on queue\n", con, msg); - list_del_init(&msg->list_head); - ceph_msg_put(msg); - msg->hdr.seq = 0; - } - if (con->out_msg == msg) { - dout("con_revoke %p msg %p - was sending\n", con, msg); - con->out_msg = NULL; - if (con->out_kvec_is_msg) { - con->out_skip = con->out_kvec_bytes; - con->out_kvec_is_msg = false; - } - ceph_msg_put(msg); - msg->hdr.seq = 0; - } - mutex_unlock(&con->mutex); -} - -/* - * Revoke a message that we may be reading data into - */ -void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg) -{ - mutex_lock(&con->mutex); - if (con->in_msg && con->in_msg == msg) { - unsigned front_len = le32_to_cpu(con->in_hdr.front_len); - unsigned middle_len = le32_to_cpu(con->in_hdr.middle_len); - unsigned data_len = le32_to_cpu(con->in_hdr.data_len); - - /* skip rest of message */ - dout("con_revoke_pages %p msg %p revoked\n", con, msg); - con->in_base_pos = con->in_base_pos - - sizeof(struct ceph_msg_header) - - front_len - - middle_len - - data_len - - sizeof(struct ceph_msg_footer); - ceph_msg_put(con->in_msg); - con->in_msg = NULL; - con->in_tag = CEPH_MSGR_TAG_READY; - con->in_seq++; - } else { - dout("con_revoke_pages %p msg %p pages %p no-op\n", - con, con->in_msg, msg); - } - mutex_unlock(&con->mutex); -} - -/* - * Queue a keepalive byte to ensure the tcp connection is alive. - */ -void ceph_con_keepalive(struct ceph_connection *con) -{ - if (test_and_set_bit(KEEPALIVE_PENDING, &con->state) == 0 && - test_and_set_bit(WRITE_PENDING, &con->state) == 0) - queue_con(con); -} - - -/* - * construct a new message with given type, size - * the new msg has a ref count of 1. - */ -struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags) -{ - struct ceph_msg *m; - - m = kmalloc(sizeof(*m), flags); - if (m == NULL) - goto out; - kref_init(&m->kref); - INIT_LIST_HEAD(&m->list_head); - - m->hdr.tid = 0; - m->hdr.type = cpu_to_le16(type); - m->hdr.priority = cpu_to_le16(CEPH_MSG_PRIO_DEFAULT); - m->hdr.version = 0; - m->hdr.front_len = cpu_to_le32(front_len); - m->hdr.middle_len = 0; - m->hdr.data_len = 0; - m->hdr.data_off = 0; - m->hdr.reserved = 0; - m->footer.front_crc = 0; - m->footer.middle_crc = 0; - m->footer.data_crc = 0; - m->footer.flags = 0; - m->front_max = front_len; - m->front_is_vmalloc = false; - m->more_to_follow = false; - m->pool = NULL; - - /* front */ - if (front_len) { - if (front_len > PAGE_CACHE_SIZE) { - m->front.iov_base = __vmalloc(front_len, flags, - PAGE_KERNEL); - m->front_is_vmalloc = true; - } else { - m->front.iov_base = kmalloc(front_len, flags); - } - if (m->front.iov_base == NULL) { - pr_err("msg_new can't allocate %d bytes\n", - front_len); - goto out2; - } - } else { - m->front.iov_base = NULL; - } - m->front.iov_len = front_len; - - /* middle */ - m->middle = NULL; - - /* data */ - m->nr_pages = 0; - m->pages = NULL; - m->pagelist = NULL; - m->bio = NULL; - m->bio_iter = NULL; - m->bio_seg = 0; - m->trail = NULL; - - dout("ceph_msg_new %p front %d\n", m, front_len); - return m; - -out2: - ceph_msg_put(m); -out: - pr_err("msg_new can't create type %d front %d\n", type, front_len); - return NULL; -} - -/* - * Allocate "middle" portion of a message, if it is needed and wasn't - * allocated by alloc_msg. This allows us to read a small fixed-size - * per-type header in the front and then gracefully fail (i.e., - * propagate the error to the caller based on info in the front) when - * the middle is too large. - */ -static int ceph_alloc_middle(struct ceph_connection *con, struct ceph_msg *msg) -{ - int type = le16_to_cpu(msg->hdr.type); - int middle_len = le32_to_cpu(msg->hdr.middle_len); - - dout("alloc_middle %p type %d %s middle_len %d\n", msg, type, - ceph_msg_type_name(type), middle_len); - BUG_ON(!middle_len); - BUG_ON(msg->middle); - - msg->middle = ceph_buffer_new(middle_len, GFP_NOFS); - if (!msg->middle) - return -ENOMEM; - return 0; -} - -/* - * Generic message allocator, for incoming messages. - */ -static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, - struct ceph_msg_header *hdr, - int *skip) -{ - int type = le16_to_cpu(hdr->type); - int front_len = le32_to_cpu(hdr->front_len); - int middle_len = le32_to_cpu(hdr->middle_len); - struct ceph_msg *msg = NULL; - int ret; - - if (con->ops->alloc_msg) { - mutex_unlock(&con->mutex); - msg = con->ops->alloc_msg(con, hdr, skip); - mutex_lock(&con->mutex); - if (!msg || *skip) - return NULL; - } - if (!msg) { - *skip = 0; - msg = ceph_msg_new(type, front_len, GFP_NOFS); - if (!msg) { - pr_err("unable to allocate msg type %d len %d\n", - type, front_len); - return NULL; - } - } - memcpy(&msg->hdr, &con->in_hdr, sizeof(con->in_hdr)); - - if (middle_len && !msg->middle) { - ret = ceph_alloc_middle(con, msg); - if (ret < 0) { - ceph_msg_put(msg); - return NULL; - } - } - - return msg; -} - - -/* - * Free a generically kmalloc'd message. - */ -void ceph_msg_kfree(struct ceph_msg *m) -{ - dout("msg_kfree %p\n", m); - if (m->front_is_vmalloc) - vfree(m->front.iov_base); - else - kfree(m->front.iov_base); - kfree(m); -} - -/* - * Drop a msg ref. Destroy as needed. - */ -void ceph_msg_last_put(struct kref *kref) -{ - struct ceph_msg *m = container_of(kref, struct ceph_msg, kref); - - dout("ceph_msg_put last one on %p\n", m); - WARN_ON(!list_empty(&m->list_head)); - - /* drop middle, data, if any */ - if (m->middle) { - ceph_buffer_put(m->middle); - m->middle = NULL; - } - m->nr_pages = 0; - m->pages = NULL; - - if (m->pagelist) { - ceph_pagelist_release(m->pagelist); - kfree(m->pagelist); - m->pagelist = NULL; - } - - m->trail = NULL; - - if (m->pool) - ceph_msgpool_put(m->pool, m); - else - ceph_msg_kfree(m); -} - -void ceph_msg_dump(struct ceph_msg *msg) -{ - pr_debug("msg_dump %p (front_max %d nr_pages %d)\n", msg, - msg->front_max, msg->nr_pages); - print_hex_dump(KERN_DEBUG, "header: ", - DUMP_PREFIX_OFFSET, 16, 1, - &msg->hdr, sizeof(msg->hdr), true); - print_hex_dump(KERN_DEBUG, " front: ", - DUMP_PREFIX_OFFSET, 16, 1, - msg->front.iov_base, msg->front.iov_len, true); - if (msg->middle) - print_hex_dump(KERN_DEBUG, "middle: ", - DUMP_PREFIX_OFFSET, 16, 1, - msg->middle->vec.iov_base, - msg->middle->vec.iov_len, true); - print_hex_dump(KERN_DEBUG, "footer: ", - DUMP_PREFIX_OFFSET, 16, 1, - &msg->footer, sizeof(msg->footer), true); -} diff --git a/fs/ceph/messenger.h b/fs/ceph/messenger.h deleted file mode 100644 index 5a79450604e..00000000000 --- a/fs/ceph/messenger.h +++ /dev/null @@ -1,257 +0,0 @@ -#ifndef __FS_CEPH_MESSENGER_H -#define __FS_CEPH_MESSENGER_H - -#include -#include -#include -#include -#include -#include -#include - -#include "types.h" -#include "buffer.h" - -struct ceph_msg; -struct ceph_connection; - -extern struct workqueue_struct *ceph_msgr_wq; /* receive work queue */ - -/* - * Ceph defines these callbacks for handling connection events. - */ -struct ceph_connection_operations { - struct ceph_connection *(*get)(struct ceph_connection *); - void (*put)(struct ceph_connection *); - - /* handle an incoming message. */ - void (*dispatch) (struct ceph_connection *con, struct ceph_msg *m); - - /* authorize an outgoing connection */ - int (*get_authorizer) (struct ceph_connection *con, - void **buf, int *len, int *proto, - void **reply_buf, int *reply_len, int force_new); - int (*verify_authorizer_reply) (struct ceph_connection *con, int len); - int (*invalidate_authorizer)(struct ceph_connection *con); - - /* protocol version mismatch */ - void (*bad_proto) (struct ceph_connection *con); - - /* there was some error on the socket (disconnect, whatever) */ - void (*fault) (struct ceph_connection *con); - - /* a remote host as terminated a message exchange session, and messages - * we sent (or they tried to send us) may be lost. */ - void (*peer_reset) (struct ceph_connection *con); - - struct ceph_msg * (*alloc_msg) (struct ceph_connection *con, - struct ceph_msg_header *hdr, - int *skip); -}; - -/* use format string %s%d */ -#define ENTITY_NAME(n) ceph_entity_type_name((n).type), le64_to_cpu((n).num) - -struct ceph_messenger { - struct ceph_entity_inst inst; /* my name+address */ - struct ceph_entity_addr my_enc_addr; - struct page *zero_page; /* used in certain error cases */ - - bool nocrc; - - /* - * the global_seq counts connections i (attempt to) initiate - * in order to disambiguate certain connect race conditions. - */ - u32 global_seq; - spinlock_t global_seq_lock; -}; - -/* - * a single message. it contains a header (src, dest, message type, etc.), - * footer (crc values, mainly), a "front" message body, and possibly a - * data payload (stored in some number of pages). - */ -struct ceph_msg { - struct ceph_msg_header hdr; /* header */ - struct ceph_msg_footer footer; /* footer */ - struct kvec front; /* unaligned blobs of message */ - struct ceph_buffer *middle; - struct page **pages; /* data payload. NOT OWNER. */ - unsigned nr_pages; /* size of page array */ - struct ceph_pagelist *pagelist; /* instead of pages */ - struct list_head list_head; - struct kref kref; - struct bio *bio; /* instead of pages/pagelist */ - struct bio *bio_iter; /* bio iterator */ - int bio_seg; /* current bio segment */ - struct ceph_pagelist *trail; /* the trailing part of the data */ - bool front_is_vmalloc; - bool more_to_follow; - bool needs_out_seq; - int front_max; - - struct ceph_msgpool *pool; -}; - -struct ceph_msg_pos { - int page, page_pos; /* which page; offset in page */ - int data_pos; /* offset in data payload */ - int did_page_crc; /* true if we've calculated crc for current page */ -}; - -/* ceph connection fault delay defaults, for exponential backoff */ -#define BASE_DELAY_INTERVAL (HZ/2) -#define MAX_DELAY_INTERVAL (5 * 60 * HZ) - -/* - * ceph_connection state bit flags - * - * QUEUED and BUSY are used together to ensure that only a single - * thread is currently opening, reading or writing data to the socket. - */ -#define LOSSYTX 0 /* we can close channel or drop messages on errors */ -#define CONNECTING 1 -#define NEGOTIATING 2 -#define KEEPALIVE_PENDING 3 -#define WRITE_PENDING 4 /* we have data ready to send */ -#define QUEUED 5 /* there is work queued on this connection */ -#define BUSY 6 /* work is being done */ -#define STANDBY 8 /* no outgoing messages, socket closed. we keep - * the ceph_connection around to maintain shared - * state with the peer. */ -#define CLOSED 10 /* we've closed the connection */ -#define SOCK_CLOSED 11 /* socket state changed to closed */ -#define OPENING 13 /* open connection w/ (possibly new) peer */ -#define DEAD 14 /* dead, about to kfree */ - -/* - * A single connection with another host. - * - * We maintain a queue of outgoing messages, and some session state to - * ensure that we can preserve the lossless, ordered delivery of - * messages in the case of a TCP disconnect. - */ -struct ceph_connection { - void *private; - atomic_t nref; - - const struct ceph_connection_operations *ops; - - struct ceph_messenger *msgr; - struct socket *sock; - unsigned long state; /* connection state (see flags above) */ - const char *error_msg; /* error message, if any */ - - struct ceph_entity_addr peer_addr; /* peer address */ - struct ceph_entity_name peer_name; /* peer name */ - struct ceph_entity_addr peer_addr_for_me; - unsigned peer_features; - u32 connect_seq; /* identify the most recent connection - attempt for this connection, client */ - u32 peer_global_seq; /* peer's global seq for this connection */ - - int auth_retry; /* true if we need a newer authorizer */ - void *auth_reply_buf; /* where to put the authorizer reply */ - int auth_reply_buf_len; - - struct mutex mutex; - - /* out queue */ - struct list_head out_queue; - struct list_head out_sent; /* sending or sent but unacked */ - u64 out_seq; /* last message queued for send */ - bool out_keepalive_pending; - - u64 in_seq, in_seq_acked; /* last message received, acked */ - - /* connection negotiation temps */ - char in_banner[CEPH_BANNER_MAX_LEN]; - union { - struct { /* outgoing connection */ - struct ceph_msg_connect out_connect; - struct ceph_msg_connect_reply in_reply; - }; - struct { /* incoming */ - struct ceph_msg_connect in_connect; - struct ceph_msg_connect_reply out_reply; - }; - }; - struct ceph_entity_addr actual_peer_addr; - - /* message out temps */ - struct ceph_msg *out_msg; /* sending message (== tail of - out_sent) */ - bool out_msg_done; - struct ceph_msg_pos out_msg_pos; - - struct kvec out_kvec[8], /* sending header/footer data */ - *out_kvec_cur; - int out_kvec_left; /* kvec's left in out_kvec */ - int out_skip; /* skip this many bytes */ - int out_kvec_bytes; /* total bytes left */ - bool out_kvec_is_msg; /* kvec refers to out_msg */ - int out_more; /* there is more data after the kvecs */ - __le64 out_temp_ack; /* for writing an ack */ - - /* message in temps */ - struct ceph_msg_header in_hdr; - struct ceph_msg *in_msg; - struct ceph_msg_pos in_msg_pos; - u32 in_front_crc, in_middle_crc, in_data_crc; /* calculated crc */ - - char in_tag; /* protocol control byte */ - int in_base_pos; /* bytes read */ - __le64 in_temp_ack; /* for reading an ack */ - - struct delayed_work work; /* send|recv work */ - unsigned long delay; /* current delay interval */ -}; - - -extern const char *pr_addr(const struct sockaddr_storage *ss); -extern int ceph_parse_ips(const char *c, const char *end, - struct ceph_entity_addr *addr, - int max_count, int *count); - - -extern int ceph_msgr_init(void); -extern void ceph_msgr_exit(void); -extern void ceph_msgr_flush(void); - -extern struct ceph_messenger *ceph_messenger_create( - struct ceph_entity_addr *myaddr); -extern void ceph_messenger_destroy(struct ceph_messenger *); - -extern void ceph_con_init(struct ceph_messenger *msgr, - struct ceph_connection *con); -extern void ceph_con_open(struct ceph_connection *con, - struct ceph_entity_addr *addr); -extern bool ceph_con_opened(struct ceph_connection *con); -extern void ceph_con_close(struct ceph_connection *con); -extern void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg); -extern void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg); -extern void ceph_con_revoke_message(struct ceph_connection *con, - struct ceph_msg *msg); -extern void ceph_con_keepalive(struct ceph_connection *con); -extern struct ceph_connection *ceph_con_get(struct ceph_connection *con); -extern void ceph_con_put(struct ceph_connection *con); - -extern struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags); -extern void ceph_msg_kfree(struct ceph_msg *m); - - -static inline struct ceph_msg *ceph_msg_get(struct ceph_msg *msg) -{ - kref_get(&msg->kref); - return msg; -} -extern void ceph_msg_last_put(struct kref *kref); -static inline void ceph_msg_put(struct ceph_msg *msg) -{ - kref_put(&msg->kref, ceph_msg_last_put); -} - -extern void ceph_msg_dump(struct ceph_msg *msg); - -#endif diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c deleted file mode 100644 index b2a5a3e4a67..00000000000 --- a/fs/ceph/mon_client.c +++ /dev/null @@ -1,1018 +0,0 @@ -#include "ceph_debug.h" - -#include -#include -#include -#include - -#include "mon_client.h" -#include "super.h" -#include "auth.h" -#include "decode.h" - -/* - * Interact with Ceph monitor cluster. Handle requests for new map - * versions, and periodically resend as needed. Also implement - * statfs() and umount(). - * - * A small cluster of Ceph "monitors" are responsible for managing critical - * cluster configuration and state information. An odd number (e.g., 3, 5) - * of cmon daemons use a modified version of the Paxos part-time parliament - * algorithm to manage the MDS map (mds cluster membership), OSD map, and - * list of clients who have mounted the file system. - * - * We maintain an open, active session with a monitor at all times in order to - * receive timely MDSMap updates. We periodically send a keepalive byte on the - * TCP socket to ensure we detect a failure. If the connection does break, we - * randomly hunt for a new monitor. Once the connection is reestablished, we - * resend any outstanding requests. - */ - -static const struct ceph_connection_operations mon_con_ops; - -static int __validate_auth(struct ceph_mon_client *monc); - -/* - * Decode a monmap blob (e.g., during mount). - */ -struct ceph_monmap *ceph_monmap_decode(void *p, void *end) -{ - struct ceph_monmap *m = NULL; - int i, err = -EINVAL; - struct ceph_fsid fsid; - u32 epoch, num_mon; - u16 version; - u32 len; - - ceph_decode_32_safe(&p, end, len, bad); - ceph_decode_need(&p, end, len, bad); - - dout("monmap_decode %p %p len %d\n", p, end, (int)(end-p)); - - ceph_decode_16_safe(&p, end, version, bad); - - ceph_decode_need(&p, end, sizeof(fsid) + 2*sizeof(u32), bad); - ceph_decode_copy(&p, &fsid, sizeof(fsid)); - epoch = ceph_decode_32(&p); - - num_mon = ceph_decode_32(&p); - ceph_decode_need(&p, end, num_mon*sizeof(m->mon_inst[0]), bad); - - if (num_mon >= CEPH_MAX_MON) - goto bad; - m = kmalloc(sizeof(*m) + sizeof(m->mon_inst[0])*num_mon, GFP_NOFS); - if (m == NULL) - return ERR_PTR(-ENOMEM); - m->fsid = fsid; - m->epoch = epoch; - m->num_mon = num_mon; - ceph_decode_copy(&p, m->mon_inst, num_mon*sizeof(m->mon_inst[0])); - for (i = 0; i < num_mon; i++) - ceph_decode_addr(&m->mon_inst[i].addr); - - dout("monmap_decode epoch %d, num_mon %d\n", m->epoch, - m->num_mon); - for (i = 0; i < m->num_mon; i++) - dout("monmap_decode mon%d is %s\n", i, - pr_addr(&m->mon_inst[i].addr.in_addr)); - return m; - -bad: - dout("monmap_decode failed with %d\n", err); - kfree(m); - return ERR_PTR(err); -} - -/* - * return true if *addr is included in the monmap. - */ -int ceph_monmap_contains(struct ceph_monmap *m, struct ceph_entity_addr *addr) -{ - int i; - - for (i = 0; i < m->num_mon; i++) - if (memcmp(addr, &m->mon_inst[i].addr, sizeof(*addr)) == 0) - return 1; - return 0; -} - -/* - * Send an auth request. - */ -static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) -{ - monc->pending_auth = 1; - monc->m_auth->front.iov_len = len; - monc->m_auth->hdr.front_len = cpu_to_le32(len); - ceph_con_revoke(monc->con, monc->m_auth); - ceph_msg_get(monc->m_auth); /* keep our ref */ - ceph_con_send(monc->con, monc->m_auth); -} - -/* - * Close monitor session, if any. - */ -static void __close_session(struct ceph_mon_client *monc) -{ - if (monc->con) { - dout("__close_session closing mon%d\n", monc->cur_mon); - ceph_con_revoke(monc->con, monc->m_auth); - ceph_con_close(monc->con); - monc->cur_mon = -1; - monc->pending_auth = 0; - ceph_auth_reset(monc->auth); - } -} - -/* - * Open a session with a (new) monitor. - */ -static int __open_session(struct ceph_mon_client *monc) -{ - char r; - int ret; - - if (monc->cur_mon < 0) { - get_random_bytes(&r, 1); - monc->cur_mon = r % monc->monmap->num_mon; - dout("open_session num=%d r=%d -> mon%d\n", - monc->monmap->num_mon, r, monc->cur_mon); - monc->sub_sent = 0; - monc->sub_renew_after = jiffies; /* i.e., expired */ - monc->want_next_osdmap = !!monc->want_next_osdmap; - - dout("open_session mon%d opening\n", monc->cur_mon); - monc->con->peer_name.type = CEPH_ENTITY_TYPE_MON; - monc->con->peer_name.num = cpu_to_le64(monc->cur_mon); - ceph_con_open(monc->con, - &monc->monmap->mon_inst[monc->cur_mon].addr); - - /* initiatiate authentication handshake */ - ret = ceph_auth_build_hello(monc->auth, - monc->m_auth->front.iov_base, - monc->m_auth->front_max); - __send_prepared_auth_request(monc, ret); - } else { - dout("open_session mon%d already open\n", monc->cur_mon); - } - return 0; -} - -static bool __sub_expired(struct ceph_mon_client *monc) -{ - return time_after_eq(jiffies, monc->sub_renew_after); -} - -/* - * Reschedule delayed work timer. - */ -static void __schedule_delayed(struct ceph_mon_client *monc) -{ - unsigned delay; - - if (monc->cur_mon < 0 || __sub_expired(monc)) - delay = 10 * HZ; - else - delay = 20 * HZ; - dout("__schedule_delayed after %u\n", delay); - schedule_delayed_work(&monc->delayed_work, delay); -} - -/* - * Send subscribe request for mdsmap and/or osdmap. - */ -static void __send_subscribe(struct ceph_mon_client *monc) -{ - dout("__send_subscribe sub_sent=%u exp=%u want_osd=%d\n", - (unsigned)monc->sub_sent, __sub_expired(monc), - monc->want_next_osdmap); - if ((__sub_expired(monc) && !monc->sub_sent) || - monc->want_next_osdmap == 1) { - struct ceph_msg *msg = monc->m_subscribe; - struct ceph_mon_subscribe_item *i; - void *p, *end; - - p = msg->front.iov_base; - end = p + msg->front_max; - - dout("__send_subscribe to 'mdsmap' %u+\n", - (unsigned)monc->have_mdsmap); - if (monc->want_next_osdmap) { - dout("__send_subscribe to 'osdmap' %u\n", - (unsigned)monc->have_osdmap); - ceph_encode_32(&p, 3); - ceph_encode_string(&p, end, "osdmap", 6); - i = p; - i->have = cpu_to_le64(monc->have_osdmap); - i->onetime = 1; - p += sizeof(*i); - monc->want_next_osdmap = 2; /* requested */ - } else { - ceph_encode_32(&p, 2); - } - ceph_encode_string(&p, end, "mdsmap", 6); - i = p; - i->have = cpu_to_le64(monc->have_mdsmap); - i->onetime = 0; - p += sizeof(*i); - ceph_encode_string(&p, end, "monmap", 6); - i = p; - i->have = 0; - i->onetime = 0; - p += sizeof(*i); - - msg->front.iov_len = p - msg->front.iov_base; - msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); - ceph_con_revoke(monc->con, msg); - ceph_con_send(monc->con, ceph_msg_get(msg)); - - monc->sub_sent = jiffies | 1; /* never 0 */ - } -} - -static void handle_subscribe_ack(struct ceph_mon_client *monc, - struct ceph_msg *msg) -{ - unsigned seconds; - struct ceph_mon_subscribe_ack *h = msg->front.iov_base; - - if (msg->front.iov_len < sizeof(*h)) - goto bad; - seconds = le32_to_cpu(h->duration); - - mutex_lock(&monc->mutex); - if (monc->hunting) { - pr_info("mon%d %s session established\n", - monc->cur_mon, pr_addr(&monc->con->peer_addr.in_addr)); - monc->hunting = false; - } - dout("handle_subscribe_ack after %d seconds\n", seconds); - monc->sub_renew_after = monc->sub_sent + (seconds >> 1)*HZ - 1; - monc->sub_sent = 0; - mutex_unlock(&monc->mutex); - return; -bad: - pr_err("got corrupt subscribe-ack msg\n"); - ceph_msg_dump(msg); -} - -/* - * Keep track of which maps we have - */ -int ceph_monc_got_mdsmap(struct ceph_mon_client *monc, u32 got) -{ - mutex_lock(&monc->mutex); - monc->have_mdsmap = got; - mutex_unlock(&monc->mutex); - return 0; -} - -int ceph_monc_got_osdmap(struct ceph_mon_client *monc, u32 got) -{ - mutex_lock(&monc->mutex); - monc->have_osdmap = got; - monc->want_next_osdmap = 0; - mutex_unlock(&monc->mutex); - return 0; -} - -/* - * Register interest in the next osdmap - */ -void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc) -{ - dout("request_next_osdmap have %u\n", monc->have_osdmap); - mutex_lock(&monc->mutex); - if (!monc->want_next_osdmap) - monc->want_next_osdmap = 1; - if (monc->want_next_osdmap < 2) - __send_subscribe(monc); - mutex_unlock(&monc->mutex); -} - -/* - * - */ -int ceph_monc_open_session(struct ceph_mon_client *monc) -{ - if (!monc->con) { - monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); - if (!monc->con) - return -ENOMEM; - ceph_con_init(monc->client->msgr, monc->con); - monc->con->private = monc; - monc->con->ops = &mon_con_ops; - } - - mutex_lock(&monc->mutex); - __open_session(monc); - __schedule_delayed(monc); - mutex_unlock(&monc->mutex); - return 0; -} - -/* - * The monitor responds with mount ack indicate mount success. The - * included client ticket allows the client to talk to MDSs and OSDs. - */ -static void ceph_monc_handle_map(struct ceph_mon_client *monc, - struct ceph_msg *msg) -{ - struct ceph_client *client = monc->client; - struct ceph_monmap *monmap = NULL, *old = monc->monmap; - void *p, *end; - - mutex_lock(&monc->mutex); - - dout("handle_monmap\n"); - p = msg->front.iov_base; - end = p + msg->front.iov_len; - - monmap = ceph_monmap_decode(p, end); - if (IS_ERR(monmap)) { - pr_err("problem decoding monmap, %d\n", - (int)PTR_ERR(monmap)); - goto out; - } - - if (ceph_check_fsid(monc->client, &monmap->fsid) < 0) { - kfree(monmap); - goto out; - } - - client->monc.monmap = monmap; - kfree(old); - -out: - mutex_unlock(&monc->mutex); - wake_up_all(&client->auth_wq); -} - -/* - * generic requests (e.g., statfs, poolop) - */ -static struct ceph_mon_generic_request *__lookup_generic_req( - struct ceph_mon_client *monc, u64 tid) -{ - struct ceph_mon_generic_request *req; - struct rb_node *n = monc->generic_request_tree.rb_node; - - while (n) { - req = rb_entry(n, struct ceph_mon_generic_request, node); - if (tid < req->tid) - n = n->rb_left; - else if (tid > req->tid) - n = n->rb_right; - else - return req; - } - return NULL; -} - -static void __insert_generic_request(struct ceph_mon_client *monc, - struct ceph_mon_generic_request *new) -{ - struct rb_node **p = &monc->generic_request_tree.rb_node; - struct rb_node *parent = NULL; - struct ceph_mon_generic_request *req = NULL; - - while (*p) { - parent = *p; - req = rb_entry(parent, struct ceph_mon_generic_request, node); - if (new->tid < req->tid) - p = &(*p)->rb_left; - else if (new->tid > req->tid) - p = &(*p)->rb_right; - else - BUG(); - } - - rb_link_node(&new->node, parent, p); - rb_insert_color(&new->node, &monc->generic_request_tree); -} - -static void release_generic_request(struct kref *kref) -{ - struct ceph_mon_generic_request *req = - container_of(kref, struct ceph_mon_generic_request, kref); - - if (req->reply) - ceph_msg_put(req->reply); - if (req->request) - ceph_msg_put(req->request); - - kfree(req); -} - -static void put_generic_request(struct ceph_mon_generic_request *req) -{ - kref_put(&req->kref, release_generic_request); -} - -static void get_generic_request(struct ceph_mon_generic_request *req) -{ - kref_get(&req->kref); -} - -static struct ceph_msg *get_generic_reply(struct ceph_connection *con, - struct ceph_msg_header *hdr, - int *skip) -{ - struct ceph_mon_client *monc = con->private; - struct ceph_mon_generic_request *req; - u64 tid = le64_to_cpu(hdr->tid); - struct ceph_msg *m; - - mutex_lock(&monc->mutex); - req = __lookup_generic_req(monc, tid); - if (!req) { - dout("get_generic_reply %lld dne\n", tid); - *skip = 1; - m = NULL; - } else { - dout("get_generic_reply %lld got %p\n", tid, req->reply); - m = ceph_msg_get(req->reply); - /* - * we don't need to track the connection reading into - * this reply because we only have one open connection - * at a time, ever. - */ - } - mutex_unlock(&monc->mutex); - return m; -} - -static int do_generic_request(struct ceph_mon_client *monc, - struct ceph_mon_generic_request *req) -{ - int err; - - /* register request */ - mutex_lock(&monc->mutex); - req->tid = ++monc->last_tid; - req->request->hdr.tid = cpu_to_le64(req->tid); - __insert_generic_request(monc, req); - monc->num_generic_requests++; - ceph_con_send(monc->con, ceph_msg_get(req->request)); - mutex_unlock(&monc->mutex); - - err = wait_for_completion_interruptible(&req->completion); - - mutex_lock(&monc->mutex); - rb_erase(&req->node, &monc->generic_request_tree); - monc->num_generic_requests--; - mutex_unlock(&monc->mutex); - - if (!err) - err = req->result; - return err; -} - -/* - * statfs - */ -static void handle_statfs_reply(struct ceph_mon_client *monc, - struct ceph_msg *msg) -{ - struct ceph_mon_generic_request *req; - struct ceph_mon_statfs_reply *reply = msg->front.iov_base; - u64 tid = le64_to_cpu(msg->hdr.tid); - - if (msg->front.iov_len != sizeof(*reply)) - goto bad; - dout("handle_statfs_reply %p tid %llu\n", msg, tid); - - mutex_lock(&monc->mutex); - req = __lookup_generic_req(monc, tid); - if (req) { - *(struct ceph_statfs *)req->buf = reply->st; - req->result = 0; - get_generic_request(req); - } - mutex_unlock(&monc->mutex); - if (req) { - complete_all(&req->completion); - put_generic_request(req); - } - return; - -bad: - pr_err("corrupt generic reply, tid %llu\n", tid); - ceph_msg_dump(msg); -} - -/* - * Do a synchronous statfs(). - */ -int ceph_monc_do_statfs(struct ceph_mon_client *monc, struct ceph_statfs *buf) -{ - struct ceph_mon_generic_request *req; - struct ceph_mon_statfs *h; - int err; - - req = kzalloc(sizeof(*req), GFP_NOFS); - if (!req) - return -ENOMEM; - - kref_init(&req->kref); - req->buf = buf; - req->buf_len = sizeof(*buf); - init_completion(&req->completion); - - err = -ENOMEM; - req->request = ceph_msg_new(CEPH_MSG_STATFS, sizeof(*h), GFP_NOFS); - if (!req->request) - goto out; - req->reply = ceph_msg_new(CEPH_MSG_STATFS_REPLY, 1024, GFP_NOFS); - if (!req->reply) - goto out; - - /* fill out request */ - h = req->request->front.iov_base; - h->monhdr.have_version = 0; - h->monhdr.session_mon = cpu_to_le16(-1); - h->monhdr.session_mon_tid = 0; - h->fsid = monc->monmap->fsid; - - err = do_generic_request(monc, req); - -out: - kref_put(&req->kref, release_generic_request); - return err; -} - -/* - * pool ops - */ -static int get_poolop_reply_buf(const char *src, size_t src_len, - char *dst, size_t dst_len) -{ - u32 buf_len; - - if (src_len != sizeof(u32) + dst_len) - return -EINVAL; - - buf_len = le32_to_cpu(*(u32 *)src); - if (buf_len != dst_len) - return -EINVAL; - - memcpy(dst, src + sizeof(u32), dst_len); - return 0; -} - -static void handle_poolop_reply(struct ceph_mon_client *monc, - struct ceph_msg *msg) -{ - struct ceph_mon_generic_request *req; - struct ceph_mon_poolop_reply *reply = msg->front.iov_base; - u64 tid = le64_to_cpu(msg->hdr.tid); - - if (msg->front.iov_len < sizeof(*reply)) - goto bad; - dout("handle_poolop_reply %p tid %llu\n", msg, tid); - - mutex_lock(&monc->mutex); - req = __lookup_generic_req(monc, tid); - if (req) { - if (req->buf_len && - get_poolop_reply_buf(msg->front.iov_base + sizeof(*reply), - msg->front.iov_len - sizeof(*reply), - req->buf, req->buf_len) < 0) { - mutex_unlock(&monc->mutex); - goto bad; - } - req->result = le32_to_cpu(reply->reply_code); - get_generic_request(req); - } - mutex_unlock(&monc->mutex); - if (req) { - complete(&req->completion); - put_generic_request(req); - } - return; - -bad: - pr_err("corrupt generic reply, tid %llu\n", tid); - ceph_msg_dump(msg); -} - -/* - * Do a synchronous pool op. - */ -int ceph_monc_do_poolop(struct ceph_mon_client *monc, u32 op, - u32 pool, u64 snapid, - char *buf, int len) -{ - struct ceph_mon_generic_request *req; - struct ceph_mon_poolop *h; - int err; - - req = kzalloc(sizeof(*req), GFP_NOFS); - if (!req) - return -ENOMEM; - - kref_init(&req->kref); - req->buf = buf; - req->buf_len = len; - init_completion(&req->completion); - - err = -ENOMEM; - req->request = ceph_msg_new(CEPH_MSG_POOLOP, sizeof(*h), GFP_NOFS); - if (!req->request) - goto out; - req->reply = ceph_msg_new(CEPH_MSG_POOLOP_REPLY, 1024, GFP_NOFS); - if (!req->reply) - goto out; - - /* fill out request */ - req->request->hdr.version = cpu_to_le16(2); - h = req->request->front.iov_base; - h->monhdr.have_version = 0; - h->monhdr.session_mon = cpu_to_le16(-1); - h->monhdr.session_mon_tid = 0; - h->fsid = monc->monmap->fsid; - h->pool = cpu_to_le32(pool); - h->op = cpu_to_le32(op); - h->auid = 0; - h->snapid = cpu_to_le64(snapid); - h->name_len = 0; - - err = do_generic_request(monc, req); - -out: - kref_put(&req->kref, release_generic_request); - return err; -} - -int ceph_monc_create_snapid(struct ceph_mon_client *monc, - u32 pool, u64 *snapid) -{ - return ceph_monc_do_poolop(monc, POOL_OP_CREATE_UNMANAGED_SNAP, - pool, 0, (char *)snapid, sizeof(*snapid)); - -} - -int ceph_monc_delete_snapid(struct ceph_mon_client *monc, - u32 pool, u64 snapid) -{ - return ceph_monc_do_poolop(monc, POOL_OP_CREATE_UNMANAGED_SNAP, - pool, snapid, 0, 0); - -} - -/* - * Resend pending generic requests. - */ -static void __resend_generic_request(struct ceph_mon_client *monc) -{ - struct ceph_mon_generic_request *req; - struct rb_node *p; - - for (p = rb_first(&monc->generic_request_tree); p; p = rb_next(p)) { - req = rb_entry(p, struct ceph_mon_generic_request, node); - ceph_con_revoke(monc->con, req->request); - ceph_con_send(monc->con, ceph_msg_get(req->request)); - } -} - -/* - * Delayed work. If we haven't mounted yet, retry. Otherwise, - * renew/retry subscription as needed (in case it is timing out, or we - * got an ENOMEM). And keep the monitor connection alive. - */ -static void delayed_work(struct work_struct *work) -{ - struct ceph_mon_client *monc = - container_of(work, struct ceph_mon_client, delayed_work.work); - - dout("monc delayed_work\n"); - mutex_lock(&monc->mutex); - if (monc->hunting) { - __close_session(monc); - __open_session(monc); /* continue hunting */ - } else { - ceph_con_keepalive(monc->con); - - __validate_auth(monc); - - if (monc->auth->ops->is_authenticated(monc->auth)) - __send_subscribe(monc); - } - __schedule_delayed(monc); - mutex_unlock(&monc->mutex); -} - -/* - * On startup, we build a temporary monmap populated with the IPs - * provided by mount(2). - */ -static int build_initial_monmap(struct ceph_mon_client *monc) -{ - struct ceph_mount_args *args = monc->client->mount_args; - struct ceph_entity_addr *mon_addr = args->mon_addr; - int num_mon = args->num_mon; - int i; - - /* build initial monmap */ - monc->monmap = kzalloc(sizeof(*monc->monmap) + - num_mon*sizeof(monc->monmap->mon_inst[0]), - GFP_KERNEL); - if (!monc->monmap) - return -ENOMEM; - for (i = 0; i < num_mon; i++) { - monc->monmap->mon_inst[i].addr = mon_addr[i]; - monc->monmap->mon_inst[i].addr.nonce = 0; - monc->monmap->mon_inst[i].name.type = - CEPH_ENTITY_TYPE_MON; - monc->monmap->mon_inst[i].name.num = cpu_to_le64(i); - } - monc->monmap->num_mon = num_mon; - monc->have_fsid = false; - - /* release addr memory */ - kfree(args->mon_addr); - args->mon_addr = NULL; - args->num_mon = 0; - return 0; -} - -int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) -{ - int err = 0; - - dout("init\n"); - memset(monc, 0, sizeof(*monc)); - monc->client = cl; - monc->monmap = NULL; - mutex_init(&monc->mutex); - - err = build_initial_monmap(monc); - if (err) - goto out; - - monc->con = NULL; - - /* authentication */ - monc->auth = ceph_auth_init(cl->mount_args->name, - cl->mount_args->secret); - if (IS_ERR(monc->auth)) - return PTR_ERR(monc->auth); - monc->auth->want_keys = - CEPH_ENTITY_TYPE_AUTH | CEPH_ENTITY_TYPE_MON | - CEPH_ENTITY_TYPE_OSD | CEPH_ENTITY_TYPE_MDS; - - /* msgs */ - err = -ENOMEM; - monc->m_subscribe_ack = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE_ACK, - sizeof(struct ceph_mon_subscribe_ack), - GFP_NOFS); - if (!monc->m_subscribe_ack) - goto out_monmap; - - monc->m_subscribe = ceph_msg_new(CEPH_MSG_MON_SUBSCRIBE, 96, GFP_NOFS); - if (!monc->m_subscribe) - goto out_subscribe_ack; - - monc->m_auth_reply = ceph_msg_new(CEPH_MSG_AUTH_REPLY, 4096, GFP_NOFS); - if (!monc->m_auth_reply) - goto out_subscribe; - - monc->m_auth = ceph_msg_new(CEPH_MSG_AUTH, 4096, GFP_NOFS); - monc->pending_auth = 0; - if (!monc->m_auth) - goto out_auth_reply; - - monc->cur_mon = -1; - monc->hunting = true; - monc->sub_renew_after = jiffies; - monc->sub_sent = 0; - - INIT_DELAYED_WORK(&monc->delayed_work, delayed_work); - monc->generic_request_tree = RB_ROOT; - monc->num_generic_requests = 0; - monc->last_tid = 0; - - monc->have_mdsmap = 0; - monc->have_osdmap = 0; - monc->want_next_osdmap = 1; - return 0; - -out_auth_reply: - ceph_msg_put(monc->m_auth_reply); -out_subscribe: - ceph_msg_put(monc->m_subscribe); -out_subscribe_ack: - ceph_msg_put(monc->m_subscribe_ack); -out_monmap: - kfree(monc->monmap); -out: - return err; -} - -void ceph_monc_stop(struct ceph_mon_client *monc) -{ - dout("stop\n"); - cancel_delayed_work_sync(&monc->delayed_work); - - mutex_lock(&monc->mutex); - __close_session(monc); - if (monc->con) { - monc->con->private = NULL; - monc->con->ops->put(monc->con); - monc->con = NULL; - } - mutex_unlock(&monc->mutex); - - ceph_auth_destroy(monc->auth); - - ceph_msg_put(monc->m_auth); - ceph_msg_put(monc->m_auth_reply); - ceph_msg_put(monc->m_subscribe); - ceph_msg_put(monc->m_subscribe_ack); - - kfree(monc->monmap); -} - -static void handle_auth_reply(struct ceph_mon_client *monc, - struct ceph_msg *msg) -{ - int ret; - int was_auth = 0; - - mutex_lock(&monc->mutex); - if (monc->auth->ops) - was_auth = monc->auth->ops->is_authenticated(monc->auth); - monc->pending_auth = 0; - ret = ceph_handle_auth_reply(monc->auth, msg->front.iov_base, - msg->front.iov_len, - monc->m_auth->front.iov_base, - monc->m_auth->front_max); - if (ret < 0) { - monc->client->auth_err = ret; - wake_up_all(&monc->client->auth_wq); - } else if (ret > 0) { - __send_prepared_auth_request(monc, ret); - } else if (!was_auth && monc->auth->ops->is_authenticated(monc->auth)) { - dout("authenticated, starting session\n"); - - monc->client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT; - monc->client->msgr->inst.name.num = - cpu_to_le64(monc->auth->global_id); - - __send_subscribe(monc); - __resend_generic_request(monc); - } - mutex_unlock(&monc->mutex); -} - -static int __validate_auth(struct ceph_mon_client *monc) -{ - int ret; - - if (monc->pending_auth) - return 0; - - ret = ceph_build_auth(monc->auth, monc->m_auth->front.iov_base, - monc->m_auth->front_max); - if (ret <= 0) - return ret; /* either an error, or no need to authenticate */ - __send_prepared_auth_request(monc, ret); - return 0; -} - -int ceph_monc_validate_auth(struct ceph_mon_client *monc) -{ - int ret; - - mutex_lock(&monc->mutex); - ret = __validate_auth(monc); - mutex_unlock(&monc->mutex); - return ret; -} - -/* - * handle incoming message - */ -static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) -{ - struct ceph_mon_client *monc = con->private; - int type = le16_to_cpu(msg->hdr.type); - - if (!monc) - return; - - switch (type) { - case CEPH_MSG_AUTH_REPLY: - handle_auth_reply(monc, msg); - break; - - case CEPH_MSG_MON_SUBSCRIBE_ACK: - handle_subscribe_ack(monc, msg); - break; - - case CEPH_MSG_STATFS_REPLY: - handle_statfs_reply(monc, msg); - break; - - case CEPH_MSG_POOLOP_REPLY: - handle_poolop_reply(monc, msg); - break; - - case CEPH_MSG_MON_MAP: - ceph_monc_handle_map(monc, msg); - break; - - case CEPH_MSG_MDS_MAP: - ceph_mdsc_handle_map(&monc->client->mdsc, msg); - break; - - case CEPH_MSG_OSD_MAP: - ceph_osdc_handle_map(&monc->client->osdc, msg); - break; - - default: - pr_err("received unknown message type %d %s\n", type, - ceph_msg_type_name(type)); - } - ceph_msg_put(msg); -} - -/* - * Allocate memory for incoming message - */ -static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, - struct ceph_msg_header *hdr, - int *skip) -{ - struct ceph_mon_client *monc = con->private; - int type = le16_to_cpu(hdr->type); - int front_len = le32_to_cpu(hdr->front_len); - struct ceph_msg *m = NULL; - - *skip = 0; - - switch (type) { - case CEPH_MSG_MON_SUBSCRIBE_ACK: - m = ceph_msg_get(monc->m_subscribe_ack); - break; - case CEPH_MSG_POOLOP_REPLY: - case CEPH_MSG_STATFS_REPLY: - return get_generic_reply(con, hdr, skip); - case CEPH_MSG_AUTH_REPLY: - m = ceph_msg_get(monc->m_auth_reply); - break; - case CEPH_MSG_MON_MAP: - case CEPH_MSG_MDS_MAP: - case CEPH_MSG_OSD_MAP: - m = ceph_msg_new(type, front_len, GFP_NOFS); - break; - } - - if (!m) { - pr_info("alloc_msg unknown type %d\n", type); - *skip = 1; - } - return m; -} - -/* - * If the monitor connection resets, pick a new monitor and resubmit - * any pending requests. - */ -static void mon_fault(struct ceph_connection *con) -{ - struct ceph_mon_client *monc = con->private; - - if (!monc) - return; - - dout("mon_fault\n"); - mutex_lock(&monc->mutex); - if (!con->private) - goto out; - - if (monc->con && !monc->hunting) - pr_info("mon%d %s session lost, " - "hunting for new mon\n", monc->cur_mon, - pr_addr(&monc->con->peer_addr.in_addr)); - - __close_session(monc); - if (!monc->hunting) { - /* start hunting */ - monc->hunting = true; - __open_session(monc); - } else { - /* already hunting, let's wait a bit */ - __schedule_delayed(monc); - } -out: - mutex_unlock(&monc->mutex); -} - -static const struct ceph_connection_operations mon_con_ops = { - .get = ceph_con_get, - .put = ceph_con_put, - .dispatch = dispatch, - .fault = mon_fault, - .alloc_msg = mon_alloc_msg, -}; diff --git a/fs/ceph/mon_client.h b/fs/ceph/mon_client.h deleted file mode 100644 index 8e396f2c096..00000000000 --- a/fs/ceph/mon_client.h +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef _FS_CEPH_MON_CLIENT_H -#define _FS_CEPH_MON_CLIENT_H - -#include -#include -#include - -#include "messenger.h" - -struct ceph_client; -struct ceph_mount_args; -struct ceph_auth_client; - -/* - * The monitor map enumerates the set of all monitors. - */ -struct ceph_monmap { - struct ceph_fsid fsid; - u32 epoch; - u32 num_mon; - struct ceph_entity_inst mon_inst[0]; -}; - -struct ceph_mon_client; -struct ceph_mon_generic_request; - - -/* - * Generic mechanism for resending monitor requests. - */ -typedef void (*ceph_monc_request_func_t)(struct ceph_mon_client *monc, - int newmon); - -/* a pending monitor request */ -struct ceph_mon_request { - struct ceph_mon_client *monc; - struct delayed_work delayed_work; - unsigned long delay; - ceph_monc_request_func_t do_request; -}; - -/* - * ceph_mon_generic_request is being used for the statfs and poolop requests - * which are bening done a bit differently because we need to get data back - * to the caller - */ -struct ceph_mon_generic_request { - struct kref kref; - u64 tid; - struct rb_node node; - int result; - void *buf; - int buf_len; - struct completion completion; - struct ceph_msg *request; /* original request */ - struct ceph_msg *reply; /* and reply */ -}; - -struct ceph_mon_client { - struct ceph_client *client; - struct ceph_monmap *monmap; - - struct mutex mutex; - struct delayed_work delayed_work; - - struct ceph_auth_client *auth; - struct ceph_msg *m_auth, *m_auth_reply, *m_subscribe, *m_subscribe_ack; - int pending_auth; - - bool hunting; - int cur_mon; /* last monitor i contacted */ - unsigned long sub_sent, sub_renew_after; - struct ceph_connection *con; - bool have_fsid; - - /* pending generic requests */ - struct rb_root generic_request_tree; - int num_generic_requests; - u64 last_tid; - - /* mds/osd map */ - int want_next_osdmap; /* 1 = want, 2 = want+asked */ - u32 have_osdmap, have_mdsmap; - -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_file; -#endif -}; - -extern struct ceph_monmap *ceph_monmap_decode(void *p, void *end); -extern int ceph_monmap_contains(struct ceph_monmap *m, - struct ceph_entity_addr *addr); - -extern int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl); -extern void ceph_monc_stop(struct ceph_mon_client *monc); - -/* - * The model here is to indicate that we need a new map of at least - * epoch @want, and also call in when we receive a map. We will - * periodically rerequest the map from the monitor cluster until we - * get what we want. - */ -extern int ceph_monc_got_mdsmap(struct ceph_mon_client *monc, u32 have); -extern int ceph_monc_got_osdmap(struct ceph_mon_client *monc, u32 have); - -extern void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc); - -extern int ceph_monc_do_statfs(struct ceph_mon_client *monc, - struct ceph_statfs *buf); - -extern int ceph_monc_open_session(struct ceph_mon_client *monc); - -extern int ceph_monc_validate_auth(struct ceph_mon_client *monc); - -extern int ceph_monc_create_snapid(struct ceph_mon_client *monc, - u32 pool, u64 *snapid); - -extern int ceph_monc_delete_snapid(struct ceph_mon_client *monc, - u32 pool, u64 snapid); - -#endif diff --git a/fs/ceph/msgpool.c b/fs/ceph/msgpool.c deleted file mode 100644 index dd65a643813..00000000000 --- a/fs/ceph/msgpool.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "ceph_debug.h" - -#include -#include -#include -#include - -#include "msgpool.h" - -static void *alloc_fn(gfp_t gfp_mask, void *arg) -{ - struct ceph_msgpool *pool = arg; - void *p; - - p = ceph_msg_new(0, pool->front_len, gfp_mask); - if (!p) - pr_err("msgpool %s alloc failed\n", pool->name); - return p; -} - -static void free_fn(void *element, void *arg) -{ - ceph_msg_put(element); -} - -int ceph_msgpool_init(struct ceph_msgpool *pool, - int front_len, int size, bool blocking, const char *name) -{ - pool->front_len = front_len; - pool->pool = mempool_create(size, alloc_fn, free_fn, pool); - if (!pool->pool) - return -ENOMEM; - pool->name = name; - return 0; -} - -void ceph_msgpool_destroy(struct ceph_msgpool *pool) -{ - mempool_destroy(pool->pool); -} - -struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *pool, - int front_len) -{ - if (front_len > pool->front_len) { - pr_err("msgpool_get pool %s need front %d, pool size is %d\n", - pool->name, front_len, pool->front_len); - WARN_ON(1); - - /* try to alloc a fresh message */ - return ceph_msg_new(0, front_len, GFP_NOFS); - } - - return mempool_alloc(pool->pool, GFP_NOFS); -} - -void ceph_msgpool_put(struct ceph_msgpool *pool, struct ceph_msg *msg) -{ - /* reset msg front_len; user may have changed it */ - msg->front.iov_len = pool->front_len; - msg->hdr.front_len = cpu_to_le32(pool->front_len); - - kref_init(&msg->kref); /* retake single ref */ -} diff --git a/fs/ceph/msgpool.h b/fs/ceph/msgpool.h deleted file mode 100644 index a362605f936..00000000000 --- a/fs/ceph/msgpool.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _FS_CEPH_MSGPOOL -#define _FS_CEPH_MSGPOOL - -#include -#include "messenger.h" - -/* - * we use memory pools for preallocating messages we may receive, to - * avoid unexpected OOM conditions. - */ -struct ceph_msgpool { - const char *name; - mempool_t *pool; - int front_len; /* preallocated payload size */ -}; - -extern int ceph_msgpool_init(struct ceph_msgpool *pool, - int front_len, int size, bool blocking, - const char *name); -extern void ceph_msgpool_destroy(struct ceph_msgpool *pool); -extern struct ceph_msg *ceph_msgpool_get(struct ceph_msgpool *, - int front_len); -extern void ceph_msgpool_put(struct ceph_msgpool *, struct ceph_msg *); - -#endif diff --git a/fs/ceph/msgr.h b/fs/ceph/msgr.h deleted file mode 100644 index 680d3d648ca..00000000000 --- a/fs/ceph/msgr.h +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef CEPH_MSGR_H -#define CEPH_MSGR_H - -/* - * Data types for message passing layer used by Ceph. - */ - -#define CEPH_MON_PORT 6789 /* default monitor port */ - -/* - * client-side processes will try to bind to ports in this - * range, simply for the benefit of tools like nmap or wireshark - * that would like to identify the protocol. - */ -#define CEPH_PORT_FIRST 6789 -#define CEPH_PORT_START 6800 /* non-monitors start here */ -#define CEPH_PORT_LAST 6900 - -/* - * tcp connection banner. include a protocol version. and adjust - * whenever the wire protocol changes. try to keep this string length - * constant. - */ -#define CEPH_BANNER "ceph v027" -#define CEPH_BANNER_MAX_LEN 30 - - -/* - * Rollover-safe type and comparator for 32-bit sequence numbers. - * Comparator returns -1, 0, or 1. - */ -typedef __u32 ceph_seq_t; - -static inline __s32 ceph_seq_cmp(__u32 a, __u32 b) -{ - return (__s32)a - (__s32)b; -} - - -/* - * entity_name -- logical name for a process participating in the - * network, e.g. 'mds0' or 'osd3'. - */ -struct ceph_entity_name { - __u8 type; /* CEPH_ENTITY_TYPE_* */ - __le64 num; -} __attribute__ ((packed)); - -#define CEPH_ENTITY_TYPE_MON 0x01 -#define CEPH_ENTITY_TYPE_MDS 0x02 -#define CEPH_ENTITY_TYPE_OSD 0x04 -#define CEPH_ENTITY_TYPE_CLIENT 0x08 -#define CEPH_ENTITY_TYPE_AUTH 0x20 - -#define CEPH_ENTITY_TYPE_ANY 0xFF - -extern const char *ceph_entity_type_name(int type); - -/* - * entity_addr -- network address - */ -struct ceph_entity_addr { - __le32 type; - __le32 nonce; /* unique id for process (e.g. pid) */ - struct sockaddr_storage in_addr; -} __attribute__ ((packed)); - -struct ceph_entity_inst { - struct ceph_entity_name name; - struct ceph_entity_addr addr; -} __attribute__ ((packed)); - - -/* used by message exchange protocol */ -#define CEPH_MSGR_TAG_READY 1 /* server->client: ready for messages */ -#define CEPH_MSGR_TAG_RESETSESSION 2 /* server->client: reset, try again */ -#define CEPH_MSGR_TAG_WAIT 3 /* server->client: wait for racing - incoming connection */ -#define CEPH_MSGR_TAG_RETRY_SESSION 4 /* server->client + cseq: try again - with higher cseq */ -#define CEPH_MSGR_TAG_RETRY_GLOBAL 5 /* server->client + gseq: try again - with higher gseq */ -#define CEPH_MSGR_TAG_CLOSE 6 /* closing pipe */ -#define CEPH_MSGR_TAG_MSG 7 /* message */ -#define CEPH_MSGR_TAG_ACK 8 /* message ack */ -#define CEPH_MSGR_TAG_KEEPALIVE 9 /* just a keepalive byte! */ -#define CEPH_MSGR_TAG_BADPROTOVER 10 /* bad protocol version */ -#define CEPH_MSGR_TAG_BADAUTHORIZER 11 /* bad authorizer */ -#define CEPH_MSGR_TAG_FEATURES 12 /* insufficient features */ - - -/* - * connection negotiation - */ -struct ceph_msg_connect { - __le64 features; /* supported feature bits */ - __le32 host_type; /* CEPH_ENTITY_TYPE_* */ - __le32 global_seq; /* count connections initiated by this host */ - __le32 connect_seq; /* count connections initiated in this session */ - __le32 protocol_version; - __le32 authorizer_protocol; - __le32 authorizer_len; - __u8 flags; /* CEPH_MSG_CONNECT_* */ -} __attribute__ ((packed)); - -struct ceph_msg_connect_reply { - __u8 tag; - __le64 features; /* feature bits for this session */ - __le32 global_seq; - __le32 connect_seq; - __le32 protocol_version; - __le32 authorizer_len; - __u8 flags; -} __attribute__ ((packed)); - -#define CEPH_MSG_CONNECT_LOSSY 1 /* messages i send may be safely dropped */ - - -/* - * message header - */ -struct ceph_msg_header_old { - __le64 seq; /* message seq# for this session */ - __le64 tid; /* transaction id */ - __le16 type; /* message type */ - __le16 priority; /* priority. higher value == higher priority */ - __le16 version; /* version of message encoding */ - - __le32 front_len; /* bytes in main payload */ - __le32 middle_len;/* bytes in middle payload */ - __le32 data_len; /* bytes of data payload */ - __le16 data_off; /* sender: include full offset; - receiver: mask against ~PAGE_MASK */ - - struct ceph_entity_inst src, orig_src; - __le32 reserved; - __le32 crc; /* header crc32c */ -} __attribute__ ((packed)); - -struct ceph_msg_header { - __le64 seq; /* message seq# for this session */ - __le64 tid; /* transaction id */ - __le16 type; /* message type */ - __le16 priority; /* priority. higher value == higher priority */ - __le16 version; /* version of message encoding */ - - __le32 front_len; /* bytes in main payload */ - __le32 middle_len;/* bytes in middle payload */ - __le32 data_len; /* bytes of data payload */ - __le16 data_off; /* sender: include full offset; - receiver: mask against ~PAGE_MASK */ - - struct ceph_entity_name src; - __le32 reserved; - __le32 crc; /* header crc32c */ -} __attribute__ ((packed)); - -#define CEPH_MSG_PRIO_LOW 64 -#define CEPH_MSG_PRIO_DEFAULT 127 -#define CEPH_MSG_PRIO_HIGH 196 -#define CEPH_MSG_PRIO_HIGHEST 255 - -/* - * follows data payload - */ -struct ceph_msg_footer { - __le32 front_crc, middle_crc, data_crc; - __u8 flags; -} __attribute__ ((packed)); - -#define CEPH_MSG_FOOTER_COMPLETE (1<<0) /* msg wasn't aborted */ -#define CEPH_MSG_FOOTER_NOCRC (1<<1) /* no data crc */ - - -#endif diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c deleted file mode 100644 index 0edb43f1ef2..00000000000 --- a/fs/ceph/osd_client.c +++ /dev/null @@ -1,1771 +0,0 @@ -#include "ceph_debug.h" - -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_BLOCK -#include -#endif - -#include "super.h" -#include "osd_client.h" -#include "messenger.h" -#include "decode.h" -#include "auth.h" -#include "pagelist.h" - -#define OSD_OP_FRONT_LEN 4096 -#define OSD_OPREPLY_FRONT_LEN 512 - -static const struct ceph_connection_operations osd_con_ops; -static int __kick_requests(struct ceph_osd_client *osdc, - struct ceph_osd *kickosd); - -static void kick_requests(struct ceph_osd_client *osdc, struct ceph_osd *osd); - -static int op_needs_trail(int op) -{ - switch (op) { - case CEPH_OSD_OP_GETXATTR: - case CEPH_OSD_OP_SETXATTR: - case CEPH_OSD_OP_CMPXATTR: - case CEPH_OSD_OP_CALL: - return 1; - default: - return 0; - } -} - -static int op_has_extent(int op) -{ - return (op == CEPH_OSD_OP_READ || - op == CEPH_OSD_OP_WRITE); -} - -void ceph_calc_raw_layout(struct ceph_osd_client *osdc, - struct ceph_file_layout *layout, - u64 snapid, - u64 off, u64 *plen, u64 *bno, - struct ceph_osd_request *req, - struct ceph_osd_req_op *op) -{ - struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; - u64 orig_len = *plen; - u64 objoff, objlen; /* extent in object */ - - reqhead->snapid = cpu_to_le64(snapid); - - /* object extent? */ - ceph_calc_file_object_mapping(layout, off, plen, bno, - &objoff, &objlen); - if (*plen < orig_len) - dout(" skipping last %llu, final file extent %llu~%llu\n", - orig_len - *plen, off, *plen); - - if (op_has_extent(op->op)) { - op->extent.offset = objoff; - op->extent.length = objlen; - } - req->r_num_pages = calc_pages_for(off, *plen); - - dout("calc_layout bno=%llx %llu~%llu (%d pages)\n", - *bno, objoff, objlen, req->r_num_pages); - -} - -/* - * Implement client access to distributed object storage cluster. - * - * All data objects are stored within a cluster/cloud of OSDs, or - * "object storage devices." (Note that Ceph OSDs have _nothing_ to - * do with the T10 OSD extensions to SCSI.) Ceph OSDs are simply - * remote daemons serving up and coordinating consistent and safe - * access to storage. - * - * Cluster membership and the mapping of data objects onto storage devices - * are described by the osd map. - * - * We keep track of pending OSD requests (read, write), resubmit - * requests to different OSDs when the cluster topology/data layout - * change, or retry the affected requests when the communications - * channel with an OSD is reset. - */ - -/* - * calculate the mapping of a file extent onto an object, and fill out the - * request accordingly. shorten extent as necessary if it crosses an - * object boundary. - * - * fill osd op in request message. - */ -static void calc_layout(struct ceph_osd_client *osdc, - struct ceph_vino vino, - struct ceph_file_layout *layout, - u64 off, u64 *plen, - struct ceph_osd_request *req, - struct ceph_osd_req_op *op) -{ - u64 bno; - - ceph_calc_raw_layout(osdc, layout, vino.snap, off, - plen, &bno, req, op); - - sprintf(req->r_oid, "%llx.%08llx", vino.ino, bno); - req->r_oid_len = strlen(req->r_oid); -} - -/* - * requests - */ -void ceph_osdc_release_request(struct kref *kref) -{ - struct ceph_osd_request *req = container_of(kref, - struct ceph_osd_request, - r_kref); - - if (req->r_request) - ceph_msg_put(req->r_request); - if (req->r_reply) - ceph_msg_put(req->r_reply); - if (req->r_con_filling_msg) { - dout("release_request revoking pages %p from con %p\n", - req->r_pages, req->r_con_filling_msg); - ceph_con_revoke_message(req->r_con_filling_msg, - req->r_reply); - ceph_con_put(req->r_con_filling_msg); - } - if (req->r_own_pages) - ceph_release_page_vector(req->r_pages, - req->r_num_pages); -#ifdef CONFIG_BLOCK - if (req->r_bio) - bio_put(req->r_bio); -#endif - ceph_put_snap_context(req->r_snapc); - if (req->r_trail) { - ceph_pagelist_release(req->r_trail); - kfree(req->r_trail); - } - if (req->r_mempool) - mempool_free(req, req->r_osdc->req_mempool); - else - kfree(req); -} - -static int op_needs_trail(int op) -{ - switch (op) { - case CEPH_OSD_OP_GETXATTR: - case CEPH_OSD_OP_SETXATTR: - case CEPH_OSD_OP_CMPXATTR: - case CEPH_OSD_OP_CALL: - return 1; - default: - return 0; - } -} - -static int get_num_ops(struct ceph_osd_req_op *ops, int *needs_trail) -{ - int i = 0; - - if (needs_trail) - *needs_trail = 0; - while (ops[i].op) { - if (needs_trail && op_needs_trail(ops[i].op)) - *needs_trail = 1; - i++; - } - - return i; -} - -struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, - int flags, - struct ceph_snap_context *snapc, - struct ceph_osd_req_op *ops, - bool use_mempool, - gfp_t gfp_flags, - struct page **pages, - struct bio *bio) -{ - struct ceph_osd_request *req; - struct ceph_msg *msg; - int needs_trail; - int num_op = get_num_ops(ops, &needs_trail); - size_t msg_size = sizeof(struct ceph_osd_request_head); - - msg_size += num_op*sizeof(struct ceph_osd_op); - - if (use_mempool) { - req = mempool_alloc(osdc->req_mempool, gfp_flags); - memset(req, 0, sizeof(*req)); - } else { - req = kzalloc(sizeof(*req), gfp_flags); - } - if (req == NULL) - return NULL; - - req->r_osdc = osdc; - req->r_mempool = use_mempool; - - kref_init(&req->r_kref); - init_completion(&req->r_completion); - init_completion(&req->r_safe_completion); - INIT_LIST_HEAD(&req->r_unsafe_item); - req->r_flags = flags; - - WARN_ON((flags & (CEPH_OSD_FLAG_READ|CEPH_OSD_FLAG_WRITE)) == 0); - - /* create reply message */ - if (use_mempool) - msg = ceph_msgpool_get(&osdc->msgpool_op_reply, 0); - else - msg = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, - OSD_OPREPLY_FRONT_LEN, gfp_flags); - if (!msg) { - ceph_osdc_put_request(req); - return NULL; - } - req->r_reply = msg; - - /* allocate space for the trailing data */ - if (needs_trail) { - req->r_trail = kmalloc(sizeof(struct ceph_pagelist), gfp_flags); - if (!req->r_trail) { - ceph_osdc_put_request(req); - return NULL; - } - ceph_pagelist_init(req->r_trail); - } - /* create request message; allow space for oid */ - msg_size += 40; - if (snapc) - msg_size += sizeof(u64) * snapc->num_snaps; - if (use_mempool) - msg = ceph_msgpool_get(&osdc->msgpool_op, 0); - else - msg = ceph_msg_new(CEPH_MSG_OSD_OP, msg_size, gfp_flags); - if (!msg) { - ceph_osdc_put_request(req); - return NULL; - } - - msg->hdr.type = cpu_to_le16(CEPH_MSG_OSD_OP); - memset(msg->front.iov_base, 0, msg->front.iov_len); - - req->r_request = msg; - req->r_pages = pages; -#ifdef CONFIG_BLOCK - if (bio) { - req->r_bio = bio; - bio_get(req->r_bio); - } -#endif - - return req; -} - -static void osd_req_encode_op(struct ceph_osd_request *req, - struct ceph_osd_op *dst, - struct ceph_osd_req_op *src) -{ - dst->op = cpu_to_le16(src->op); - - switch (dst->op) { - case CEPH_OSD_OP_READ: - case CEPH_OSD_OP_WRITE: - dst->extent.offset = - cpu_to_le64(src->extent.offset); - dst->extent.length = - cpu_to_le64(src->extent.length); - dst->extent.truncate_size = - cpu_to_le64(src->extent.truncate_size); - dst->extent.truncate_seq = - cpu_to_le32(src->extent.truncate_seq); - break; - - case CEPH_OSD_OP_GETXATTR: - case CEPH_OSD_OP_SETXATTR: - case CEPH_OSD_OP_CMPXATTR: - BUG_ON(!req->r_trail); - - dst->xattr.name_len = cpu_to_le32(src->xattr.name_len); - dst->xattr.value_len = cpu_to_le32(src->xattr.value_len); - dst->xattr.cmp_op = src->xattr.cmp_op; - dst->xattr.cmp_mode = src->xattr.cmp_mode; - ceph_pagelist_append(req->r_trail, src->xattr.name, - src->xattr.name_len); - ceph_pagelist_append(req->r_trail, src->xattr.val, - src->xattr.value_len); - break; - case CEPH_OSD_OP_CALL: - BUG_ON(!req->r_trail); - - dst->cls.class_len = src->cls.class_len; - dst->cls.method_len = src->cls.method_len; - dst->cls.indata_len = cpu_to_le32(src->cls.indata_len); - - ceph_pagelist_append(req->r_trail, src->cls.class_name, - src->cls.class_len); - ceph_pagelist_append(req->r_trail, src->cls.method_name, - src->cls.method_len); - ceph_pagelist_append(req->r_trail, src->cls.indata, - src->cls.indata_len); - break; - case CEPH_OSD_OP_ROLLBACK: - dst->snap.snapid = cpu_to_le64(src->snap.snapid); - break; - case CEPH_OSD_OP_STARTSYNC: - break; - default: - pr_err("unrecognized osd opcode %d\n", dst->op); - WARN_ON(1); - break; - } - dst->payload_len = cpu_to_le32(src->payload_len); -} - -/* - * build new request AND message - * - */ -void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 *plen, - struct ceph_osd_req_op *src_ops, - struct ceph_snap_context *snapc, - struct timespec *mtime, - const char *oid, - int oid_len) -{ - struct ceph_msg *msg = req->r_request; - struct ceph_osd_request_head *head; - struct ceph_osd_req_op *src_op; - struct ceph_osd_op *op; - void *p; - int num_op = get_num_ops(src_ops, NULL); - size_t msg_size = sizeof(*head) + num_op*sizeof(*op); - int flags = req->r_flags; - u64 data_len = 0; - int i; - - head = msg->front.iov_base; - op = (void *)(head + 1); - p = (void *)(op + num_op); - - req->r_snapc = ceph_get_snap_context(snapc); - - head->client_inc = cpu_to_le32(1); /* always, for now. */ - head->flags = cpu_to_le32(flags); - if (flags & CEPH_OSD_FLAG_WRITE) - ceph_encode_timespec(&head->mtime, mtime); - head->num_ops = cpu_to_le16(num_op); - - - /* fill in oid */ - head->object_len = cpu_to_le32(oid_len); - memcpy(p, oid, oid_len); - p += oid_len; - - src_op = src_ops; - while (src_op->op) { - osd_req_encode_op(req, op, src_op); - src_op++; - op++; - } - - if (req->r_trail) - data_len += req->r_trail->length; - - if (snapc) { - head->snap_seq = cpu_to_le64(snapc->seq); - head->num_snaps = cpu_to_le32(snapc->num_snaps); - for (i = 0; i < snapc->num_snaps; i++) { - put_unaligned_le64(snapc->snaps[i], p); - p += sizeof(u64); - } - } - - if (flags & CEPH_OSD_FLAG_WRITE) { - req->r_request->hdr.data_off = cpu_to_le16(off); - req->r_request->hdr.data_len = cpu_to_le32(*plen + data_len); - } else if (data_len) { - req->r_request->hdr.data_off = 0; - req->r_request->hdr.data_len = cpu_to_le32(data_len); - } - - BUG_ON(p > msg->front.iov_base + msg->front.iov_len); - msg_size = p - msg->front.iov_base; - msg->front.iov_len = msg_size; - msg->hdr.front_len = cpu_to_le32(msg_size); - return; -} - -/* - * build new request AND message, calculate layout, and adjust file - * extent as needed. - * - * if the file was recently truncated, we include information about its - * old and new size so that the object can be updated appropriately. (we - * avoid synchronously deleting truncated objects because it's slow.) - * - * if @do_sync, include a 'startsync' command so that the osd will flush - * data quickly. - */ -struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, - struct ceph_file_layout *layout, - struct ceph_vino vino, - u64 off, u64 *plen, - int opcode, int flags, - struct ceph_snap_context *snapc, - int do_sync, - u32 truncate_seq, - u64 truncate_size, - struct timespec *mtime, - bool use_mempool, int num_reply) -{ - struct ceph_osd_req_op ops[3]; - struct ceph_osd_request *req; - - ops[0].op = opcode; - ops[0].extent.truncate_seq = truncate_seq; - ops[0].extent.truncate_size = truncate_size; - ops[0].payload_len = 0; - - if (do_sync) { - ops[1].op = CEPH_OSD_OP_STARTSYNC; - ops[1].payload_len = 0; - ops[2].op = 0; - } else - ops[1].op = 0; - - req = ceph_osdc_alloc_request(osdc, flags, - snapc, ops, - use_mempool, - GFP_NOFS, NULL, NULL); - if (IS_ERR(req)) - return req; - - /* calculate max write size */ - calc_layout(osdc, vino, layout, off, plen, req, ops); - req->r_file_layout = *layout; /* keep a copy */ - - ceph_osdc_build_request(req, off, plen, ops, - snapc, - mtime, - req->r_oid, req->r_oid_len); - - return req; -} - -/* - * We keep osd requests in an rbtree, sorted by ->r_tid. - */ -static void __insert_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *new) -{ - struct rb_node **p = &osdc->requests.rb_node; - struct rb_node *parent = NULL; - struct ceph_osd_request *req = NULL; - - while (*p) { - parent = *p; - req = rb_entry(parent, struct ceph_osd_request, r_node); - if (new->r_tid < req->r_tid) - p = &(*p)->rb_left; - else if (new->r_tid > req->r_tid) - p = &(*p)->rb_right; - else - BUG(); - } - - rb_link_node(&new->r_node, parent, p); - rb_insert_color(&new->r_node, &osdc->requests); -} - -static struct ceph_osd_request *__lookup_request(struct ceph_osd_client *osdc, - u64 tid) -{ - struct ceph_osd_request *req; - struct rb_node *n = osdc->requests.rb_node; - - while (n) { - req = rb_entry(n, struct ceph_osd_request, r_node); - if (tid < req->r_tid) - n = n->rb_left; - else if (tid > req->r_tid) - n = n->rb_right; - else - return req; - } - return NULL; -} - -static struct ceph_osd_request * -__lookup_request_ge(struct ceph_osd_client *osdc, - u64 tid) -{ - struct ceph_osd_request *req; - struct rb_node *n = osdc->requests.rb_node; - - while (n) { - req = rb_entry(n, struct ceph_osd_request, r_node); - if (tid < req->r_tid) { - if (!n->rb_left) - return req; - n = n->rb_left; - } else if (tid > req->r_tid) { - n = n->rb_right; - } else { - return req; - } - } - return NULL; -} - - -/* - * If the osd connection drops, we need to resubmit all requests. - */ -static void osd_reset(struct ceph_connection *con) -{ - struct ceph_osd *osd = con->private; - struct ceph_osd_client *osdc; - - if (!osd) - return; - dout("osd_reset osd%d\n", osd->o_osd); - osdc = osd->o_osdc; - down_read(&osdc->map_sem); - kick_requests(osdc, osd); - up_read(&osdc->map_sem); -} - -/* - * Track open sessions with osds. - */ -static struct ceph_osd *create_osd(struct ceph_osd_client *osdc) -{ - struct ceph_osd *osd; - - osd = kzalloc(sizeof(*osd), GFP_NOFS); - if (!osd) - return NULL; - - atomic_set(&osd->o_ref, 1); - osd->o_osdc = osdc; - INIT_LIST_HEAD(&osd->o_requests); - INIT_LIST_HEAD(&osd->o_osd_lru); - osd->o_incarnation = 1; - - ceph_con_init(osdc->client->msgr, &osd->o_con); - osd->o_con.private = osd; - osd->o_con.ops = &osd_con_ops; - osd->o_con.peer_name.type = CEPH_ENTITY_TYPE_OSD; - - INIT_LIST_HEAD(&osd->o_keepalive_item); - return osd; -} - -static struct ceph_osd *get_osd(struct ceph_osd *osd) -{ - if (atomic_inc_not_zero(&osd->o_ref)) { - dout("get_osd %p %d -> %d\n", osd, atomic_read(&osd->o_ref)-1, - atomic_read(&osd->o_ref)); - return osd; - } else { - dout("get_osd %p FAIL\n", osd); - return NULL; - } -} - -static void put_osd(struct ceph_osd *osd) -{ - dout("put_osd %p %d -> %d\n", osd, atomic_read(&osd->o_ref), - atomic_read(&osd->o_ref) - 1); - if (atomic_dec_and_test(&osd->o_ref)) { - struct ceph_auth_client *ac = osd->o_osdc->client->monc.auth; - - if (osd->o_authorizer) - ac->ops->destroy_authorizer(ac, osd->o_authorizer); - kfree(osd); - } -} - -/* - * remove an osd from our map - */ -static void __remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) -{ - dout("__remove_osd %p\n", osd); - BUG_ON(!list_empty(&osd->o_requests)); - rb_erase(&osd->o_node, &osdc->osds); - list_del_init(&osd->o_osd_lru); - ceph_con_close(&osd->o_con); - put_osd(osd); -} - -static void __move_osd_to_lru(struct ceph_osd_client *osdc, - struct ceph_osd *osd) -{ - dout("__move_osd_to_lru %p\n", osd); - BUG_ON(!list_empty(&osd->o_osd_lru)); - list_add_tail(&osd->o_osd_lru, &osdc->osd_lru); - osd->lru_ttl = jiffies + osdc->client->mount_args->osd_idle_ttl * HZ; -} - -static void __remove_osd_from_lru(struct ceph_osd *osd) -{ - dout("__remove_osd_from_lru %p\n", osd); - if (!list_empty(&osd->o_osd_lru)) - list_del_init(&osd->o_osd_lru); -} - -static void remove_old_osds(struct ceph_osd_client *osdc, int remove_all) -{ - struct ceph_osd *osd, *nosd; - - dout("__remove_old_osds %p\n", osdc); - mutex_lock(&osdc->request_mutex); - list_for_each_entry_safe(osd, nosd, &osdc->osd_lru, o_osd_lru) { - if (!remove_all && time_before(jiffies, osd->lru_ttl)) - break; - __remove_osd(osdc, osd); - } - mutex_unlock(&osdc->request_mutex); -} - -/* - * reset osd connect - */ -static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) -{ - struct ceph_osd_request *req; - int ret = 0; - - dout("__reset_osd %p osd%d\n", osd, osd->o_osd); - if (list_empty(&osd->o_requests)) { - __remove_osd(osdc, osd); - } else if (memcmp(&osdc->osdmap->osd_addr[osd->o_osd], - &osd->o_con.peer_addr, - sizeof(osd->o_con.peer_addr)) == 0 && - !ceph_con_opened(&osd->o_con)) { - dout(" osd addr hasn't changed and connection never opened," - " letting msgr retry"); - /* touch each r_stamp for handle_timeout()'s benfit */ - list_for_each_entry(req, &osd->o_requests, r_osd_item) - req->r_stamp = jiffies; - ret = -EAGAIN; - } else { - ceph_con_close(&osd->o_con); - ceph_con_open(&osd->o_con, &osdc->osdmap->osd_addr[osd->o_osd]); - osd->o_incarnation++; - } - return ret; -} - -static void __insert_osd(struct ceph_osd_client *osdc, struct ceph_osd *new) -{ - struct rb_node **p = &osdc->osds.rb_node; - struct rb_node *parent = NULL; - struct ceph_osd *osd = NULL; - - while (*p) { - parent = *p; - osd = rb_entry(parent, struct ceph_osd, o_node); - if (new->o_osd < osd->o_osd) - p = &(*p)->rb_left; - else if (new->o_osd > osd->o_osd) - p = &(*p)->rb_right; - else - BUG(); - } - - rb_link_node(&new->o_node, parent, p); - rb_insert_color(&new->o_node, &osdc->osds); -} - -static struct ceph_osd *__lookup_osd(struct ceph_osd_client *osdc, int o) -{ - struct ceph_osd *osd; - struct rb_node *n = osdc->osds.rb_node; - - while (n) { - osd = rb_entry(n, struct ceph_osd, o_node); - if (o < osd->o_osd) - n = n->rb_left; - else if (o > osd->o_osd) - n = n->rb_right; - else - return osd; - } - return NULL; -} - -static void __schedule_osd_timeout(struct ceph_osd_client *osdc) -{ - schedule_delayed_work(&osdc->timeout_work, - osdc->client->mount_args->osd_keepalive_timeout * HZ); -} - -static void __cancel_osd_timeout(struct ceph_osd_client *osdc) -{ - cancel_delayed_work(&osdc->timeout_work); -} - -/* - * Register request, assign tid. If this is the first request, set up - * the timeout event. - */ -static void register_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req) -{ - mutex_lock(&osdc->request_mutex); - req->r_tid = ++osdc->last_tid; - req->r_request->hdr.tid = cpu_to_le64(req->r_tid); - INIT_LIST_HEAD(&req->r_req_lru_item); - - dout("register_request %p tid %lld\n", req, req->r_tid); - __insert_request(osdc, req); - ceph_osdc_get_request(req); - osdc->num_requests++; - - if (osdc->num_requests == 1) { - dout(" first request, scheduling timeout\n"); - __schedule_osd_timeout(osdc); - } - mutex_unlock(&osdc->request_mutex); -} - -/* - * called under osdc->request_mutex - */ -static void __unregister_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req) -{ - dout("__unregister_request %p tid %lld\n", req, req->r_tid); - rb_erase(&req->r_node, &osdc->requests); - osdc->num_requests--; - - if (req->r_osd) { - /* make sure the original request isn't in flight. */ - ceph_con_revoke(&req->r_osd->o_con, req->r_request); - - list_del_init(&req->r_osd_item); - if (list_empty(&req->r_osd->o_requests)) - __move_osd_to_lru(osdc, req->r_osd); - req->r_osd = NULL; - } - - ceph_osdc_put_request(req); - - list_del_init(&req->r_req_lru_item); - if (osdc->num_requests == 0) { - dout(" no requests, canceling timeout\n"); - __cancel_osd_timeout(osdc); - } -} - -/* - * Cancel a previously queued request message - */ -static void __cancel_request(struct ceph_osd_request *req) -{ - if (req->r_sent && req->r_osd) { - ceph_con_revoke(&req->r_osd->o_con, req->r_request); - req->r_sent = 0; - } - list_del_init(&req->r_req_lru_item); -} - -/* - * Pick an osd (the first 'up' osd in the pg), allocate the osd struct - * (as needed), and set the request r_osd appropriately. If there is - * no up osd, set r_osd to NULL. - * - * Return 0 if unchanged, 1 if changed, or negative on error. - * - * Caller should hold map_sem for read and request_mutex. - */ -static int __map_osds(struct ceph_osd_client *osdc, - struct ceph_osd_request *req) -{ - struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; - struct ceph_pg pgid; - int acting[CEPH_PG_MAX_SIZE]; - int o = -1, num = 0; - int err; - - dout("map_osds %p tid %lld\n", req, req->r_tid); - err = ceph_calc_object_layout(&reqhead->layout, req->r_oid, - &req->r_file_layout, osdc->osdmap); - if (err) - return err; - pgid = reqhead->layout.ol_pgid; - req->r_pgid = pgid; - - err = ceph_calc_pg_acting(osdc->osdmap, pgid, acting); - if (err > 0) { - o = acting[0]; - num = err; - } - - if ((req->r_osd && req->r_osd->o_osd == o && - req->r_sent >= req->r_osd->o_incarnation && - req->r_num_pg_osds == num && - memcmp(req->r_pg_osds, acting, sizeof(acting[0])*num) == 0) || - (req->r_osd == NULL && o == -1)) - return 0; /* no change */ - - dout("map_osds tid %llu pgid %d.%x osd%d (was osd%d)\n", - req->r_tid, le32_to_cpu(pgid.pool), le16_to_cpu(pgid.ps), o, - req->r_osd ? req->r_osd->o_osd : -1); - - /* record full pg acting set */ - memcpy(req->r_pg_osds, acting, sizeof(acting[0]) * num); - req->r_num_pg_osds = num; - - if (req->r_osd) { - __cancel_request(req); - list_del_init(&req->r_osd_item); - req->r_osd = NULL; - } - - req->r_osd = __lookup_osd(osdc, o); - if (!req->r_osd && o >= 0) { - err = -ENOMEM; - req->r_osd = create_osd(osdc); - if (!req->r_osd) - goto out; - - dout("map_osds osd %p is osd%d\n", req->r_osd, o); - req->r_osd->o_osd = o; - req->r_osd->o_con.peer_name.num = cpu_to_le64(o); - __insert_osd(osdc, req->r_osd); - - ceph_con_open(&req->r_osd->o_con, &osdc->osdmap->osd_addr[o]); - } - - if (req->r_osd) { - __remove_osd_from_lru(req->r_osd); - list_add(&req->r_osd_item, &req->r_osd->o_requests); - } - err = 1; /* osd or pg changed */ - -out: - return err; -} - -/* - * caller should hold map_sem (for read) and request_mutex - */ -static int __send_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req) -{ - struct ceph_osd_request_head *reqhead; - int err; - - err = __map_osds(osdc, req); - if (err < 0) - return err; - if (req->r_osd == NULL) { - dout("send_request %p no up osds in pg\n", req); - ceph_monc_request_next_osdmap(&osdc->client->monc); - return 0; - } - - dout("send_request %p tid %llu to osd%d flags %d\n", - req, req->r_tid, req->r_osd->o_osd, req->r_flags); - - reqhead = req->r_request->front.iov_base; - reqhead->osdmap_epoch = cpu_to_le32(osdc->osdmap->epoch); - reqhead->flags |= cpu_to_le32(req->r_flags); /* e.g., RETRY */ - reqhead->reassert_version = req->r_reassert_version; - - req->r_stamp = jiffies; - list_move_tail(&req->r_req_lru_item, &osdc->req_lru); - - ceph_msg_get(req->r_request); /* send consumes a ref */ - ceph_con_send(&req->r_osd->o_con, req->r_request); - req->r_sent = req->r_osd->o_incarnation; - return 0; -} - -/* - * Timeout callback, called every N seconds when 1 or more osd - * requests has been active for more than N seconds. When this - * happens, we ping all OSDs with requests who have timed out to - * ensure any communications channel reset is detected. Reset the - * request timeouts another N seconds in the future as we go. - * Reschedule the timeout event another N seconds in future (unless - * there are no open requests). - */ -static void handle_timeout(struct work_struct *work) -{ - struct ceph_osd_client *osdc = - container_of(work, struct ceph_osd_client, timeout_work.work); - struct ceph_osd_request *req, *last_req = NULL; - struct ceph_osd *osd; - unsigned long timeout = osdc->client->mount_args->osd_timeout * HZ; - unsigned long keepalive = - osdc->client->mount_args->osd_keepalive_timeout * HZ; - unsigned long last_stamp = 0; - struct rb_node *p; - struct list_head slow_osds; - - dout("timeout\n"); - down_read(&osdc->map_sem); - - ceph_monc_request_next_osdmap(&osdc->client->monc); - - mutex_lock(&osdc->request_mutex); - for (p = rb_first(&osdc->requests); p; p = rb_next(p)) { - req = rb_entry(p, struct ceph_osd_request, r_node); - - if (req->r_resend) { - int err; - - dout("osdc resending prev failed %lld\n", req->r_tid); - err = __send_request(osdc, req); - if (err) - dout("osdc failed again on %lld\n", req->r_tid); - else - req->r_resend = false; - continue; - } - } - - /* - * reset osds that appear to be _really_ unresponsive. this - * is a failsafe measure.. we really shouldn't be getting to - * this point if the system is working properly. the monitors - * should mark the osd as failed and we should find out about - * it from an updated osd map. - */ - while (timeout && !list_empty(&osdc->req_lru)) { - req = list_entry(osdc->req_lru.next, struct ceph_osd_request, - r_req_lru_item); - - if (time_before(jiffies, req->r_stamp + timeout)) - break; - - BUG_ON(req == last_req && req->r_stamp == last_stamp); - last_req = req; - last_stamp = req->r_stamp; - - osd = req->r_osd; - BUG_ON(!osd); - pr_warning(" tid %llu timed out on osd%d, will reset osd\n", - req->r_tid, osd->o_osd); - __kick_requests(osdc, osd); - } - - /* - * ping osds that are a bit slow. this ensures that if there - * is a break in the TCP connection we will notice, and reopen - * a connection with that osd (from the fault callback). - */ - INIT_LIST_HEAD(&slow_osds); - list_for_each_entry(req, &osdc->req_lru, r_req_lru_item) { - if (time_before(jiffies, req->r_stamp + keepalive)) - break; - - osd = req->r_osd; - BUG_ON(!osd); - dout(" tid %llu is slow, will send keepalive on osd%d\n", - req->r_tid, osd->o_osd); - list_move_tail(&osd->o_keepalive_item, &slow_osds); - } - while (!list_empty(&slow_osds)) { - osd = list_entry(slow_osds.next, struct ceph_osd, - o_keepalive_item); - list_del_init(&osd->o_keepalive_item); - ceph_con_keepalive(&osd->o_con); - } - - __schedule_osd_timeout(osdc); - mutex_unlock(&osdc->request_mutex); - - up_read(&osdc->map_sem); -} - -static void handle_osds_timeout(struct work_struct *work) -{ - struct ceph_osd_client *osdc = - container_of(work, struct ceph_osd_client, - osds_timeout_work.work); - unsigned long delay = - osdc->client->mount_args->osd_idle_ttl * HZ >> 2; - - dout("osds timeout\n"); - down_read(&osdc->map_sem); - remove_old_osds(osdc, 0); - up_read(&osdc->map_sem); - - schedule_delayed_work(&osdc->osds_timeout_work, - round_jiffies_relative(delay)); -} - -/* - * handle osd op reply. either call the callback if it is specified, - * or do the completion to wake up the waiting thread. - */ -static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg, - struct ceph_connection *con) -{ - struct ceph_osd_reply_head *rhead = msg->front.iov_base; - struct ceph_osd_request *req; - u64 tid; - int numops, object_len, flags; - s32 result; - - tid = le64_to_cpu(msg->hdr.tid); - if (msg->front.iov_len < sizeof(*rhead)) - goto bad; - numops = le32_to_cpu(rhead->num_ops); - object_len = le32_to_cpu(rhead->object_len); - result = le32_to_cpu(rhead->result); - if (msg->front.iov_len != sizeof(*rhead) + object_len + - numops * sizeof(struct ceph_osd_op)) - goto bad; - dout("handle_reply %p tid %llu result %d\n", msg, tid, (int)result); - - /* lookup */ - mutex_lock(&osdc->request_mutex); - req = __lookup_request(osdc, tid); - if (req == NULL) { - dout("handle_reply tid %llu dne\n", tid); - mutex_unlock(&osdc->request_mutex); - return; - } - ceph_osdc_get_request(req); - flags = le32_to_cpu(rhead->flags); - - /* - * if this connection filled our message, drop our reference now, to - * avoid a (safe but slower) revoke later. - */ - if (req->r_con_filling_msg == con && req->r_reply == msg) { - dout(" dropping con_filling_msg ref %p\n", con); - req->r_con_filling_msg = NULL; - ceph_con_put(con); - } - - if (!req->r_got_reply) { - unsigned bytes; - - req->r_result = le32_to_cpu(rhead->result); - bytes = le32_to_cpu(msg->hdr.data_len); - dout("handle_reply result %d bytes %d\n", req->r_result, - bytes); - if (req->r_result == 0) - req->r_result = bytes; - - /* in case this is a write and we need to replay, */ - req->r_reassert_version = rhead->reassert_version; - - req->r_got_reply = 1; - } else if ((flags & CEPH_OSD_FLAG_ONDISK) == 0) { - dout("handle_reply tid %llu dup ack\n", tid); - mutex_unlock(&osdc->request_mutex); - goto done; - } - - dout("handle_reply tid %llu flags %d\n", tid, flags); - - /* either this is a read, or we got the safe response */ - if (result < 0 || - (flags & CEPH_OSD_FLAG_ONDISK) || - ((flags & CEPH_OSD_FLAG_WRITE) == 0)) - __unregister_request(osdc, req); - - mutex_unlock(&osdc->request_mutex); - - if (req->r_callback) - req->r_callback(req, msg); - else - complete_all(&req->r_completion); - - if (flags & CEPH_OSD_FLAG_ONDISK) { - if (req->r_safe_callback) - req->r_safe_callback(req, msg); - complete_all(&req->r_safe_completion); /* fsync waiter */ - } - -done: - ceph_osdc_put_request(req); - return; - -bad: - pr_err("corrupt osd_op_reply got %d %d expected %d\n", - (int)msg->front.iov_len, le32_to_cpu(msg->hdr.front_len), - (int)sizeof(*rhead)); - ceph_msg_dump(msg); -} - - -static int __kick_requests(struct ceph_osd_client *osdc, - struct ceph_osd *kickosd) -{ - struct ceph_osd_request *req; - struct rb_node *p, *n; - int needmap = 0; - int err; - - dout("kick_requests osd%d\n", kickosd ? kickosd->o_osd : -1); - if (kickosd) { - err = __reset_osd(osdc, kickosd); - if (err == -EAGAIN) - return 1; - } else { - for (p = rb_first(&osdc->osds); p; p = n) { - struct ceph_osd *osd = - rb_entry(p, struct ceph_osd, o_node); - - n = rb_next(p); - if (!ceph_osd_is_up(osdc->osdmap, osd->o_osd) || - memcmp(&osd->o_con.peer_addr, - ceph_osd_addr(osdc->osdmap, - osd->o_osd), - sizeof(struct ceph_entity_addr)) != 0) - __reset_osd(osdc, osd); - } - } - - for (p = rb_first(&osdc->requests); p; p = rb_next(p)) { - req = rb_entry(p, struct ceph_osd_request, r_node); - - if (req->r_resend) { - dout(" r_resend set on tid %llu\n", req->r_tid); - __cancel_request(req); - goto kick; - } - if (req->r_osd && kickosd == req->r_osd) { - __cancel_request(req); - goto kick; - } - - err = __map_osds(osdc, req); - if (err == 0) - continue; /* no change */ - if (err < 0) { - /* - * FIXME: really, we should set the request - * error and fail if this isn't a 'nofail' - * request, but that's a fair bit more - * complicated to do. So retry! - */ - dout(" setting r_resend on %llu\n", req->r_tid); - req->r_resend = true; - continue; - } - if (req->r_osd == NULL) { - dout("tid %llu maps to no valid osd\n", req->r_tid); - needmap++; /* request a newer map */ - continue; - } - -kick: - dout("kicking %p tid %llu osd%d\n", req, req->r_tid, - req->r_osd ? req->r_osd->o_osd : -1); - req->r_flags |= CEPH_OSD_FLAG_RETRY; - err = __send_request(osdc, req); - if (err) { - dout(" setting r_resend on %llu\n", req->r_tid); - req->r_resend = true; - } - } - - return needmap; -} - -/* - * Resubmit osd requests whose osd or osd address has changed. Request - * a new osd map if osds are down, or we are otherwise unable to determine - * how to direct a request. - * - * Close connections to down osds. - * - * If @who is specified, resubmit requests for that specific osd. - * - * Caller should hold map_sem for read and request_mutex. - */ -static void kick_requests(struct ceph_osd_client *osdc, - struct ceph_osd *kickosd) -{ - int needmap; - - mutex_lock(&osdc->request_mutex); - needmap = __kick_requests(osdc, kickosd); - mutex_unlock(&osdc->request_mutex); - - if (needmap) { - dout("%d requests for down osds, need new map\n", needmap); - ceph_monc_request_next_osdmap(&osdc->client->monc); - } - -} -/* - * Process updated osd map. - * - * The message contains any number of incremental and full maps, normally - * indicating some sort of topology change in the cluster. Kick requests - * off to different OSDs as needed. - */ -void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) -{ - void *p, *end, *next; - u32 nr_maps, maplen; - u32 epoch; - struct ceph_osdmap *newmap = NULL, *oldmap; - int err; - struct ceph_fsid fsid; - - dout("handle_map have %u\n", osdc->osdmap ? osdc->osdmap->epoch : 0); - p = msg->front.iov_base; - end = p + msg->front.iov_len; - - /* verify fsid */ - ceph_decode_need(&p, end, sizeof(fsid), bad); - ceph_decode_copy(&p, &fsid, sizeof(fsid)); - if (ceph_check_fsid(osdc->client, &fsid) < 0) - return; - - down_write(&osdc->map_sem); - - /* incremental maps */ - ceph_decode_32_safe(&p, end, nr_maps, bad); - dout(" %d inc maps\n", nr_maps); - while (nr_maps > 0) { - ceph_decode_need(&p, end, 2*sizeof(u32), bad); - epoch = ceph_decode_32(&p); - maplen = ceph_decode_32(&p); - ceph_decode_need(&p, end, maplen, bad); - next = p + maplen; - if (osdc->osdmap && osdc->osdmap->epoch+1 == epoch) { - dout("applying incremental map %u len %d\n", - epoch, maplen); - newmap = osdmap_apply_incremental(&p, next, - osdc->osdmap, - osdc->client->msgr); - if (IS_ERR(newmap)) { - err = PTR_ERR(newmap); - goto bad; - } - BUG_ON(!newmap); - if (newmap != osdc->osdmap) { - ceph_osdmap_destroy(osdc->osdmap); - osdc->osdmap = newmap; - } - } else { - dout("ignoring incremental map %u len %d\n", - epoch, maplen); - } - p = next; - nr_maps--; - } - if (newmap) - goto done; - - /* full maps */ - ceph_decode_32_safe(&p, end, nr_maps, bad); - dout(" %d full maps\n", nr_maps); - while (nr_maps) { - ceph_decode_need(&p, end, 2*sizeof(u32), bad); - epoch = ceph_decode_32(&p); - maplen = ceph_decode_32(&p); - ceph_decode_need(&p, end, maplen, bad); - if (nr_maps > 1) { - dout("skipping non-latest full map %u len %d\n", - epoch, maplen); - } else if (osdc->osdmap && osdc->osdmap->epoch >= epoch) { - dout("skipping full map %u len %d, " - "older than our %u\n", epoch, maplen, - osdc->osdmap->epoch); - } else { - dout("taking full map %u len %d\n", epoch, maplen); - newmap = osdmap_decode(&p, p+maplen); - if (IS_ERR(newmap)) { - err = PTR_ERR(newmap); - goto bad; - } - BUG_ON(!newmap); - oldmap = osdc->osdmap; - osdc->osdmap = newmap; - if (oldmap) - ceph_osdmap_destroy(oldmap); - } - p += maplen; - nr_maps--; - } - -done: - downgrade_write(&osdc->map_sem); - ceph_monc_got_osdmap(&osdc->client->monc, osdc->osdmap->epoch); - if (newmap) - kick_requests(osdc, NULL); - up_read(&osdc->map_sem); - wake_up_all(&osdc->client->auth_wq); - return; - -bad: - pr_err("osdc handle_map corrupt msg\n"); - ceph_msg_dump(msg); - up_write(&osdc->map_sem); - return; -} - -/* - * Register request, send initial attempt. - */ -int ceph_osdc_start_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req, - bool nofail) -{ - int rc = 0; - - req->r_request->pages = req->r_pages; - req->r_request->nr_pages = req->r_num_pages; -#ifdef CONFIG_BLOCK - req->r_request->bio = req->r_bio; -#endif - req->r_request->trail = req->r_trail; - - register_request(osdc, req); - - down_read(&osdc->map_sem); - mutex_lock(&osdc->request_mutex); - /* - * a racing kick_requests() may have sent the message for us - * while we dropped request_mutex above, so only send now if - * the request still han't been touched yet. - */ - if (req->r_sent == 0) { - rc = __send_request(osdc, req); - if (rc) { - if (nofail) { - dout("osdc_start_request failed send, " - " marking %lld\n", req->r_tid); - req->r_resend = true; - rc = 0; - } else { - __unregister_request(osdc, req); - } - } - } - mutex_unlock(&osdc->request_mutex); - up_read(&osdc->map_sem); - return rc; -} - -/* - * wait for a request to complete - */ -int ceph_osdc_wait_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req) -{ - int rc; - - rc = wait_for_completion_interruptible(&req->r_completion); - if (rc < 0) { - mutex_lock(&osdc->request_mutex); - __cancel_request(req); - __unregister_request(osdc, req); - mutex_unlock(&osdc->request_mutex); - dout("wait_request tid %llu canceled/timed out\n", req->r_tid); - return rc; - } - - dout("wait_request tid %llu result %d\n", req->r_tid, req->r_result); - return req->r_result; -} - -/* - * sync - wait for all in-flight requests to flush. avoid starvation. - */ -void ceph_osdc_sync(struct ceph_osd_client *osdc) -{ - struct ceph_osd_request *req; - u64 last_tid, next_tid = 0; - - mutex_lock(&osdc->request_mutex); - last_tid = osdc->last_tid; - while (1) { - req = __lookup_request_ge(osdc, next_tid); - if (!req) - break; - if (req->r_tid > last_tid) - break; - - next_tid = req->r_tid + 1; - if ((req->r_flags & CEPH_OSD_FLAG_WRITE) == 0) - continue; - - ceph_osdc_get_request(req); - mutex_unlock(&osdc->request_mutex); - dout("sync waiting on tid %llu (last is %llu)\n", - req->r_tid, last_tid); - wait_for_completion(&req->r_safe_completion); - mutex_lock(&osdc->request_mutex); - ceph_osdc_put_request(req); - } - mutex_unlock(&osdc->request_mutex); - dout("sync done (thru tid %llu)\n", last_tid); -} - -/* - * init, shutdown - */ -int ceph_osdc_init(struct ceph_osd_client *osdc, struct ceph_client *client) -{ - int err; - - dout("init\n"); - osdc->client = client; - osdc->osdmap = NULL; - init_rwsem(&osdc->map_sem); - init_completion(&osdc->map_waiters); - osdc->last_requested_map = 0; - mutex_init(&osdc->request_mutex); - osdc->last_tid = 0; - osdc->osds = RB_ROOT; - INIT_LIST_HEAD(&osdc->osd_lru); - osdc->requests = RB_ROOT; - INIT_LIST_HEAD(&osdc->req_lru); - osdc->num_requests = 0; - INIT_DELAYED_WORK(&osdc->timeout_work, handle_timeout); - INIT_DELAYED_WORK(&osdc->osds_timeout_work, handle_osds_timeout); - - schedule_delayed_work(&osdc->osds_timeout_work, - round_jiffies_relative(osdc->client->mount_args->osd_idle_ttl * HZ)); - - err = -ENOMEM; - osdc->req_mempool = mempool_create_kmalloc_pool(10, - sizeof(struct ceph_osd_request)); - if (!osdc->req_mempool) - goto out; - - err = ceph_msgpool_init(&osdc->msgpool_op, OSD_OP_FRONT_LEN, 10, true, - "osd_op"); - if (err < 0) - goto out_mempool; - err = ceph_msgpool_init(&osdc->msgpool_op_reply, - OSD_OPREPLY_FRONT_LEN, 10, true, - "osd_op_reply"); - if (err < 0) - goto out_msgpool; - return 0; - -out_msgpool: - ceph_msgpool_destroy(&osdc->msgpool_op); -out_mempool: - mempool_destroy(osdc->req_mempool); -out: - return err; -} - -void ceph_osdc_stop(struct ceph_osd_client *osdc) -{ - cancel_delayed_work_sync(&osdc->timeout_work); - cancel_delayed_work_sync(&osdc->osds_timeout_work); - if (osdc->osdmap) { - ceph_osdmap_destroy(osdc->osdmap); - osdc->osdmap = NULL; - } - remove_old_osds(osdc, 1); - mempool_destroy(osdc->req_mempool); - ceph_msgpool_destroy(&osdc->msgpool_op); - ceph_msgpool_destroy(&osdc->msgpool_op_reply); -} - -/* - * Read some contiguous pages. If we cross a stripe boundary, shorten - * *plen. Return number of bytes read, or error. - */ -int ceph_osdc_readpages(struct ceph_osd_client *osdc, - struct ceph_vino vino, struct ceph_file_layout *layout, - u64 off, u64 *plen, - u32 truncate_seq, u64 truncate_size, - struct page **pages, int num_pages) -{ - struct ceph_osd_request *req; - int rc = 0; - - dout("readpages on ino %llx.%llx on %llu~%llu\n", vino.ino, - vino.snap, off, *plen); - req = ceph_osdc_new_request(osdc, layout, vino, off, plen, - CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, - NULL, 0, truncate_seq, truncate_size, NULL, - false, 1); - if (!req) - return -ENOMEM; - - /* it may be a short read due to an object boundary */ - req->r_pages = pages; - - dout("readpages final extent is %llu~%llu (%d pages)\n", - off, *plen, req->r_num_pages); - - rc = ceph_osdc_start_request(osdc, req, false); - if (!rc) - rc = ceph_osdc_wait_request(osdc, req); - - ceph_osdc_put_request(req); - dout("readpages result %d\n", rc); - return rc; -} - -/* - * do a synchronous write on N pages - */ -int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, - struct ceph_file_layout *layout, - struct ceph_snap_context *snapc, - u64 off, u64 len, - u32 truncate_seq, u64 truncate_size, - struct timespec *mtime, - struct page **pages, int num_pages, - int flags, int do_sync, bool nofail) -{ - struct ceph_osd_request *req; - int rc = 0; - - BUG_ON(vino.snap != CEPH_NOSNAP); - req = ceph_osdc_new_request(osdc, layout, vino, off, &len, - CEPH_OSD_OP_WRITE, - flags | CEPH_OSD_FLAG_ONDISK | - CEPH_OSD_FLAG_WRITE, - snapc, do_sync, - truncate_seq, truncate_size, mtime, - nofail, 1); - if (!req) - return -ENOMEM; - - /* it may be a short write due to an object boundary */ - req->r_pages = pages; - dout("writepages %llu~%llu (%d pages)\n", off, len, - req->r_num_pages); - - rc = ceph_osdc_start_request(osdc, req, nofail); - if (!rc) - rc = ceph_osdc_wait_request(osdc, req); - - ceph_osdc_put_request(req); - if (rc == 0) - rc = len; - dout("writepages result %d\n", rc); - return rc; -} - -/* - * handle incoming message - */ -static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) -{ - struct ceph_osd *osd = con->private; - struct ceph_osd_client *osdc; - int type = le16_to_cpu(msg->hdr.type); - - if (!osd) - goto out; - osdc = osd->o_osdc; - - switch (type) { - case CEPH_MSG_OSD_MAP: - ceph_osdc_handle_map(osdc, msg); - break; - case CEPH_MSG_OSD_OPREPLY: - handle_reply(osdc, msg, con); - break; - - default: - pr_err("received unknown message type %d %s\n", type, - ceph_msg_type_name(type)); - } -out: - ceph_msg_put(msg); -} - -/* - * lookup and return message for incoming reply. set up reply message - * pages. - */ -static struct ceph_msg *get_reply(struct ceph_connection *con, - struct ceph_msg_header *hdr, - int *skip) -{ - struct ceph_osd *osd = con->private; - struct ceph_osd_client *osdc = osd->o_osdc; - struct ceph_msg *m; - struct ceph_osd_request *req; - int front = le32_to_cpu(hdr->front_len); - int data_len = le32_to_cpu(hdr->data_len); - u64 tid; - - tid = le64_to_cpu(hdr->tid); - mutex_lock(&osdc->request_mutex); - req = __lookup_request(osdc, tid); - if (!req) { - *skip = 1; - m = NULL; - pr_info("get_reply unknown tid %llu from osd%d\n", tid, - osd->o_osd); - goto out; - } - - if (req->r_con_filling_msg) { - dout("get_reply revoking msg %p from old con %p\n", - req->r_reply, req->r_con_filling_msg); - ceph_con_revoke_message(req->r_con_filling_msg, req->r_reply); - ceph_con_put(req->r_con_filling_msg); - req->r_con_filling_msg = NULL; - } - - if (front > req->r_reply->front.iov_len) { - pr_warning("get_reply front %d > preallocated %d\n", - front, (int)req->r_reply->front.iov_len); - m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front, GFP_NOFS); - if (!m) - goto out; - ceph_msg_put(req->r_reply); - req->r_reply = m; - } - m = ceph_msg_get(req->r_reply); - - if (data_len > 0) { - unsigned data_off = le16_to_cpu(hdr->data_off); - int want = calc_pages_for(data_off & ~PAGE_MASK, data_len); - - if (unlikely(req->r_num_pages < want)) { - pr_warning("tid %lld reply %d > expected %d pages\n", - tid, want, m->nr_pages); - *skip = 1; - ceph_msg_put(m); - m = NULL; - goto out; - } - m->pages = req->r_pages; - m->nr_pages = req->r_num_pages; -#ifdef CONFIG_BLOCK - m->bio = req->r_bio; -#endif - } - *skip = 0; - req->r_con_filling_msg = ceph_con_get(con); - dout("get_reply tid %lld %p\n", tid, m); - -out: - mutex_unlock(&osdc->request_mutex); - return m; - -} - -static struct ceph_msg *alloc_msg(struct ceph_connection *con, - struct ceph_msg_header *hdr, - int *skip) -{ - struct ceph_osd *osd = con->private; - int type = le16_to_cpu(hdr->type); - int front = le32_to_cpu(hdr->front_len); - - switch (type) { - case CEPH_MSG_OSD_MAP: - return ceph_msg_new(type, front, GFP_NOFS); - case CEPH_MSG_OSD_OPREPLY: - return get_reply(con, hdr, skip); - default: - pr_info("alloc_msg unexpected msg type %d from osd%d\n", type, - osd->o_osd); - *skip = 1; - return NULL; - } -} - -/* - * Wrappers to refcount containing ceph_osd struct - */ -static struct ceph_connection *get_osd_con(struct ceph_connection *con) -{ - struct ceph_osd *osd = con->private; - if (get_osd(osd)) - return con; - return NULL; -} - -static void put_osd_con(struct ceph_connection *con) -{ - struct ceph_osd *osd = con->private; - put_osd(osd); -} - -/* - * authentication - */ -static int get_authorizer(struct ceph_connection *con, - void **buf, int *len, int *proto, - void **reply_buf, int *reply_len, int force_new) -{ - struct ceph_osd *o = con->private; - struct ceph_osd_client *osdc = o->o_osdc; - struct ceph_auth_client *ac = osdc->client->monc.auth; - int ret = 0; - - if (force_new && o->o_authorizer) { - ac->ops->destroy_authorizer(ac, o->o_authorizer); - o->o_authorizer = NULL; - } - if (o->o_authorizer == NULL) { - ret = ac->ops->create_authorizer( - ac, CEPH_ENTITY_TYPE_OSD, - &o->o_authorizer, - &o->o_authorizer_buf, - &o->o_authorizer_buf_len, - &o->o_authorizer_reply_buf, - &o->o_authorizer_reply_buf_len); - if (ret) - return ret; - } - - *proto = ac->protocol; - *buf = o->o_authorizer_buf; - *len = o->o_authorizer_buf_len; - *reply_buf = o->o_authorizer_reply_buf; - *reply_len = o->o_authorizer_reply_buf_len; - return 0; -} - - -static int verify_authorizer_reply(struct ceph_connection *con, int len) -{ - struct ceph_osd *o = con->private; - struct ceph_osd_client *osdc = o->o_osdc; - struct ceph_auth_client *ac = osdc->client->monc.auth; - - return ac->ops->verify_authorizer_reply(ac, o->o_authorizer, len); -} - -static int invalidate_authorizer(struct ceph_connection *con) -{ - struct ceph_osd *o = con->private; - struct ceph_osd_client *osdc = o->o_osdc; - struct ceph_auth_client *ac = osdc->client->monc.auth; - - if (ac->ops->invalidate_authorizer) - ac->ops->invalidate_authorizer(ac, CEPH_ENTITY_TYPE_OSD); - - return ceph_monc_validate_auth(&osdc->client->monc); -} - -static const struct ceph_connection_operations osd_con_ops = { - .get = get_osd_con, - .put = put_osd_con, - .dispatch = dispatch, - .get_authorizer = get_authorizer, - .verify_authorizer_reply = verify_authorizer_reply, - .invalidate_authorizer = invalidate_authorizer, - .alloc_msg = alloc_msg, - .fault = osd_reset, -}; diff --git a/fs/ceph/osd_client.h b/fs/ceph/osd_client.h deleted file mode 100644 index 65c9c560f1a..00000000000 --- a/fs/ceph/osd_client.h +++ /dev/null @@ -1,233 +0,0 @@ -#ifndef _FS_CEPH_OSD_CLIENT_H -#define _FS_CEPH_OSD_CLIENT_H - -#include -#include -#include -#include - -#include "types.h" -#include "osdmap.h" -#include "messenger.h" - -struct ceph_msg; -struct ceph_snap_context; -struct ceph_osd_request; -struct ceph_osd_client; -struct ceph_authorizer; -struct ceph_pagelist; - -/* - * completion callback for async writepages - */ -typedef void (*ceph_osdc_callback_t)(struct ceph_osd_request *, - struct ceph_msg *); - -/* a given osd we're communicating with */ -struct ceph_osd { - atomic_t o_ref; - struct ceph_osd_client *o_osdc; - int o_osd; - int o_incarnation; - struct rb_node o_node; - struct ceph_connection o_con; - struct list_head o_requests; - struct list_head o_osd_lru; - struct ceph_authorizer *o_authorizer; - void *o_authorizer_buf, *o_authorizer_reply_buf; - size_t o_authorizer_buf_len, o_authorizer_reply_buf_len; - unsigned long lru_ttl; - int o_marked_for_keepalive; - struct list_head o_keepalive_item; -}; - -/* an in-flight request */ -struct ceph_osd_request { - u64 r_tid; /* unique for this client */ - struct rb_node r_node; - struct list_head r_req_lru_item; - struct list_head r_osd_item; - struct ceph_osd *r_osd; - struct ceph_pg r_pgid; - int r_pg_osds[CEPH_PG_MAX_SIZE]; - int r_num_pg_osds; - - struct ceph_connection *r_con_filling_msg; - - struct ceph_msg *r_request, *r_reply; - int r_result; - int r_flags; /* any additional flags for the osd */ - u32 r_sent; /* >0 if r_request is sending/sent */ - int r_got_reply; - - struct ceph_osd_client *r_osdc; - struct kref r_kref; - bool r_mempool; - struct completion r_completion, r_safe_completion; - ceph_osdc_callback_t r_callback, r_safe_callback; - struct ceph_eversion r_reassert_version; - struct list_head r_unsafe_item; - - struct inode *r_inode; /* for use by callbacks */ - - char r_oid[40]; /* object name */ - int r_oid_len; - unsigned long r_stamp; /* send OR check time */ - bool r_resend; /* msg send failed, needs retry */ - - struct ceph_file_layout r_file_layout; - struct ceph_snap_context *r_snapc; /* snap context for writes */ - unsigned r_num_pages; /* size of page array (follows) */ - struct page **r_pages; /* pages for data payload */ - int r_pages_from_pool; - int r_own_pages; /* if true, i own page list */ -#ifdef CONFIG_BLOCK - struct bio *r_bio; /* instead of pages */ -#endif - - struct ceph_pagelist *r_trail; /* trailing part of the data */ -}; - -struct ceph_osd_client { - struct ceph_client *client; - - struct ceph_osdmap *osdmap; /* current map */ - struct rw_semaphore map_sem; - struct completion map_waiters; - u64 last_requested_map; - - struct mutex request_mutex; - struct rb_root osds; /* osds */ - struct list_head osd_lru; /* idle osds */ - u64 timeout_tid; /* tid of timeout triggering rq */ - u64 last_tid; /* tid of last request */ - struct rb_root requests; /* pending requests */ - struct list_head req_lru; /* pending requests lru */ - int num_requests; - struct delayed_work timeout_work; - struct delayed_work osds_timeout_work; -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_file; -#endif - - mempool_t *req_mempool; - - struct ceph_msgpool msgpool_op; - struct ceph_msgpool msgpool_op_reply; -}; - -struct ceph_osd_req_op { - u16 op; /* CEPH_OSD_OP_* */ - u32 flags; /* CEPH_OSD_FLAG_* */ - union { - struct { - u64 offset, length; - u64 truncate_size; - u32 truncate_seq; - } extent; - struct { - const char *name; - u32 name_len; - const char *val; - u32 value_len; - __u8 cmp_op; /* CEPH_OSD_CMPXATTR_OP_* */ - __u8 cmp_mode; /* CEPH_OSD_CMPXATTR_MODE_* */ - } xattr; - struct { - const char *class_name; - __u8 class_len; - const char *method_name; - __u8 method_len; - __u8 argc; - const char *indata; - u32 indata_len; - } cls; - struct { - u64 cookie, count; - } pgls; - struct { - u64 snapid; - } snap; - }; - u32 payload_len; -}; - -extern int ceph_osdc_init(struct ceph_osd_client *osdc, - struct ceph_client *client); -extern void ceph_osdc_stop(struct ceph_osd_client *osdc); - -extern void ceph_osdc_handle_reply(struct ceph_osd_client *osdc, - struct ceph_msg *msg); -extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc, - struct ceph_msg *msg); - -extern void ceph_calc_raw_layout(struct ceph_osd_client *osdc, - struct ceph_file_layout *layout, - u64 snapid, - u64 off, u64 *plen, u64 *bno, - struct ceph_osd_request *req, - struct ceph_osd_req_op *op); - -extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, - int flags, - struct ceph_snap_context *snapc, - struct ceph_osd_req_op *ops, - bool use_mempool, - gfp_t gfp_flags, - struct page **pages, - struct bio *bio); - -extern void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 *plen, - struct ceph_osd_req_op *src_ops, - struct ceph_snap_context *snapc, - struct timespec *mtime, - const char *oid, - int oid_len); - -extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, - struct ceph_file_layout *layout, - struct ceph_vino vino, - u64 offset, u64 *len, int op, int flags, - struct ceph_snap_context *snapc, - int do_sync, u32 truncate_seq, - u64 truncate_size, - struct timespec *mtime, - bool use_mempool, int num_reply); - -static inline void ceph_osdc_get_request(struct ceph_osd_request *req) -{ - kref_get(&req->r_kref); -} -extern void ceph_osdc_release_request(struct kref *kref); -static inline void ceph_osdc_put_request(struct ceph_osd_request *req) -{ - kref_put(&req->r_kref, ceph_osdc_release_request); -} - -extern int ceph_osdc_start_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req, - bool nofail); -extern int ceph_osdc_wait_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req); -extern void ceph_osdc_sync(struct ceph_osd_client *osdc); - -extern int ceph_osdc_readpages(struct ceph_osd_client *osdc, - struct ceph_vino vino, - struct ceph_file_layout *layout, - u64 off, u64 *plen, - u32 truncate_seq, u64 truncate_size, - struct page **pages, int nr_pages); - -extern int ceph_osdc_writepages(struct ceph_osd_client *osdc, - struct ceph_vino vino, - struct ceph_file_layout *layout, - struct ceph_snap_context *sc, - u64 off, u64 len, - u32 truncate_seq, u64 truncate_size, - struct timespec *mtime, - struct page **pages, int nr_pages, - int flags, int do_sync, bool nofail); - -#endif - diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c deleted file mode 100644 index 3ccd11761cb..00000000000 --- a/fs/ceph/osdmap.c +++ /dev/null @@ -1,1123 +0,0 @@ - -#include "ceph_debug.h" - -#include -#include - -#include "super.h" -#include "osdmap.h" -#include "crush/hash.h" -#include "crush/mapper.h" -#include "decode.h" - -char *ceph_osdmap_state_str(char *str, int len, int state) -{ - int flag = 0; - - if (!len) - goto done; - - *str = '\0'; - if (state) { - if (state & CEPH_OSD_EXISTS) { - snprintf(str, len, "exists"); - flag = 1; - } - if (state & CEPH_OSD_UP) { - snprintf(str, len, "%s%s%s", str, (flag ? ", " : ""), - "up"); - flag = 1; - } - } else { - snprintf(str, len, "doesn't exist"); - } -done: - return str; -} - -/* maps */ - -static int calc_bits_of(unsigned t) -{ - int b = 0; - while (t) { - t = t >> 1; - b++; - } - return b; -} - -/* - * the foo_mask is the smallest value 2^n-1 that is >= foo. - */ -static void calc_pg_masks(struct ceph_pg_pool_info *pi) -{ - pi->pg_num_mask = (1 << calc_bits_of(le32_to_cpu(pi->v.pg_num)-1)) - 1; - pi->pgp_num_mask = - (1 << calc_bits_of(le32_to_cpu(pi->v.pgp_num)-1)) - 1; - pi->lpg_num_mask = - (1 << calc_bits_of(le32_to_cpu(pi->v.lpg_num)-1)) - 1; - pi->lpgp_num_mask = - (1 << calc_bits_of(le32_to_cpu(pi->v.lpgp_num)-1)) - 1; -} - -/* - * decode crush map - */ -static int crush_decode_uniform_bucket(void **p, void *end, - struct crush_bucket_uniform *b) -{ - dout("crush_decode_uniform_bucket %p to %p\n", *p, end); - ceph_decode_need(p, end, (1+b->h.size) * sizeof(u32), bad); - b->item_weight = ceph_decode_32(p); - return 0; -bad: - return -EINVAL; -} - -static int crush_decode_list_bucket(void **p, void *end, - struct crush_bucket_list *b) -{ - int j; - dout("crush_decode_list_bucket %p to %p\n", *p, end); - b->item_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS); - if (b->item_weights == NULL) - return -ENOMEM; - b->sum_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS); - if (b->sum_weights == NULL) - return -ENOMEM; - ceph_decode_need(p, end, 2 * b->h.size * sizeof(u32), bad); - for (j = 0; j < b->h.size; j++) { - b->item_weights[j] = ceph_decode_32(p); - b->sum_weights[j] = ceph_decode_32(p); - } - return 0; -bad: - return -EINVAL; -} - -static int crush_decode_tree_bucket(void **p, void *end, - struct crush_bucket_tree *b) -{ - int j; - dout("crush_decode_tree_bucket %p to %p\n", *p, end); - ceph_decode_32_safe(p, end, b->num_nodes, bad); - b->node_weights = kcalloc(b->num_nodes, sizeof(u32), GFP_NOFS); - if (b->node_weights == NULL) - return -ENOMEM; - ceph_decode_need(p, end, b->num_nodes * sizeof(u32), bad); - for (j = 0; j < b->num_nodes; j++) - b->node_weights[j] = ceph_decode_32(p); - return 0; -bad: - return -EINVAL; -} - -static int crush_decode_straw_bucket(void **p, void *end, - struct crush_bucket_straw *b) -{ - int j; - dout("crush_decode_straw_bucket %p to %p\n", *p, end); - b->item_weights = kcalloc(b->h.size, sizeof(u32), GFP_NOFS); - if (b->item_weights == NULL) - return -ENOMEM; - b->straws = kcalloc(b->h.size, sizeof(u32), GFP_NOFS); - if (b->straws == NULL) - return -ENOMEM; - ceph_decode_need(p, end, 2 * b->h.size * sizeof(u32), bad); - for (j = 0; j < b->h.size; j++) { - b->item_weights[j] = ceph_decode_32(p); - b->straws[j] = ceph_decode_32(p); - } - return 0; -bad: - return -EINVAL; -} - -static struct crush_map *crush_decode(void *pbyval, void *end) -{ - struct crush_map *c; - int err = -EINVAL; - int i, j; - void **p = &pbyval; - void *start = pbyval; - u32 magic; - - dout("crush_decode %p to %p len %d\n", *p, end, (int)(end - *p)); - - c = kzalloc(sizeof(*c), GFP_NOFS); - if (c == NULL) - return ERR_PTR(-ENOMEM); - - ceph_decode_need(p, end, 4*sizeof(u32), bad); - magic = ceph_decode_32(p); - if (magic != CRUSH_MAGIC) { - pr_err("crush_decode magic %x != current %x\n", - (unsigned)magic, (unsigned)CRUSH_MAGIC); - goto bad; - } - c->max_buckets = ceph_decode_32(p); - c->max_rules = ceph_decode_32(p); - c->max_devices = ceph_decode_32(p); - - c->device_parents = kcalloc(c->max_devices, sizeof(u32), GFP_NOFS); - if (c->device_parents == NULL) - goto badmem; - c->bucket_parents = kcalloc(c->max_buckets, sizeof(u32), GFP_NOFS); - if (c->bucket_parents == NULL) - goto badmem; - - c->buckets = kcalloc(c->max_buckets, sizeof(*c->buckets), GFP_NOFS); - if (c->buckets == NULL) - goto badmem; - c->rules = kcalloc(c->max_rules, sizeof(*c->rules), GFP_NOFS); - if (c->rules == NULL) - goto badmem; - - /* buckets */ - for (i = 0; i < c->max_buckets; i++) { - int size = 0; - u32 alg; - struct crush_bucket *b; - - ceph_decode_32_safe(p, end, alg, bad); - if (alg == 0) { - c->buckets[i] = NULL; - continue; - } - dout("crush_decode bucket %d off %x %p to %p\n", - i, (int)(*p-start), *p, end); - - switch (alg) { - case CRUSH_BUCKET_UNIFORM: - size = sizeof(struct crush_bucket_uniform); - break; - case CRUSH_BUCKET_LIST: - size = sizeof(struct crush_bucket_list); - break; - case CRUSH_BUCKET_TREE: - size = sizeof(struct crush_bucket_tree); - break; - case CRUSH_BUCKET_STRAW: - size = sizeof(struct crush_bucket_straw); - break; - default: - err = -EINVAL; - goto bad; - } - BUG_ON(size == 0); - b = c->buckets[i] = kzalloc(size, GFP_NOFS); - if (b == NULL) - goto badmem; - - ceph_decode_need(p, end, 4*sizeof(u32), bad); - b->id = ceph_decode_32(p); - b->type = ceph_decode_16(p); - b->alg = ceph_decode_8(p); - b->hash = ceph_decode_8(p); - b->weight = ceph_decode_32(p); - b->size = ceph_decode_32(p); - - dout("crush_decode bucket size %d off %x %p to %p\n", - b->size, (int)(*p-start), *p, end); - - b->items = kcalloc(b->size, sizeof(__s32), GFP_NOFS); - if (b->items == NULL) - goto badmem; - b->perm = kcalloc(b->size, sizeof(u32), GFP_NOFS); - if (b->perm == NULL) - goto badmem; - b->perm_n = 0; - - ceph_decode_need(p, end, b->size*sizeof(u32), bad); - for (j = 0; j < b->size; j++) - b->items[j] = ceph_decode_32(p); - - switch (b->alg) { - case CRUSH_BUCKET_UNIFORM: - err = crush_decode_uniform_bucket(p, end, - (struct crush_bucket_uniform *)b); - if (err < 0) - goto bad; - break; - case CRUSH_BUCKET_LIST: - err = crush_decode_list_bucket(p, end, - (struct crush_bucket_list *)b); - if (err < 0) - goto bad; - break; - case CRUSH_BUCKET_TREE: - err = crush_decode_tree_bucket(p, end, - (struct crush_bucket_tree *)b); - if (err < 0) - goto bad; - break; - case CRUSH_BUCKET_STRAW: - err = crush_decode_straw_bucket(p, end, - (struct crush_bucket_straw *)b); - if (err < 0) - goto bad; - break; - } - } - - /* rules */ - dout("rule vec is %p\n", c->rules); - for (i = 0; i < c->max_rules; i++) { - u32 yes; - struct crush_rule *r; - - ceph_decode_32_safe(p, end, yes, bad); - if (!yes) { - dout("crush_decode NO rule %d off %x %p to %p\n", - i, (int)(*p-start), *p, end); - c->rules[i] = NULL; - continue; - } - - dout("crush_decode rule %d off %x %p to %p\n", - i, (int)(*p-start), *p, end); - - /* len */ - ceph_decode_32_safe(p, end, yes, bad); -#if BITS_PER_LONG == 32 - err = -EINVAL; - if (yes > ULONG_MAX / sizeof(struct crush_rule_step)) - goto bad; -#endif - r = c->rules[i] = kmalloc(sizeof(*r) + - yes*sizeof(struct crush_rule_step), - GFP_NOFS); - if (r == NULL) - goto badmem; - dout(" rule %d is at %p\n", i, r); - r->len = yes; - ceph_decode_copy_safe(p, end, &r->mask, 4, bad); /* 4 u8's */ - ceph_decode_need(p, end, r->len*3*sizeof(u32), bad); - for (j = 0; j < r->len; j++) { - r->steps[j].op = ceph_decode_32(p); - r->steps[j].arg1 = ceph_decode_32(p); - r->steps[j].arg2 = ceph_decode_32(p); - } - } - - /* ignore trailing name maps. */ - - dout("crush_decode success\n"); - return c; - -badmem: - err = -ENOMEM; -bad: - dout("crush_decode fail %d\n", err); - crush_destroy(c); - return ERR_PTR(err); -} - -/* - * rbtree of pg_mapping for handling pg_temp (explicit mapping of pgid - * to a set of osds) - */ -static int pgid_cmp(struct ceph_pg l, struct ceph_pg r) -{ - u64 a = *(u64 *)&l; - u64 b = *(u64 *)&r; - - if (a < b) - return -1; - if (a > b) - return 1; - return 0; -} - -static int __insert_pg_mapping(struct ceph_pg_mapping *new, - struct rb_root *root) -{ - struct rb_node **p = &root->rb_node; - struct rb_node *parent = NULL; - struct ceph_pg_mapping *pg = NULL; - int c; - - while (*p) { - parent = *p; - pg = rb_entry(parent, struct ceph_pg_mapping, node); - c = pgid_cmp(new->pgid, pg->pgid); - if (c < 0) - p = &(*p)->rb_left; - else if (c > 0) - p = &(*p)->rb_right; - else - return -EEXIST; - } - - rb_link_node(&new->node, parent, p); - rb_insert_color(&new->node, root); - return 0; -} - -static struct ceph_pg_mapping *__lookup_pg_mapping(struct rb_root *root, - struct ceph_pg pgid) -{ - struct rb_node *n = root->rb_node; - struct ceph_pg_mapping *pg; - int c; - - while (n) { - pg = rb_entry(n, struct ceph_pg_mapping, node); - c = pgid_cmp(pgid, pg->pgid); - if (c < 0) - n = n->rb_left; - else if (c > 0) - n = n->rb_right; - else - return pg; - } - return NULL; -} - -/* - * rbtree of pg pool info - */ -static int __insert_pg_pool(struct rb_root *root, struct ceph_pg_pool_info *new) -{ - struct rb_node **p = &root->rb_node; - struct rb_node *parent = NULL; - struct ceph_pg_pool_info *pi = NULL; - - while (*p) { - parent = *p; - pi = rb_entry(parent, struct ceph_pg_pool_info, node); - if (new->id < pi->id) - p = &(*p)->rb_left; - else if (new->id > pi->id) - p = &(*p)->rb_right; - else - return -EEXIST; - } - - rb_link_node(&new->node, parent, p); - rb_insert_color(&new->node, root); - return 0; -} - -static struct ceph_pg_pool_info *__lookup_pg_pool(struct rb_root *root, int id) -{ - struct ceph_pg_pool_info *pi; - struct rb_node *n = root->rb_node; - - while (n) { - pi = rb_entry(n, struct ceph_pg_pool_info, node); - if (id < pi->id) - n = n->rb_left; - else if (id > pi->id) - n = n->rb_right; - else - return pi; - } - return NULL; -} - -int ceph_pg_poolid_by_name(struct ceph_osdmap *map, const char *name) -{ - struct rb_node *rbp; - - for (rbp = rb_first(&map->pg_pools); rbp; rbp = rb_next(rbp)) { - struct ceph_pg_pool_info *pi = - rb_entry(rbp, struct ceph_pg_pool_info, node); - if (pi->name && strcmp(pi->name, name) == 0) - return pi->id; - } - return -ENOENT; -} - -static void __remove_pg_pool(struct rb_root *root, struct ceph_pg_pool_info *pi) -{ - rb_erase(&pi->node, root); - kfree(pi->name); - kfree(pi); -} - -static int __decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi) -{ - unsigned n, m; - - ceph_decode_copy(p, &pi->v, sizeof(pi->v)); - calc_pg_masks(pi); - - /* num_snaps * snap_info_t */ - n = le32_to_cpu(pi->v.num_snaps); - while (n--) { - ceph_decode_need(p, end, sizeof(u64) + 1 + sizeof(u64) + - sizeof(struct ceph_timespec), bad); - *p += sizeof(u64) + /* key */ - 1 + sizeof(u64) + /* u8, snapid */ - sizeof(struct ceph_timespec); - m = ceph_decode_32(p); /* snap name */ - *p += m; - } - - *p += le32_to_cpu(pi->v.num_removed_snap_intervals) * sizeof(u64) * 2; - return 0; - -bad: - return -EINVAL; -} - -static int __decode_pool_names(void **p, void *end, struct ceph_osdmap *map) -{ - struct ceph_pg_pool_info *pi; - u32 num, len, pool; - - ceph_decode_32_safe(p, end, num, bad); - dout(" %d pool names\n", num); - while (num--) { - ceph_decode_32_safe(p, end, pool, bad); - ceph_decode_32_safe(p, end, len, bad); - dout(" pool %d len %d\n", pool, len); - pi = __lookup_pg_pool(&map->pg_pools, pool); - if (pi) { - kfree(pi->name); - pi->name = kmalloc(len + 1, GFP_NOFS); - if (pi->name) { - memcpy(pi->name, *p, len); - pi->name[len] = '\0'; - dout(" name is %s\n", pi->name); - } - } - *p += len; - } - return 0; - -bad: - return -EINVAL; -} - -/* - * osd map - */ -void ceph_osdmap_destroy(struct ceph_osdmap *map) -{ - dout("osdmap_destroy %p\n", map); - if (map->crush) - crush_destroy(map->crush); - while (!RB_EMPTY_ROOT(&map->pg_temp)) { - struct ceph_pg_mapping *pg = - rb_entry(rb_first(&map->pg_temp), - struct ceph_pg_mapping, node); - rb_erase(&pg->node, &map->pg_temp); - kfree(pg); - } - while (!RB_EMPTY_ROOT(&map->pg_pools)) { - struct ceph_pg_pool_info *pi = - rb_entry(rb_first(&map->pg_pools), - struct ceph_pg_pool_info, node); - __remove_pg_pool(&map->pg_pools, pi); - } - kfree(map->osd_state); - kfree(map->osd_weight); - kfree(map->osd_addr); - kfree(map); -} - -/* - * adjust max osd value. reallocate arrays. - */ -static int osdmap_set_max_osd(struct ceph_osdmap *map, int max) -{ - u8 *state; - struct ceph_entity_addr *addr; - u32 *weight; - - state = kcalloc(max, sizeof(*state), GFP_NOFS); - addr = kcalloc(max, sizeof(*addr), GFP_NOFS); - weight = kcalloc(max, sizeof(*weight), GFP_NOFS); - if (state == NULL || addr == NULL || weight == NULL) { - kfree(state); - kfree(addr); - kfree(weight); - return -ENOMEM; - } - - /* copy old? */ - if (map->osd_state) { - memcpy(state, map->osd_state, map->max_osd*sizeof(*state)); - memcpy(addr, map->osd_addr, map->max_osd*sizeof(*addr)); - memcpy(weight, map->osd_weight, map->max_osd*sizeof(*weight)); - kfree(map->osd_state); - kfree(map->osd_addr); - kfree(map->osd_weight); - } - - map->osd_state = state; - map->osd_weight = weight; - map->osd_addr = addr; - map->max_osd = max; - return 0; -} - -/* - * decode a full map. - */ -struct ceph_osdmap *osdmap_decode(void **p, void *end) -{ - struct ceph_osdmap *map; - u16 version; - u32 len, max, i; - u8 ev; - int err = -EINVAL; - void *start = *p; - struct ceph_pg_pool_info *pi; - - dout("osdmap_decode %p to %p len %d\n", *p, end, (int)(end - *p)); - - map = kzalloc(sizeof(*map), GFP_NOFS); - if (map == NULL) - return ERR_PTR(-ENOMEM); - map->pg_temp = RB_ROOT; - - ceph_decode_16_safe(p, end, version, bad); - if (version > CEPH_OSDMAP_VERSION) { - pr_warning("got unknown v %d > %d of osdmap\n", version, - CEPH_OSDMAP_VERSION); - goto bad; - } - - ceph_decode_need(p, end, 2*sizeof(u64)+6*sizeof(u32), bad); - ceph_decode_copy(p, &map->fsid, sizeof(map->fsid)); - map->epoch = ceph_decode_32(p); - ceph_decode_copy(p, &map->created, sizeof(map->created)); - ceph_decode_copy(p, &map->modified, sizeof(map->modified)); - - ceph_decode_32_safe(p, end, max, bad); - while (max--) { - ceph_decode_need(p, end, 4 + 1 + sizeof(pi->v), bad); - pi = kzalloc(sizeof(*pi), GFP_NOFS); - if (!pi) - goto bad; - pi->id = ceph_decode_32(p); - ev = ceph_decode_8(p); /* encoding version */ - if (ev > CEPH_PG_POOL_VERSION) { - pr_warning("got unknown v %d > %d of ceph_pg_pool\n", - ev, CEPH_PG_POOL_VERSION); - kfree(pi); - goto bad; - } - err = __decode_pool(p, end, pi); - if (err < 0) - goto bad; - __insert_pg_pool(&map->pg_pools, pi); - } - - if (version >= 5 && __decode_pool_names(p, end, map) < 0) - goto bad; - - ceph_decode_32_safe(p, end, map->pool_max, bad); - - ceph_decode_32_safe(p, end, map->flags, bad); - - max = ceph_decode_32(p); - - /* (re)alloc osd arrays */ - err = osdmap_set_max_osd(map, max); - if (err < 0) - goto bad; - dout("osdmap_decode max_osd = %d\n", map->max_osd); - - /* osds */ - err = -EINVAL; - ceph_decode_need(p, end, 3*sizeof(u32) + - map->max_osd*(1 + sizeof(*map->osd_weight) + - sizeof(*map->osd_addr)), bad); - *p += 4; /* skip length field (should match max) */ - ceph_decode_copy(p, map->osd_state, map->max_osd); - - *p += 4; /* skip length field (should match max) */ - for (i = 0; i < map->max_osd; i++) - map->osd_weight[i] = ceph_decode_32(p); - - *p += 4; /* skip length field (should match max) */ - ceph_decode_copy(p, map->osd_addr, map->max_osd*sizeof(*map->osd_addr)); - for (i = 0; i < map->max_osd; i++) - ceph_decode_addr(&map->osd_addr[i]); - - /* pg_temp */ - ceph_decode_32_safe(p, end, len, bad); - for (i = 0; i < len; i++) { - int n, j; - struct ceph_pg pgid; - struct ceph_pg_mapping *pg; - - ceph_decode_need(p, end, sizeof(u32) + sizeof(u64), bad); - ceph_decode_copy(p, &pgid, sizeof(pgid)); - n = ceph_decode_32(p); - ceph_decode_need(p, end, n * sizeof(u32), bad); - err = -ENOMEM; - pg = kmalloc(sizeof(*pg) + n*sizeof(u32), GFP_NOFS); - if (!pg) - goto bad; - pg->pgid = pgid; - pg->len = n; - for (j = 0; j < n; j++) - pg->osds[j] = ceph_decode_32(p); - - err = __insert_pg_mapping(pg, &map->pg_temp); - if (err) - goto bad; - dout(" added pg_temp %llx len %d\n", *(u64 *)&pgid, len); - } - - /* crush */ - ceph_decode_32_safe(p, end, len, bad); - dout("osdmap_decode crush len %d from off 0x%x\n", len, - (int)(*p - start)); - ceph_decode_need(p, end, len, bad); - map->crush = crush_decode(*p, end); - *p += len; - if (IS_ERR(map->crush)) { - err = PTR_ERR(map->crush); - map->crush = NULL; - goto bad; - } - - /* ignore the rest of the map */ - *p = end; - - dout("osdmap_decode done %p %p\n", *p, end); - return map; - -bad: - dout("osdmap_decode fail\n"); - ceph_osdmap_destroy(map); - return ERR_PTR(err); -} - -/* - * decode and apply an incremental map update. - */ -struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, - struct ceph_osdmap *map, - struct ceph_messenger *msgr) -{ - struct crush_map *newcrush = NULL; - struct ceph_fsid fsid; - u32 epoch = 0; - struct ceph_timespec modified; - u32 len, pool; - __s32 new_pool_max, new_flags, max; - void *start = *p; - int err = -EINVAL; - u16 version; - struct rb_node *rbp; - - ceph_decode_16_safe(p, end, version, bad); - if (version > CEPH_OSDMAP_INC_VERSION) { - pr_warning("got unknown v %d > %d of inc osdmap\n", version, - CEPH_OSDMAP_INC_VERSION); - goto bad; - } - - ceph_decode_need(p, end, sizeof(fsid)+sizeof(modified)+2*sizeof(u32), - bad); - ceph_decode_copy(p, &fsid, sizeof(fsid)); - epoch = ceph_decode_32(p); - BUG_ON(epoch != map->epoch+1); - ceph_decode_copy(p, &modified, sizeof(modified)); - new_pool_max = ceph_decode_32(p); - new_flags = ceph_decode_32(p); - - /* full map? */ - ceph_decode_32_safe(p, end, len, bad); - if (len > 0) { - dout("apply_incremental full map len %d, %p to %p\n", - len, *p, end); - return osdmap_decode(p, min(*p+len, end)); - } - - /* new crush? */ - ceph_decode_32_safe(p, end, len, bad); - if (len > 0) { - dout("apply_incremental new crush map len %d, %p to %p\n", - len, *p, end); - newcrush = crush_decode(*p, min(*p+len, end)); - if (IS_ERR(newcrush)) - return ERR_CAST(newcrush); - *p += len; - } - - /* new flags? */ - if (new_flags >= 0) - map->flags = new_flags; - if (new_pool_max >= 0) - map->pool_max = new_pool_max; - - ceph_decode_need(p, end, 5*sizeof(u32), bad); - - /* new max? */ - max = ceph_decode_32(p); - if (max >= 0) { - err = osdmap_set_max_osd(map, max); - if (err < 0) - goto bad; - } - - map->epoch++; - map->modified = map->modified; - if (newcrush) { - if (map->crush) - crush_destroy(map->crush); - map->crush = newcrush; - newcrush = NULL; - } - - /* new_pool */ - ceph_decode_32_safe(p, end, len, bad); - while (len--) { - __u8 ev; - struct ceph_pg_pool_info *pi; - - ceph_decode_32_safe(p, end, pool, bad); - ceph_decode_need(p, end, 1 + sizeof(pi->v), bad); - ev = ceph_decode_8(p); /* encoding version */ - if (ev > CEPH_PG_POOL_VERSION) { - pr_warning("got unknown v %d > %d of ceph_pg_pool\n", - ev, CEPH_PG_POOL_VERSION); - goto bad; - } - pi = __lookup_pg_pool(&map->pg_pools, pool); - if (!pi) { - pi = kzalloc(sizeof(*pi), GFP_NOFS); - if (!pi) { - err = -ENOMEM; - goto bad; - } - pi->id = pool; - __insert_pg_pool(&map->pg_pools, pi); - } - err = __decode_pool(p, end, pi); - if (err < 0) - goto bad; - } - if (version >= 5 && __decode_pool_names(p, end, map) < 0) - goto bad; - - /* old_pool */ - ceph_decode_32_safe(p, end, len, bad); - while (len--) { - struct ceph_pg_pool_info *pi; - - ceph_decode_32_safe(p, end, pool, bad); - pi = __lookup_pg_pool(&map->pg_pools, pool); - if (pi) - __remove_pg_pool(&map->pg_pools, pi); - } - - /* new_up */ - err = -EINVAL; - ceph_decode_32_safe(p, end, len, bad); - while (len--) { - u32 osd; - struct ceph_entity_addr addr; - ceph_decode_32_safe(p, end, osd, bad); - ceph_decode_copy_safe(p, end, &addr, sizeof(addr), bad); - ceph_decode_addr(&addr); - pr_info("osd%d up\n", osd); - BUG_ON(osd >= map->max_osd); - map->osd_state[osd] |= CEPH_OSD_UP; - map->osd_addr[osd] = addr; - } - - /* new_down */ - ceph_decode_32_safe(p, end, len, bad); - while (len--) { - u32 osd; - ceph_decode_32_safe(p, end, osd, bad); - (*p)++; /* clean flag */ - pr_info("osd%d down\n", osd); - if (osd < map->max_osd) - map->osd_state[osd] &= ~CEPH_OSD_UP; - } - - /* new_weight */ - ceph_decode_32_safe(p, end, len, bad); - while (len--) { - u32 osd, off; - ceph_decode_need(p, end, sizeof(u32)*2, bad); - osd = ceph_decode_32(p); - off = ceph_decode_32(p); - pr_info("osd%d weight 0x%x %s\n", osd, off, - off == CEPH_OSD_IN ? "(in)" : - (off == CEPH_OSD_OUT ? "(out)" : "")); - if (osd < map->max_osd) - map->osd_weight[osd] = off; - } - - /* new_pg_temp */ - rbp = rb_first(&map->pg_temp); - ceph_decode_32_safe(p, end, len, bad); - while (len--) { - struct ceph_pg_mapping *pg; - int j; - struct ceph_pg pgid; - u32 pglen; - ceph_decode_need(p, end, sizeof(u64) + sizeof(u32), bad); - ceph_decode_copy(p, &pgid, sizeof(pgid)); - pglen = ceph_decode_32(p); - - /* remove any? */ - while (rbp && pgid_cmp(rb_entry(rbp, struct ceph_pg_mapping, - node)->pgid, pgid) <= 0) { - struct ceph_pg_mapping *cur = - rb_entry(rbp, struct ceph_pg_mapping, node); - - rbp = rb_next(rbp); - dout(" removed pg_temp %llx\n", *(u64 *)&cur->pgid); - rb_erase(&cur->node, &map->pg_temp); - kfree(cur); - } - - if (pglen) { - /* insert */ - ceph_decode_need(p, end, pglen*sizeof(u32), bad); - pg = kmalloc(sizeof(*pg) + sizeof(u32)*pglen, GFP_NOFS); - if (!pg) { - err = -ENOMEM; - goto bad; - } - pg->pgid = pgid; - pg->len = pglen; - for (j = 0; j < pglen; j++) - pg->osds[j] = ceph_decode_32(p); - err = __insert_pg_mapping(pg, &map->pg_temp); - if (err) { - kfree(pg); - goto bad; - } - dout(" added pg_temp %llx len %d\n", *(u64 *)&pgid, - pglen); - } - } - while (rbp) { - struct ceph_pg_mapping *cur = - rb_entry(rbp, struct ceph_pg_mapping, node); - - rbp = rb_next(rbp); - dout(" removed pg_temp %llx\n", *(u64 *)&cur->pgid); - rb_erase(&cur->node, &map->pg_temp); - kfree(cur); - } - - /* ignore the rest */ - *p = end; - return map; - -bad: - pr_err("corrupt inc osdmap epoch %d off %d (%p of %p-%p)\n", - epoch, (int)(*p - start), *p, start, end); - print_hex_dump(KERN_DEBUG, "osdmap: ", - DUMP_PREFIX_OFFSET, 16, 1, - start, end - start, true); - if (newcrush) - crush_destroy(newcrush); - return ERR_PTR(err); -} - - - - -/* - * calculate file layout from given offset, length. - * fill in correct oid, logical length, and object extent - * offset, length. - * - * for now, we write only a single su, until we can - * pass a stride back to the caller. - */ -void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, - u64 off, u64 *plen, - u64 *ono, - u64 *oxoff, u64 *oxlen) -{ - u32 osize = le32_to_cpu(layout->fl_object_size); - u32 su = le32_to_cpu(layout->fl_stripe_unit); - u32 sc = le32_to_cpu(layout->fl_stripe_count); - u32 bl, stripeno, stripepos, objsetno; - u32 su_per_object; - u64 t, su_offset; - - dout("mapping %llu~%llu osize %u fl_su %u\n", off, *plen, - osize, su); - su_per_object = osize / su; - dout("osize %u / su %u = su_per_object %u\n", osize, su, - su_per_object); - - BUG_ON((su & ~PAGE_MASK) != 0); - /* bl = *off / su; */ - t = off; - do_div(t, su); - bl = t; - dout("off %llu / su %u = bl %u\n", off, su, bl); - - stripeno = bl / sc; - stripepos = bl % sc; - objsetno = stripeno / su_per_object; - - *ono = objsetno * sc + stripepos; - dout("objset %u * sc %u = ono %u\n", objsetno, sc, (unsigned)*ono); - - /* *oxoff = *off % layout->fl_stripe_unit; # offset in su */ - t = off; - su_offset = do_div(t, su); - *oxoff = su_offset + (stripeno % su_per_object) * su; - - /* - * Calculate the length of the extent being written to the selected - * object. This is the minimum of the full length requested (plen) or - * the remainder of the current stripe being written to. - */ - *oxlen = min_t(u64, *plen, su - su_offset); - *plen = *oxlen; - - dout(" obj extent %llu~%llu\n", *oxoff, *oxlen); -} - -/* - * calculate an object layout (i.e. pgid) from an oid, - * file_layout, and osdmap - */ -int ceph_calc_object_layout(struct ceph_object_layout *ol, - const char *oid, - struct ceph_file_layout *fl, - struct ceph_osdmap *osdmap) -{ - unsigned num, num_mask; - struct ceph_pg pgid; - s32 preferred = (s32)le32_to_cpu(fl->fl_pg_preferred); - int poolid = le32_to_cpu(fl->fl_pg_pool); - struct ceph_pg_pool_info *pool; - unsigned ps; - - BUG_ON(!osdmap); - - pool = __lookup_pg_pool(&osdmap->pg_pools, poolid); - if (!pool) - return -EIO; - ps = ceph_str_hash(pool->v.object_hash, oid, strlen(oid)); - if (preferred >= 0) { - ps += preferred; - num = le32_to_cpu(pool->v.lpg_num); - num_mask = pool->lpg_num_mask; - } else { - num = le32_to_cpu(pool->v.pg_num); - num_mask = pool->pg_num_mask; - } - - pgid.ps = cpu_to_le16(ps); - pgid.preferred = cpu_to_le16(preferred); - pgid.pool = fl->fl_pg_pool; - if (preferred >= 0) - dout("calc_object_layout '%s' pgid %d.%xp%d\n", oid, poolid, ps, - (int)preferred); - else - dout("calc_object_layout '%s' pgid %d.%x\n", oid, poolid, ps); - - ol->ol_pgid = pgid; - ol->ol_stripe_unit = fl->fl_object_stripe_unit; - return 0; -} - -/* - * Calculate raw osd vector for the given pgid. Return pointer to osd - * array, or NULL on failure. - */ -static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid, - int *osds, int *num) -{ - struct ceph_pg_mapping *pg; - struct ceph_pg_pool_info *pool; - int ruleno; - unsigned poolid, ps, pps; - int preferred; - - /* pg_temp? */ - pg = __lookup_pg_mapping(&osdmap->pg_temp, pgid); - if (pg) { - *num = pg->len; - return pg->osds; - } - - /* crush */ - poolid = le32_to_cpu(pgid.pool); - ps = le16_to_cpu(pgid.ps); - preferred = (s16)le16_to_cpu(pgid.preferred); - - /* don't forcefeed bad device ids to crush */ - if (preferred >= osdmap->max_osd || - preferred >= osdmap->crush->max_devices) - preferred = -1; - - pool = __lookup_pg_pool(&osdmap->pg_pools, poolid); - if (!pool) - return NULL; - ruleno = crush_find_rule(osdmap->crush, pool->v.crush_ruleset, - pool->v.type, pool->v.size); - if (ruleno < 0) { - pr_err("no crush rule pool %d ruleset %d type %d size %d\n", - poolid, pool->v.crush_ruleset, pool->v.type, - pool->v.size); - return NULL; - } - - if (preferred >= 0) - pps = ceph_stable_mod(ps, - le32_to_cpu(pool->v.lpgp_num), - pool->lpgp_num_mask); - else - pps = ceph_stable_mod(ps, - le32_to_cpu(pool->v.pgp_num), - pool->pgp_num_mask); - pps += poolid; - *num = crush_do_rule(osdmap->crush, ruleno, pps, osds, - min_t(int, pool->v.size, *num), - preferred, osdmap->osd_weight); - return osds; -} - -/* - * Return acting set for given pgid. - */ -int ceph_calc_pg_acting(struct ceph_osdmap *osdmap, struct ceph_pg pgid, - int *acting) -{ - int rawosds[CEPH_PG_MAX_SIZE], *osds; - int i, o, num = CEPH_PG_MAX_SIZE; - - osds = calc_pg_raw(osdmap, pgid, rawosds, &num); - if (!osds) - return -1; - - /* primary is first up osd */ - o = 0; - for (i = 0; i < num; i++) - if (ceph_osd_is_up(osdmap, osds[i])) - acting[o++] = osds[i]; - return o; -} - -/* - * Return primary osd for given pgid, or -1 if none. - */ -int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, struct ceph_pg pgid) -{ - int rawosds[CEPH_PG_MAX_SIZE], *osds; - int i, num = CEPH_PG_MAX_SIZE; - - osds = calc_pg_raw(osdmap, pgid, rawosds, &num); - if (!osds) - return -1; - - /* primary is first up osd */ - for (i = 0; i < num; i++) - if (ceph_osd_is_up(osdmap, osds[i])) - return osds[i]; - return -1; -} diff --git a/fs/ceph/osdmap.h b/fs/ceph/osdmap.h deleted file mode 100644 index a592b211be3..00000000000 --- a/fs/ceph/osdmap.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef _FS_CEPH_OSDMAP_H -#define _FS_CEPH_OSDMAP_H - -#include -#include "types.h" -#include "ceph_fs.h" -#include "crush/crush.h" - -/* - * The osd map describes the current membership of the osd cluster and - * specifies the mapping of objects to placement groups and placement - * groups to (sets of) osds. That is, it completely specifies the - * (desired) distribution of all data objects in the system at some - * point in time. - * - * Each map version is identified by an epoch, which increases monotonically. - * - * The map can be updated either via an incremental map (diff) describing - * the change between two successive epochs, or as a fully encoded map. - */ -struct ceph_pg_pool_info { - struct rb_node node; - int id; - struct ceph_pg_pool v; - int pg_num_mask, pgp_num_mask, lpg_num_mask, lpgp_num_mask; - char *name; -}; - -struct ceph_pg_mapping { - struct rb_node node; - struct ceph_pg pgid; - int len; - int osds[]; -}; - -struct ceph_osdmap { - struct ceph_fsid fsid; - u32 epoch; - u32 mkfs_epoch; - struct ceph_timespec created, modified; - - u32 flags; /* CEPH_OSDMAP_* */ - - u32 max_osd; /* size of osd_state, _offload, _addr arrays */ - u8 *osd_state; /* CEPH_OSD_* */ - u32 *osd_weight; /* 0 = failed, 0x10000 = 100% normal */ - struct ceph_entity_addr *osd_addr; - - struct rb_root pg_temp; - struct rb_root pg_pools; - u32 pool_max; - - /* the CRUSH map specifies the mapping of placement groups to - * the list of osds that store+replicate them. */ - struct crush_map *crush; -}; - -/* - * file layout helpers - */ -#define ceph_file_layout_su(l) ((__s32)le32_to_cpu((l).fl_stripe_unit)) -#define ceph_file_layout_stripe_count(l) \ - ((__s32)le32_to_cpu((l).fl_stripe_count)) -#define ceph_file_layout_object_size(l) ((__s32)le32_to_cpu((l).fl_object_size)) -#define ceph_file_layout_cas_hash(l) ((__s32)le32_to_cpu((l).fl_cas_hash)) -#define ceph_file_layout_object_su(l) \ - ((__s32)le32_to_cpu((l).fl_object_stripe_unit)) -#define ceph_file_layout_pg_preferred(l) \ - ((__s32)le32_to_cpu((l).fl_pg_preferred)) -#define ceph_file_layout_pg_pool(l) \ - ((__s32)le32_to_cpu((l).fl_pg_pool)) - -static inline unsigned ceph_file_layout_stripe_width(struct ceph_file_layout *l) -{ - return le32_to_cpu(l->fl_stripe_unit) * - le32_to_cpu(l->fl_stripe_count); -} - -/* "period" == bytes before i start on a new set of objects */ -static inline unsigned ceph_file_layout_period(struct ceph_file_layout *l) -{ - return le32_to_cpu(l->fl_object_size) * - le32_to_cpu(l->fl_stripe_count); -} - - -static inline int ceph_osd_is_up(struct ceph_osdmap *map, int osd) -{ - return (osd < map->max_osd) && (map->osd_state[osd] & CEPH_OSD_UP); -} - -static inline bool ceph_osdmap_flag(struct ceph_osdmap *map, int flag) -{ - return map && (map->flags & flag); -} - -extern char *ceph_osdmap_state_str(char *str, int len, int state); - -static inline struct ceph_entity_addr *ceph_osd_addr(struct ceph_osdmap *map, - int osd) -{ - if (osd >= map->max_osd) - return NULL; - return &map->osd_addr[osd]; -} - -extern struct ceph_osdmap *osdmap_decode(void **p, void *end); -extern struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, - struct ceph_osdmap *map, - struct ceph_messenger *msgr); -extern void ceph_osdmap_destroy(struct ceph_osdmap *map); - -/* calculate mapping of a file extent to an object */ -extern void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, - u64 off, u64 *plen, - u64 *bno, u64 *oxoff, u64 *oxlen); - -/* calculate mapping of object to a placement group */ -extern int ceph_calc_object_layout(struct ceph_object_layout *ol, - const char *oid, - struct ceph_file_layout *fl, - struct ceph_osdmap *osdmap); -extern int ceph_calc_pg_acting(struct ceph_osdmap *osdmap, struct ceph_pg pgid, - int *acting); -extern int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, - struct ceph_pg pgid); - -extern int ceph_pg_poolid_by_name(struct ceph_osdmap *map, const char *name); - -#endif diff --git a/fs/ceph/pagelist.c b/fs/ceph/pagelist.c deleted file mode 100644 index 326e1c04176..00000000000 --- a/fs/ceph/pagelist.c +++ /dev/null @@ -1,63 +0,0 @@ - -#include -#include -#include - -#include "pagelist.h" - -static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl) -{ - struct page *page = list_entry(pl->head.prev, struct page, - lru); - kunmap(page); -} - -int ceph_pagelist_release(struct ceph_pagelist *pl) -{ - if (pl->mapped_tail) - ceph_pagelist_unmap_tail(pl); - - while (!list_empty(&pl->head)) { - struct page *page = list_first_entry(&pl->head, struct page, - lru); - list_del(&page->lru); - __free_page(page); - } - return 0; -} - -static int ceph_pagelist_addpage(struct ceph_pagelist *pl) -{ - struct page *page = __page_cache_alloc(GFP_NOFS); - if (!page) - return -ENOMEM; - pl->room += PAGE_SIZE; - list_add_tail(&page->lru, &pl->head); - if (pl->mapped_tail) - ceph_pagelist_unmap_tail(pl); - pl->mapped_tail = kmap(page); - return 0; -} - -int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len) -{ - while (pl->room < len) { - size_t bit = pl->room; - int ret; - - memcpy(pl->mapped_tail + (pl->length & ~PAGE_CACHE_MASK), - buf, bit); - pl->length += bit; - pl->room -= bit; - buf += bit; - len -= bit; - ret = ceph_pagelist_addpage(pl); - if (ret) - return ret; - } - - memcpy(pl->mapped_tail + (pl->length & ~PAGE_CACHE_MASK), buf, len); - pl->length += len; - pl->room -= len; - return 0; -} diff --git a/fs/ceph/pagelist.h b/fs/ceph/pagelist.h deleted file mode 100644 index cc9327aa1c9..00000000000 --- a/fs/ceph/pagelist.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef __FS_CEPH_PAGELIST_H -#define __FS_CEPH_PAGELIST_H - -#include - -struct ceph_pagelist { - struct list_head head; - void *mapped_tail; - size_t length; - size_t room; -}; - -static inline void ceph_pagelist_init(struct ceph_pagelist *pl) -{ - INIT_LIST_HEAD(&pl->head); - pl->mapped_tail = NULL; - pl->length = 0; - pl->room = 0; -} -extern int ceph_pagelist_release(struct ceph_pagelist *pl); - -extern int ceph_pagelist_append(struct ceph_pagelist *pl, const void *d, size_t l); - -static inline int ceph_pagelist_encode_64(struct ceph_pagelist *pl, u64 v) -{ - __le64 ev = cpu_to_le64(v); - return ceph_pagelist_append(pl, &ev, sizeof(ev)); -} -static inline int ceph_pagelist_encode_32(struct ceph_pagelist *pl, u32 v) -{ - __le32 ev = cpu_to_le32(v); - return ceph_pagelist_append(pl, &ev, sizeof(ev)); -} -static inline int ceph_pagelist_encode_16(struct ceph_pagelist *pl, u16 v) -{ - __le16 ev = cpu_to_le16(v); - return ceph_pagelist_append(pl, &ev, sizeof(ev)); -} -static inline int ceph_pagelist_encode_8(struct ceph_pagelist *pl, u8 v) -{ - return ceph_pagelist_append(pl, &v, 1); -} -static inline int ceph_pagelist_encode_string(struct ceph_pagelist *pl, - char *s, size_t len) -{ - int ret = ceph_pagelist_encode_32(pl, len); - if (ret) - return ret; - if (len) - return ceph_pagelist_append(pl, s, len); - return 0; -} - -#endif diff --git a/fs/ceph/rados.h b/fs/ceph/rados.h deleted file mode 100644 index 6d5247f2e81..00000000000 --- a/fs/ceph/rados.h +++ /dev/null @@ -1,405 +0,0 @@ -#ifndef CEPH_RADOS_H -#define CEPH_RADOS_H - -/* - * Data types for the Ceph distributed object storage layer RADOS - * (Reliable Autonomic Distributed Object Store). - */ - -#include "msgr.h" - -/* - * osdmap encoding versions - */ -#define CEPH_OSDMAP_INC_VERSION 5 -#define CEPH_OSDMAP_INC_VERSION_EXT 5 -#define CEPH_OSDMAP_VERSION 5 -#define CEPH_OSDMAP_VERSION_EXT 5 - -/* - * fs id - */ -struct ceph_fsid { - unsigned char fsid[16]; -}; - -static inline int ceph_fsid_compare(const struct ceph_fsid *a, - const struct ceph_fsid *b) -{ - return memcmp(a, b, sizeof(*a)); -} - -/* - * ino, object, etc. - */ -typedef __le64 ceph_snapid_t; -#define CEPH_SNAPDIR ((__u64)(-1)) /* reserved for hidden .snap dir */ -#define CEPH_NOSNAP ((__u64)(-2)) /* "head", "live" revision */ -#define CEPH_MAXSNAP ((__u64)(-3)) /* largest valid snapid */ - -struct ceph_timespec { - __le32 tv_sec; - __le32 tv_nsec; -} __attribute__ ((packed)); - - -/* - * object layout - how objects are mapped into PGs - */ -#define CEPH_OBJECT_LAYOUT_HASH 1 -#define CEPH_OBJECT_LAYOUT_LINEAR 2 -#define CEPH_OBJECT_LAYOUT_HASHINO 3 - -/* - * pg layout -- how PGs are mapped onto (sets of) OSDs - */ -#define CEPH_PG_LAYOUT_CRUSH 0 -#define CEPH_PG_LAYOUT_HASH 1 -#define CEPH_PG_LAYOUT_LINEAR 2 -#define CEPH_PG_LAYOUT_HYBRID 3 - -#define CEPH_PG_MAX_SIZE 16 /* max # osds in a single pg */ - -/* - * placement group. - * we encode this into one __le64. - */ -struct ceph_pg { - __le16 preferred; /* preferred primary osd */ - __le16 ps; /* placement seed */ - __le32 pool; /* object pool */ -} __attribute__ ((packed)); - -/* - * pg_pool is a set of pgs storing a pool of objects - * - * pg_num -- base number of pseudorandomly placed pgs - * - * pgp_num -- effective number when calculating pg placement. this - * is used for pg_num increases. new pgs result in data being "split" - * into new pgs. for this to proceed smoothly, new pgs are intiially - * colocated with their parents; that is, pgp_num doesn't increase - * until the new pgs have successfully split. only _then_ are the new - * pgs placed independently. - * - * lpg_num -- localized pg count (per device). replicas are randomly - * selected. - * - * lpgp_num -- as above. - */ -#define CEPH_PG_TYPE_REP 1 -#define CEPH_PG_TYPE_RAID4 2 -#define CEPH_PG_POOL_VERSION 2 -struct ceph_pg_pool { - __u8 type; /* CEPH_PG_TYPE_* */ - __u8 size; /* number of osds in each pg */ - __u8 crush_ruleset; /* crush placement rule */ - __u8 object_hash; /* hash mapping object name to ps */ - __le32 pg_num, pgp_num; /* number of pg's */ - __le32 lpg_num, lpgp_num; /* number of localized pg's */ - __le32 last_change; /* most recent epoch changed */ - __le64 snap_seq; /* seq for per-pool snapshot */ - __le32 snap_epoch; /* epoch of last snap */ - __le32 num_snaps; - __le32 num_removed_snap_intervals; /* if non-empty, NO per-pool snaps */ - __le64 auid; /* who owns the pg */ -} __attribute__ ((packed)); - -/* - * stable_mod func is used to control number of placement groups. - * similar to straight-up modulo, but produces a stable mapping as b - * increases over time. b is the number of bins, and bmask is the - * containing power of 2 minus 1. - * - * b <= bmask and bmask=(2**n)-1 - * e.g., b=12 -> bmask=15, b=123 -> bmask=127 - */ -static inline int ceph_stable_mod(int x, int b, int bmask) -{ - if ((x & bmask) < b) - return x & bmask; - else - return x & (bmask >> 1); -} - -/* - * object layout - how a given object should be stored. - */ -struct ceph_object_layout { - struct ceph_pg ol_pgid; /* raw pg, with _full_ ps precision. */ - __le32 ol_stripe_unit; /* for per-object parity, if any */ -} __attribute__ ((packed)); - -/* - * compound epoch+version, used by storage layer to serialize mutations - */ -struct ceph_eversion { - __le32 epoch; - __le64 version; -} __attribute__ ((packed)); - -/* - * osd map bits - */ - -/* status bits */ -#define CEPH_OSD_EXISTS 1 -#define CEPH_OSD_UP 2 - -/* osd weights. fixed point value: 0x10000 == 1.0 ("in"), 0 == "out" */ -#define CEPH_OSD_IN 0x10000 -#define CEPH_OSD_OUT 0 - - -/* - * osd map flag bits - */ -#define CEPH_OSDMAP_NEARFULL (1<<0) /* sync writes (near ENOSPC) */ -#define CEPH_OSDMAP_FULL (1<<1) /* no data writes (ENOSPC) */ -#define CEPH_OSDMAP_PAUSERD (1<<2) /* pause all reads */ -#define CEPH_OSDMAP_PAUSEWR (1<<3) /* pause all writes */ -#define CEPH_OSDMAP_PAUSEREC (1<<4) /* pause recovery */ - -/* - * osd ops - */ -#define CEPH_OSD_OP_MODE 0xf000 -#define CEPH_OSD_OP_MODE_RD 0x1000 -#define CEPH_OSD_OP_MODE_WR 0x2000 -#define CEPH_OSD_OP_MODE_RMW 0x3000 -#define CEPH_OSD_OP_MODE_SUB 0x4000 - -#define CEPH_OSD_OP_TYPE 0x0f00 -#define CEPH_OSD_OP_TYPE_LOCK 0x0100 -#define CEPH_OSD_OP_TYPE_DATA 0x0200 -#define CEPH_OSD_OP_TYPE_ATTR 0x0300 -#define CEPH_OSD_OP_TYPE_EXEC 0x0400 -#define CEPH_OSD_OP_TYPE_PG 0x0500 - -enum { - /** data **/ - /* read */ - CEPH_OSD_OP_READ = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 1, - CEPH_OSD_OP_STAT = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 2, - - /* fancy read */ - CEPH_OSD_OP_MASKTRUNC = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 4, - - /* write */ - CEPH_OSD_OP_WRITE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 1, - CEPH_OSD_OP_WRITEFULL = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 2, - CEPH_OSD_OP_TRUNCATE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 3, - CEPH_OSD_OP_ZERO = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 4, - CEPH_OSD_OP_DELETE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 5, - - /* fancy write */ - CEPH_OSD_OP_APPEND = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 6, - CEPH_OSD_OP_STARTSYNC = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 7, - CEPH_OSD_OP_SETTRUNC = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 8, - CEPH_OSD_OP_TRIMTRUNC = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 9, - - CEPH_OSD_OP_TMAPUP = CEPH_OSD_OP_MODE_RMW | CEPH_OSD_OP_TYPE_DATA | 10, - CEPH_OSD_OP_TMAPPUT = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 11, - CEPH_OSD_OP_TMAPGET = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 12, - - CEPH_OSD_OP_CREATE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 13, - CEPH_OSD_OP_ROLLBACK= CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 14, - - /** attrs **/ - /* read */ - CEPH_OSD_OP_GETXATTR = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_ATTR | 1, - CEPH_OSD_OP_GETXATTRS = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_ATTR | 2, - CEPH_OSD_OP_CMPXATTR = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_ATTR | 3, - - /* write */ - CEPH_OSD_OP_SETXATTR = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_ATTR | 1, - CEPH_OSD_OP_SETXATTRS = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_ATTR | 2, - CEPH_OSD_OP_RESETXATTRS = CEPH_OSD_OP_MODE_WR|CEPH_OSD_OP_TYPE_ATTR | 3, - CEPH_OSD_OP_RMXATTR = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_ATTR | 4, - - /** subop **/ - CEPH_OSD_OP_PULL = CEPH_OSD_OP_MODE_SUB | 1, - CEPH_OSD_OP_PUSH = CEPH_OSD_OP_MODE_SUB | 2, - CEPH_OSD_OP_BALANCEREADS = CEPH_OSD_OP_MODE_SUB | 3, - CEPH_OSD_OP_UNBALANCEREADS = CEPH_OSD_OP_MODE_SUB | 4, - CEPH_OSD_OP_SCRUB = CEPH_OSD_OP_MODE_SUB | 5, - - /** lock **/ - CEPH_OSD_OP_WRLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 1, - CEPH_OSD_OP_WRUNLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 2, - CEPH_OSD_OP_RDLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 3, - CEPH_OSD_OP_RDUNLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 4, - CEPH_OSD_OP_UPLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 5, - CEPH_OSD_OP_DNLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 6, - - /** exec **/ - CEPH_OSD_OP_CALL = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_EXEC | 1, - - /** pg **/ - CEPH_OSD_OP_PGLS = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_PG | 1, -}; - -static inline int ceph_osd_op_type_lock(int op) -{ - return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_LOCK; -} -static inline int ceph_osd_op_type_data(int op) -{ - return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_DATA; -} -static inline int ceph_osd_op_type_attr(int op) -{ - return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_ATTR; -} -static inline int ceph_osd_op_type_exec(int op) -{ - return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_EXEC; -} -static inline int ceph_osd_op_type_pg(int op) -{ - return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_PG; -} - -static inline int ceph_osd_op_mode_subop(int op) -{ - return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_SUB; -} -static inline int ceph_osd_op_mode_read(int op) -{ - return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_RD; -} -static inline int ceph_osd_op_mode_modify(int op) -{ - return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_WR; -} - -/* - * note that the following tmap stuff is also defined in the ceph librados.h - * any modification here needs to be updated there - */ -#define CEPH_OSD_TMAP_HDR 'h' -#define CEPH_OSD_TMAP_SET 's' -#define CEPH_OSD_TMAP_RM 'r' - -extern const char *ceph_osd_op_name(int op); - - -/* - * osd op flags - * - * An op may be READ, WRITE, or READ|WRITE. - */ -enum { - CEPH_OSD_FLAG_ACK = 1, /* want (or is) "ack" ack */ - CEPH_OSD_FLAG_ONNVRAM = 2, /* want (or is) "onnvram" ack */ - CEPH_OSD_FLAG_ONDISK = 4, /* want (or is) "ondisk" ack */ - CEPH_OSD_FLAG_RETRY = 8, /* resend attempt */ - CEPH_OSD_FLAG_READ = 16, /* op may read */ - CEPH_OSD_FLAG_WRITE = 32, /* op may write */ - CEPH_OSD_FLAG_ORDERSNAP = 64, /* EOLDSNAP if snapc is out of order */ - CEPH_OSD_FLAG_PEERSTAT = 128, /* msg includes osd_peer_stat */ - CEPH_OSD_FLAG_BALANCE_READS = 256, - CEPH_OSD_FLAG_PARALLELEXEC = 512, /* execute op in parallel */ - CEPH_OSD_FLAG_PGOP = 1024, /* pg op, no object */ - CEPH_OSD_FLAG_EXEC = 2048, /* op may exec */ - CEPH_OSD_FLAG_EXEC_PUBLIC = 4096, /* op may exec (public) */ -}; - -enum { - CEPH_OSD_OP_FLAG_EXCL = 1, /* EXCL object create */ -}; - -#define EOLDSNAPC ERESTART /* ORDERSNAP flag set; writer has old snapc*/ -#define EBLACKLISTED ESHUTDOWN /* blacklisted */ - -/* xattr comparison */ -enum { - CEPH_OSD_CMPXATTR_OP_NOP = 0, - CEPH_OSD_CMPXATTR_OP_EQ = 1, - CEPH_OSD_CMPXATTR_OP_NE = 2, - CEPH_OSD_CMPXATTR_OP_GT = 3, - CEPH_OSD_CMPXATTR_OP_GTE = 4, - CEPH_OSD_CMPXATTR_OP_LT = 5, - CEPH_OSD_CMPXATTR_OP_LTE = 6 -}; - -enum { - CEPH_OSD_CMPXATTR_MODE_STRING = 1, - CEPH_OSD_CMPXATTR_MODE_U64 = 2 -}; - -/* - * an individual object operation. each may be accompanied by some data - * payload - */ -struct ceph_osd_op { - __le16 op; /* CEPH_OSD_OP_* */ - __le32 flags; /* CEPH_OSD_FLAG_* */ - union { - struct { - __le64 offset, length; - __le64 truncate_size; - __le32 truncate_seq; - } __attribute__ ((packed)) extent; - struct { - __le32 name_len; - __le32 value_len; - __u8 cmp_op; /* CEPH_OSD_CMPXATTR_OP_* */ - __u8 cmp_mode; /* CEPH_OSD_CMPXATTR_MODE_* */ - } __attribute__ ((packed)) xattr; - struct { - __u8 class_len; - __u8 method_len; - __u8 argc; - __le32 indata_len; - } __attribute__ ((packed)) cls; - struct { - __le64 cookie, count; - } __attribute__ ((packed)) pgls; - struct { - __le64 snapid; - } __attribute__ ((packed)) snap; - }; - __le32 payload_len; -} __attribute__ ((packed)); - -/* - * osd request message header. each request may include multiple - * ceph_osd_op object operations. - */ -struct ceph_osd_request_head { - __le32 client_inc; /* client incarnation */ - struct ceph_object_layout layout; /* pgid */ - __le32 osdmap_epoch; /* client's osdmap epoch */ - - __le32 flags; - - struct ceph_timespec mtime; /* for mutations only */ - struct ceph_eversion reassert_version; /* if we are replaying op */ - - __le32 object_len; /* length of object name */ - - __le64 snapid; /* snapid to read */ - __le64 snap_seq; /* writer's snap context */ - __le32 num_snaps; - - __le16 num_ops; - struct ceph_osd_op ops[]; /* followed by ops[], obj, ticket, snaps */ -} __attribute__ ((packed)); - -struct ceph_osd_reply_head { - __le32 client_inc; /* client incarnation */ - __le32 flags; - struct ceph_object_layout layout; - __le32 osdmap_epoch; - struct ceph_eversion reassert_version; /* for replaying uncommitted */ - - __le32 result; /* result code */ - - __le32 object_len; /* length of object name */ - __le32 num_ops; - struct ceph_osd_op ops[0]; /* ops[], object */ -} __attribute__ ((packed)); - - -#endif diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 190b6c4a6f2..39c243acd06 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -1,10 +1,12 @@ -#include "ceph_debug.h" +#include #include #include #include "super.h" -#include "decode.h" +#include "mds_client.h" + +#include /* * Snapshots in ceph are driven in large part by cooperation from the @@ -526,7 +528,7 @@ int __ceph_finish_cap_snap(struct ceph_inode_info *ci, struct ceph_cap_snap *capsnap) { struct inode *inode = &ci->vfs_inode; - struct ceph_mds_client *mdsc = &ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; BUG_ON(capsnap->writing); capsnap->size = inode->i_size; @@ -747,7 +749,7 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc, struct ceph_mds_session *session, struct ceph_msg *msg) { - struct super_block *sb = mdsc->client->sb; + struct super_block *sb = mdsc->fsc->sb; int mds = session->s_mds; u64 split; int op; diff --git a/fs/ceph/strings.c b/fs/ceph/strings.c new file mode 100644 index 00000000000..cd5097d7c80 --- /dev/null +++ b/fs/ceph/strings.c @@ -0,0 +1,117 @@ +/* + * Ceph fs string constants + */ +#include +#include + + +const char *ceph_mds_state_name(int s) +{ + switch (s) { + /* down and out */ + case CEPH_MDS_STATE_DNE: return "down:dne"; + case CEPH_MDS_STATE_STOPPED: return "down:stopped"; + /* up and out */ + case CEPH_MDS_STATE_BOOT: return "up:boot"; + case CEPH_MDS_STATE_STANDBY: return "up:standby"; + case CEPH_MDS_STATE_STANDBY_REPLAY: return "up:standby-replay"; + case CEPH_MDS_STATE_CREATING: return "up:creating"; + case CEPH_MDS_STATE_STARTING: return "up:starting"; + /* up and in */ + case CEPH_MDS_STATE_REPLAY: return "up:replay"; + case CEPH_MDS_STATE_RESOLVE: return "up:resolve"; + case CEPH_MDS_STATE_RECONNECT: return "up:reconnect"; + case CEPH_MDS_STATE_REJOIN: return "up:rejoin"; + case CEPH_MDS_STATE_CLIENTREPLAY: return "up:clientreplay"; + case CEPH_MDS_STATE_ACTIVE: return "up:active"; + case CEPH_MDS_STATE_STOPPING: return "up:stopping"; + } + return "???"; +} + +const char *ceph_session_op_name(int op) +{ + switch (op) { + case CEPH_SESSION_REQUEST_OPEN: return "request_open"; + case CEPH_SESSION_OPEN: return "open"; + case CEPH_SESSION_REQUEST_CLOSE: return "request_close"; + case CEPH_SESSION_CLOSE: return "close"; + case CEPH_SESSION_REQUEST_RENEWCAPS: return "request_renewcaps"; + case CEPH_SESSION_RENEWCAPS: return "renewcaps"; + case CEPH_SESSION_STALE: return "stale"; + case CEPH_SESSION_RECALL_STATE: return "recall_state"; + } + return "???"; +} + +const char *ceph_mds_op_name(int op) +{ + switch (op) { + case CEPH_MDS_OP_LOOKUP: return "lookup"; + case CEPH_MDS_OP_LOOKUPHASH: return "lookuphash"; + case CEPH_MDS_OP_LOOKUPPARENT: return "lookupparent"; + case CEPH_MDS_OP_GETATTR: return "getattr"; + case CEPH_MDS_OP_SETXATTR: return "setxattr"; + case CEPH_MDS_OP_SETATTR: return "setattr"; + case CEPH_MDS_OP_RMXATTR: return "rmxattr"; + case CEPH_MDS_OP_READDIR: return "readdir"; + case CEPH_MDS_OP_MKNOD: return "mknod"; + case CEPH_MDS_OP_LINK: return "link"; + case CEPH_MDS_OP_UNLINK: return "unlink"; + case CEPH_MDS_OP_RENAME: return "rename"; + case CEPH_MDS_OP_MKDIR: return "mkdir"; + case CEPH_MDS_OP_RMDIR: return "rmdir"; + case CEPH_MDS_OP_SYMLINK: return "symlink"; + case CEPH_MDS_OP_CREATE: return "create"; + case CEPH_MDS_OP_OPEN: return "open"; + case CEPH_MDS_OP_LOOKUPSNAP: return "lookupsnap"; + case CEPH_MDS_OP_LSSNAP: return "lssnap"; + case CEPH_MDS_OP_MKSNAP: return "mksnap"; + case CEPH_MDS_OP_RMSNAP: return "rmsnap"; + case CEPH_MDS_OP_SETFILELOCK: return "setfilelock"; + case CEPH_MDS_OP_GETFILELOCK: return "getfilelock"; + } + return "???"; +} + +const char *ceph_cap_op_name(int op) +{ + switch (op) { + case CEPH_CAP_OP_GRANT: return "grant"; + case CEPH_CAP_OP_REVOKE: return "revoke"; + case CEPH_CAP_OP_TRUNC: return "trunc"; + case CEPH_CAP_OP_EXPORT: return "export"; + case CEPH_CAP_OP_IMPORT: return "import"; + case CEPH_CAP_OP_UPDATE: return "update"; + case CEPH_CAP_OP_DROP: return "drop"; + case CEPH_CAP_OP_FLUSH: return "flush"; + case CEPH_CAP_OP_FLUSH_ACK: return "flush_ack"; + case CEPH_CAP_OP_FLUSHSNAP: return "flushsnap"; + case CEPH_CAP_OP_FLUSHSNAP_ACK: return "flushsnap_ack"; + case CEPH_CAP_OP_RELEASE: return "release"; + case CEPH_CAP_OP_RENEW: return "renew"; + } + return "???"; +} + +const char *ceph_lease_op_name(int o) +{ + switch (o) { + case CEPH_MDS_LEASE_REVOKE: return "revoke"; + case CEPH_MDS_LEASE_RELEASE: return "release"; + case CEPH_MDS_LEASE_RENEW: return "renew"; + case CEPH_MDS_LEASE_REVOKE_ACK: return "revoke_ack"; + } + return "???"; +} + +const char *ceph_snap_op_name(int o) +{ + switch (o) { + case CEPH_SNAP_OP_UPDATE: return "update"; + case CEPH_SNAP_OP_CREATE: return "create"; + case CEPH_SNAP_OP_DESTROY: return "destroy"; + case CEPH_SNAP_OP_SPLIT: return "split"; + } + return "???"; +} diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 9922628532b..d6e0e042189 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -1,5 +1,5 @@ -#include "ceph_debug.h" +#include #include #include @@ -15,10 +15,13 @@ #include #include -#include "decode.h" #include "super.h" -#include "mon_client.h" -#include "auth.h" +#include "mds_client.h" + +#include +#include +#include +#include /* * Ceph superblock operations @@ -26,36 +29,22 @@ * Handle the basics of mounting, unmounting. */ - -/* - * find filename portion of a path (/foo/bar/baz -> baz) - */ -const char *ceph_file_part(const char *s, int len) -{ - const char *e = s + len; - - while (e != s && *(e-1) != '/') - e--; - return e; -} - - /* * super ops */ static void ceph_put_super(struct super_block *s) { - struct ceph_client *client = ceph_sb_to_client(s); + struct ceph_fs_client *fsc = ceph_sb_to_client(s); dout("put_super\n"); - ceph_mdsc_close_sessions(&client->mdsc); + ceph_mdsc_close_sessions(fsc->mdsc); /* * ensure we release the bdi before put_anon_super releases * the device name. */ - if (s->s_bdi == &client->backing_dev_info) { - bdi_unregister(&client->backing_dev_info); + if (s->s_bdi == &fsc->backing_dev_info) { + bdi_unregister(&fsc->backing_dev_info); s->s_bdi = NULL; } @@ -64,14 +53,14 @@ static void ceph_put_super(struct super_block *s) static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct ceph_client *client = ceph_inode_to_client(dentry->d_inode); - struct ceph_monmap *monmap = client->monc.monmap; + struct ceph_fs_client *fsc = ceph_inode_to_client(dentry->d_inode); + struct ceph_monmap *monmap = fsc->client->monc.monmap; struct ceph_statfs st; u64 fsid; int err; dout("statfs\n"); - err = ceph_monc_do_statfs(&client->monc, &st); + err = ceph_monc_do_statfs(&fsc->client->monc, &st); if (err < 0) return err; @@ -104,238 +93,28 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) static int ceph_sync_fs(struct super_block *sb, int wait) { - struct ceph_client *client = ceph_sb_to_client(sb); + struct ceph_fs_client *fsc = ceph_sb_to_client(sb); if (!wait) { dout("sync_fs (non-blocking)\n"); - ceph_flush_dirty_caps(&client->mdsc); + ceph_flush_dirty_caps(fsc->mdsc); dout("sync_fs (non-blocking) done\n"); return 0; } dout("sync_fs (blocking)\n"); - ceph_osdc_sync(&ceph_sb_to_client(sb)->osdc); - ceph_mdsc_sync(&ceph_sb_to_client(sb)->mdsc); + ceph_osdc_sync(&fsc->client->osdc); + ceph_mdsc_sync(fsc->mdsc); dout("sync_fs (blocking) done\n"); return 0; } -static int default_congestion_kb(void) -{ - int congestion_kb; - - /* - * Copied from NFS - * - * congestion size, scale with available memory. - * - * 64MB: 8192k - * 128MB: 11585k - * 256MB: 16384k - * 512MB: 23170k - * 1GB: 32768k - * 2GB: 46340k - * 4GB: 65536k - * 8GB: 92681k - * 16GB: 131072k - * - * This allows larger machines to have larger/more transfers. - * Limit the default to 256M - */ - congestion_kb = (16*int_sqrt(totalram_pages)) << (PAGE_SHIFT-10); - if (congestion_kb > 256*1024) - congestion_kb = 256*1024; - - return congestion_kb; -} - -/** - * ceph_show_options - Show mount options in /proc/mounts - * @m: seq_file to write to - * @mnt: mount descriptor - */ -static int ceph_show_options(struct seq_file *m, struct vfsmount *mnt) -{ - struct ceph_client *client = ceph_sb_to_client(mnt->mnt_sb); - struct ceph_mount_args *args = client->mount_args; - - if (args->flags & CEPH_OPT_FSID) - seq_printf(m, ",fsid=%pU", &args->fsid); - if (args->flags & CEPH_OPT_NOSHARE) - seq_puts(m, ",noshare"); - if (args->flags & CEPH_OPT_DIRSTAT) - seq_puts(m, ",dirstat"); - if ((args->flags & CEPH_OPT_RBYTES) == 0) - seq_puts(m, ",norbytes"); - if (args->flags & CEPH_OPT_NOCRC) - seq_puts(m, ",nocrc"); - if (args->flags & CEPH_OPT_NOASYNCREADDIR) - seq_puts(m, ",noasyncreaddir"); - - if (args->mount_timeout != CEPH_MOUNT_TIMEOUT_DEFAULT) - seq_printf(m, ",mount_timeout=%d", args->mount_timeout); - if (args->osd_idle_ttl != CEPH_OSD_IDLE_TTL_DEFAULT) - seq_printf(m, ",osd_idle_ttl=%d", args->osd_idle_ttl); - if (args->osd_timeout != CEPH_OSD_TIMEOUT_DEFAULT) - seq_printf(m, ",osdtimeout=%d", args->osd_timeout); - if (args->osd_keepalive_timeout != CEPH_OSD_KEEPALIVE_DEFAULT) - seq_printf(m, ",osdkeepalivetimeout=%d", - args->osd_keepalive_timeout); - if (args->wsize) - seq_printf(m, ",wsize=%d", args->wsize); - if (args->rsize != CEPH_MOUNT_RSIZE_DEFAULT) - seq_printf(m, ",rsize=%d", args->rsize); - if (args->congestion_kb != default_congestion_kb()) - seq_printf(m, ",write_congestion_kb=%d", args->congestion_kb); - if (args->caps_wanted_delay_min != CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT) - seq_printf(m, ",caps_wanted_delay_min=%d", - args->caps_wanted_delay_min); - if (args->caps_wanted_delay_max != CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT) - seq_printf(m, ",caps_wanted_delay_max=%d", - args->caps_wanted_delay_max); - if (args->cap_release_safety != CEPH_CAP_RELEASE_SAFETY_DEFAULT) - seq_printf(m, ",cap_release_safety=%d", - args->cap_release_safety); - if (args->max_readdir != CEPH_MAX_READDIR_DEFAULT) - seq_printf(m, ",readdir_max_entries=%d", args->max_readdir); - if (args->max_readdir_bytes != CEPH_MAX_READDIR_BYTES_DEFAULT) - seq_printf(m, ",readdir_max_bytes=%d", args->max_readdir_bytes); - if (strcmp(args->snapdir_name, CEPH_SNAPDIRNAME_DEFAULT)) - seq_printf(m, ",snapdirname=%s", args->snapdir_name); - if (args->name) - seq_printf(m, ",name=%s", args->name); - if (args->secret) - seq_puts(m, ",secret="); - return 0; -} - -/* - * caches - */ -struct kmem_cache *ceph_inode_cachep; -struct kmem_cache *ceph_cap_cachep; -struct kmem_cache *ceph_dentry_cachep; -struct kmem_cache *ceph_file_cachep; - -static void ceph_inode_init_once(void *foo) -{ - struct ceph_inode_info *ci = foo; - inode_init_once(&ci->vfs_inode); -} - -static int __init init_caches(void) -{ - ceph_inode_cachep = kmem_cache_create("ceph_inode_info", - sizeof(struct ceph_inode_info), - __alignof__(struct ceph_inode_info), - (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), - ceph_inode_init_once); - if (ceph_inode_cachep == NULL) - return -ENOMEM; - - ceph_cap_cachep = KMEM_CACHE(ceph_cap, - SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); - if (ceph_cap_cachep == NULL) - goto bad_cap; - - ceph_dentry_cachep = KMEM_CACHE(ceph_dentry_info, - SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); - if (ceph_dentry_cachep == NULL) - goto bad_dentry; - - ceph_file_cachep = KMEM_CACHE(ceph_file_info, - SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); - if (ceph_file_cachep == NULL) - goto bad_file; - - return 0; - -bad_file: - kmem_cache_destroy(ceph_dentry_cachep); -bad_dentry: - kmem_cache_destroy(ceph_cap_cachep); -bad_cap: - kmem_cache_destroy(ceph_inode_cachep); - return -ENOMEM; -} - -static void destroy_caches(void) -{ - kmem_cache_destroy(ceph_inode_cachep); - kmem_cache_destroy(ceph_cap_cachep); - kmem_cache_destroy(ceph_dentry_cachep); - kmem_cache_destroy(ceph_file_cachep); -} - - -/* - * ceph_umount_begin - initiate forced umount. Tear down down the - * mount, skipping steps that may hang while waiting for server(s). - */ -static void ceph_umount_begin(struct super_block *sb) -{ - struct ceph_client *client = ceph_sb_to_client(sb); - - dout("ceph_umount_begin - starting forced umount\n"); - if (!client) - return; - client->mount_state = CEPH_MOUNT_SHUTDOWN; - return; -} - -static const struct super_operations ceph_super_ops = { - .alloc_inode = ceph_alloc_inode, - .destroy_inode = ceph_destroy_inode, - .write_inode = ceph_write_inode, - .sync_fs = ceph_sync_fs, - .put_super = ceph_put_super, - .show_options = ceph_show_options, - .statfs = ceph_statfs, - .umount_begin = ceph_umount_begin, -}; - - -const char *ceph_msg_type_name(int type) -{ - switch (type) { - case CEPH_MSG_SHUTDOWN: return "shutdown"; - case CEPH_MSG_PING: return "ping"; - case CEPH_MSG_AUTH: return "auth"; - case CEPH_MSG_AUTH_REPLY: return "auth_reply"; - case CEPH_MSG_MON_MAP: return "mon_map"; - case CEPH_MSG_MON_GET_MAP: return "mon_get_map"; - case CEPH_MSG_MON_SUBSCRIBE: return "mon_subscribe"; - case CEPH_MSG_MON_SUBSCRIBE_ACK: return "mon_subscribe_ack"; - case CEPH_MSG_STATFS: return "statfs"; - case CEPH_MSG_STATFS_REPLY: return "statfs_reply"; - case CEPH_MSG_MDS_MAP: return "mds_map"; - case CEPH_MSG_CLIENT_SESSION: return "client_session"; - case CEPH_MSG_CLIENT_RECONNECT: return "client_reconnect"; - case CEPH_MSG_CLIENT_REQUEST: return "client_request"; - case CEPH_MSG_CLIENT_REQUEST_FORWARD: return "client_request_forward"; - case CEPH_MSG_CLIENT_REPLY: return "client_reply"; - case CEPH_MSG_CLIENT_CAPS: return "client_caps"; - case CEPH_MSG_CLIENT_CAPRELEASE: return "client_cap_release"; - case CEPH_MSG_CLIENT_SNAP: return "client_snap"; - case CEPH_MSG_CLIENT_LEASE: return "client_lease"; - case CEPH_MSG_OSD_MAP: return "osd_map"; - case CEPH_MSG_OSD_OP: return "osd_op"; - case CEPH_MSG_OSD_OPREPLY: return "osd_opreply"; - default: return "unknown"; - } -} - - /* * mount options */ enum { Opt_wsize, Opt_rsize, - Opt_osdtimeout, - Opt_osdkeepalivetimeout, - Opt_mount_timeout, - Opt_osd_idle_ttl, Opt_caps_wanted_delay_min, Opt_caps_wanted_delay_max, Opt_cap_release_safety, @@ -344,29 +123,19 @@ enum { Opt_congestion_kb, Opt_last_int, /* int args above */ - Opt_fsid, Opt_snapdirname, - Opt_name, - Opt_secret, Opt_last_string, /* string args above */ - Opt_ip, - Opt_noshare, Opt_dirstat, Opt_nodirstat, Opt_rbytes, Opt_norbytes, - Opt_nocrc, Opt_noasyncreaddir, }; -static match_table_t arg_tokens = { +static match_table_t fsopt_tokens = { {Opt_wsize, "wsize=%d"}, {Opt_rsize, "rsize=%d"}, - {Opt_osdtimeout, "osdtimeout=%d"}, - {Opt_osdkeepalivetimeout, "osdkeepalive=%d"}, - {Opt_mount_timeout, "mount_timeout=%d"}, - {Opt_osd_idle_ttl, "osd_idle_ttl=%d"}, {Opt_caps_wanted_delay_min, "caps_wanted_delay_min=%d"}, {Opt_caps_wanted_delay_max, "caps_wanted_delay_max=%d"}, {Opt_cap_release_safety, "cap_release_safety=%d"}, @@ -374,403 +143,459 @@ static match_table_t arg_tokens = { {Opt_readdir_max_bytes, "readdir_max_bytes=%d"}, {Opt_congestion_kb, "write_congestion_kb=%d"}, /* int args above */ - {Opt_fsid, "fsid=%s"}, {Opt_snapdirname, "snapdirname=%s"}, - {Opt_name, "name=%s"}, - {Opt_secret, "secret=%s"}, /* string args above */ - {Opt_ip, "ip=%s"}, - {Opt_noshare, "noshare"}, {Opt_dirstat, "dirstat"}, {Opt_nodirstat, "nodirstat"}, {Opt_rbytes, "rbytes"}, {Opt_norbytes, "norbytes"}, - {Opt_nocrc, "nocrc"}, {Opt_noasyncreaddir, "noasyncreaddir"}, {-1, NULL} }; -static int parse_fsid(const char *str, struct ceph_fsid *fsid) +static int parse_fsopt_token(char *c, void *private) { - int i = 0; - char tmp[3]; - int err = -EINVAL; - int d; - - dout("parse_fsid '%s'\n", str); - tmp[2] = 0; - while (*str && i < 16) { - if (ispunct(*str)) { - str++; - continue; + struct ceph_mount_options *fsopt = private; + substring_t argstr[MAX_OPT_ARGS]; + int token, intval, ret; + + token = match_token((char *)c, fsopt_tokens, argstr); + if (token < 0) + return -EINVAL; + + if (token < Opt_last_int) { + ret = match_int(&argstr[0], &intval); + if (ret < 0) { + pr_err("bad mount option arg (not int) " + "at '%s'\n", c); + return ret; } - if (!isxdigit(str[0]) || !isxdigit(str[1])) - break; - tmp[0] = str[0]; - tmp[1] = str[1]; - if (sscanf(tmp, "%x", &d) < 1) - break; - fsid->fsid[i] = d & 0xff; - i++; - str += 2; + dout("got int token %d val %d\n", token, intval); + } else if (token > Opt_last_int && token < Opt_last_string) { + dout("got string token %d val %s\n", token, + argstr[0].from); + } else { + dout("got token %d\n", token); } - if (i == 16) - err = 0; - dout("parse_fsid ret %d got fsid %pU", err, fsid); - return err; + switch (token) { + case Opt_snapdirname: + kfree(fsopt->snapdir_name); + fsopt->snapdir_name = kstrndup(argstr[0].from, + argstr[0].to-argstr[0].from, + GFP_KERNEL); + if (!fsopt->snapdir_name) + return -ENOMEM; + break; + + /* misc */ + case Opt_wsize: + fsopt->wsize = intval; + break; + case Opt_rsize: + fsopt->rsize = intval; + break; + case Opt_caps_wanted_delay_min: + fsopt->caps_wanted_delay_min = intval; + break; + case Opt_caps_wanted_delay_max: + fsopt->caps_wanted_delay_max = intval; + break; + case Opt_readdir_max_entries: + fsopt->max_readdir = intval; + break; + case Opt_readdir_max_bytes: + fsopt->max_readdir_bytes = intval; + break; + case Opt_congestion_kb: + fsopt->congestion_kb = intval; + break; + case Opt_dirstat: + fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT; + break; + case Opt_nodirstat: + fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT; + break; + case Opt_rbytes: + fsopt->flags |= CEPH_MOUNT_OPT_RBYTES; + break; + case Opt_norbytes: + fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES; + break; + case Opt_noasyncreaddir: + fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR; + break; + default: + BUG_ON(token); + } + return 0; } -static struct ceph_mount_args *parse_mount_args(int flags, char *options, - const char *dev_name, - const char **path) +static void destroy_mount_options(struct ceph_mount_options *args) { - struct ceph_mount_args *args; - const char *c; - int err = -ENOMEM; - substring_t argstr[MAX_OPT_ARGS]; + dout("destroy_mount_options %p\n", args); + kfree(args->snapdir_name); + kfree(args); +} - args = kzalloc(sizeof(*args), GFP_KERNEL); - if (!args) - return ERR_PTR(-ENOMEM); - args->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*args->mon_addr), - GFP_KERNEL); - if (!args->mon_addr) - goto out; +static int strcmp_null(const char *s1, const char *s2) +{ + if (!s1 && !s2) + return 0; + if (s1 && !s2) + return -1; + if (!s1 && s2) + return 1; + return strcmp(s1, s2); +} - dout("parse_mount_args %p, dev_name '%s'\n", args, dev_name); - - /* start with defaults */ - args->sb_flags = flags; - args->flags = CEPH_OPT_DEFAULT; - args->osd_timeout = CEPH_OSD_TIMEOUT_DEFAULT; - args->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT; - args->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT; /* seconds */ - args->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT; /* seconds */ - args->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT; - args->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT; - args->rsize = CEPH_MOUNT_RSIZE_DEFAULT; - args->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL); - args->cap_release_safety = CEPH_CAP_RELEASE_SAFETY_DEFAULT; - args->max_readdir = CEPH_MAX_READDIR_DEFAULT; - args->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT; - args->congestion_kb = default_congestion_kb(); - - /* ip1[:port1][,ip2[:port2]...]:/subdir/in/fs */ - err = -EINVAL; - if (!dev_name) - goto out; - *path = strstr(dev_name, ":/"); - if (*path == NULL) { - pr_err("device name is missing path (no :/ in %s)\n", - dev_name); - goto out; - } +static int compare_mount_options(struct ceph_mount_options *new_fsopt, + struct ceph_options *new_opt, + struct ceph_fs_client *fsc) +{ + struct ceph_mount_options *fsopt1 = new_fsopt; + struct ceph_mount_options *fsopt2 = fsc->mount_options; + int ofs = offsetof(struct ceph_mount_options, snapdir_name); + int ret; - /* get mon ip(s) */ - err = ceph_parse_ips(dev_name, *path, args->mon_addr, - CEPH_MAX_MON, &args->num_mon); - if (err < 0) - goto out; + ret = memcmp(fsopt1, fsopt2, ofs); + if (ret) + return ret; + + ret = strcmp_null(fsopt1->snapdir_name, fsopt2->snapdir_name); + if (ret) + return ret; + + return ceph_compare_options(new_opt, fsc->client); +} + +static int parse_mount_options(struct ceph_mount_options **pfsopt, + struct ceph_options **popt, + int flags, char *options, + const char *dev_name, + const char **path) +{ + struct ceph_mount_options *fsopt; + const char *dev_name_end; + int err = -ENOMEM; + + fsopt = kzalloc(sizeof(*fsopt), GFP_KERNEL); + if (!fsopt) + return -ENOMEM; + + dout("parse_mount_options %p, dev_name '%s'\n", fsopt, dev_name); + + fsopt->sb_flags = flags; + fsopt->flags = CEPH_MOUNT_OPT_DEFAULT; + + fsopt->rsize = CEPH_MOUNT_RSIZE_DEFAULT; + fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL); + fsopt->cap_release_safety = CEPH_CAP_RELEASE_SAFETY_DEFAULT; + fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT; + fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT; + fsopt->congestion_kb = default_congestion_kb(); + + /* ip1[:port1][,ip2[:port2]...]:/subdir/in/fs */ + err = -EINVAL; + if (!dev_name) + goto out; + *path = strstr(dev_name, ":/"); + if (*path == NULL) { + pr_err("device name is missing path (no :/ in %s)\n", + dev_name); + goto out; + } + dev_name_end = *path; + dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name); /* path on server */ *path += 2; dout("server path '%s'\n", *path); - /* parse mount options */ - while ((c = strsep(&options, ",")) != NULL) { - int token, intval, ret; - if (!*c) - continue; - err = -EINVAL; - token = match_token((char *)c, arg_tokens, argstr); - if (token < 0) { - pr_err("bad mount option at '%s'\n", c); - goto out; - } - if (token < Opt_last_int) { - ret = match_int(&argstr[0], &intval); - if (ret < 0) { - pr_err("bad mount option arg (not int) " - "at '%s'\n", c); - continue; - } - dout("got int token %d val %d\n", token, intval); - } else if (token > Opt_last_int && token < Opt_last_string) { - dout("got string token %d val %s\n", token, - argstr[0].from); - } else { - dout("got token %d\n", token); - } - switch (token) { - case Opt_ip: - err = ceph_parse_ips(argstr[0].from, - argstr[0].to, - &args->my_addr, - 1, NULL); - if (err < 0) - goto out; - args->flags |= CEPH_OPT_MYIP; - break; - - case Opt_fsid: - err = parse_fsid(argstr[0].from, &args->fsid); - if (err == 0) - args->flags |= CEPH_OPT_FSID; - break; - case Opt_snapdirname: - kfree(args->snapdir_name); - args->snapdir_name = kstrndup(argstr[0].from, - argstr[0].to-argstr[0].from, - GFP_KERNEL); - break; - case Opt_name: - args->name = kstrndup(argstr[0].from, - argstr[0].to-argstr[0].from, - GFP_KERNEL); - break; - case Opt_secret: - args->secret = kstrndup(argstr[0].from, - argstr[0].to-argstr[0].from, - GFP_KERNEL); - break; - - /* misc */ - case Opt_wsize: - args->wsize = intval; - break; - case Opt_rsize: - args->rsize = intval; - break; - case Opt_osdtimeout: - args->osd_timeout = intval; - break; - case Opt_osdkeepalivetimeout: - args->osd_keepalive_timeout = intval; - break; - case Opt_osd_idle_ttl: - args->osd_idle_ttl = intval; - break; - case Opt_mount_timeout: - args->mount_timeout = intval; - break; - case Opt_caps_wanted_delay_min: - args->caps_wanted_delay_min = intval; - break; - case Opt_caps_wanted_delay_max: - args->caps_wanted_delay_max = intval; - break; - case Opt_readdir_max_entries: - args->max_readdir = intval; - break; - case Opt_readdir_max_bytes: - args->max_readdir_bytes = intval; - break; - case Opt_congestion_kb: - args->congestion_kb = intval; - break; - - case Opt_noshare: - args->flags |= CEPH_OPT_NOSHARE; - break; - - case Opt_dirstat: - args->flags |= CEPH_OPT_DIRSTAT; - break; - case Opt_nodirstat: - args->flags &= ~CEPH_OPT_DIRSTAT; - break; - case Opt_rbytes: - args->flags |= CEPH_OPT_RBYTES; - break; - case Opt_norbytes: - args->flags &= ~CEPH_OPT_RBYTES; - break; - case Opt_nocrc: - args->flags |= CEPH_OPT_NOCRC; - break; - case Opt_noasyncreaddir: - args->flags |= CEPH_OPT_NOASYNCREADDIR; - break; - - default: - BUG_ON(token); - } - } - return args; + err = ceph_parse_options(popt, options, dev_name, dev_name_end, + parse_fsopt_token, (void *)fsopt); + if (err) + goto out; + + /* success */ + *pfsopt = fsopt; + return 0; out: - kfree(args->mon_addr); - kfree(args); - return ERR_PTR(err); + destroy_mount_options(fsopt); + return err; } -static void destroy_mount_args(struct ceph_mount_args *args) +/** + * ceph_show_options - Show mount options in /proc/mounts + * @m: seq_file to write to + * @mnt: mount descriptor + */ +static int ceph_show_options(struct seq_file *m, struct vfsmount *mnt) { - dout("destroy_mount_args %p\n", args); - kfree(args->snapdir_name); - args->snapdir_name = NULL; - kfree(args->name); - args->name = NULL; - kfree(args->secret); - args->secret = NULL; - kfree(args); + struct ceph_fs_client *fsc = ceph_sb_to_client(mnt->mnt_sb); + struct ceph_mount_options *fsopt = fsc->mount_options; + struct ceph_options *opt = fsc->client->options; + + if (opt->flags & CEPH_OPT_FSID) + seq_printf(m, ",fsid=%pU", &opt->fsid); + if (opt->flags & CEPH_OPT_NOSHARE) + seq_puts(m, ",noshare"); + if (opt->flags & CEPH_OPT_NOCRC) + seq_puts(m, ",nocrc"); + + if (opt->name) + seq_printf(m, ",name=%s", opt->name); + if (opt->secret) + seq_puts(m, ",secret="); + + if (opt->mount_timeout != CEPH_MOUNT_TIMEOUT_DEFAULT) + seq_printf(m, ",mount_timeout=%d", opt->mount_timeout); + if (opt->osd_idle_ttl != CEPH_OSD_IDLE_TTL_DEFAULT) + seq_printf(m, ",osd_idle_ttl=%d", opt->osd_idle_ttl); + if (opt->osd_timeout != CEPH_OSD_TIMEOUT_DEFAULT) + seq_printf(m, ",osdtimeout=%d", opt->osd_timeout); + if (opt->osd_keepalive_timeout != CEPH_OSD_KEEPALIVE_DEFAULT) + seq_printf(m, ",osdkeepalivetimeout=%d", + opt->osd_keepalive_timeout); + + if (fsopt->flags & CEPH_MOUNT_OPT_DIRSTAT) + seq_puts(m, ",dirstat"); + if ((fsopt->flags & CEPH_MOUNT_OPT_RBYTES) == 0) + seq_puts(m, ",norbytes"); + if (fsopt->flags & CEPH_MOUNT_OPT_NOASYNCREADDIR) + seq_puts(m, ",noasyncreaddir"); + + if (fsopt->wsize) + seq_printf(m, ",wsize=%d", fsopt->wsize); + if (fsopt->rsize != CEPH_MOUNT_RSIZE_DEFAULT) + seq_printf(m, ",rsize=%d", fsopt->rsize); + if (fsopt->congestion_kb != default_congestion_kb()) + seq_printf(m, ",write_congestion_kb=%d", fsopt->congestion_kb); + if (fsopt->caps_wanted_delay_min != CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT) + seq_printf(m, ",caps_wanted_delay_min=%d", + fsopt->caps_wanted_delay_min); + if (fsopt->caps_wanted_delay_max != CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT) + seq_printf(m, ",caps_wanted_delay_max=%d", + fsopt->caps_wanted_delay_max); + if (fsopt->cap_release_safety != CEPH_CAP_RELEASE_SAFETY_DEFAULT) + seq_printf(m, ",cap_release_safety=%d", + fsopt->cap_release_safety); + if (fsopt->max_readdir != CEPH_MAX_READDIR_DEFAULT) + seq_printf(m, ",readdir_max_entries=%d", fsopt->max_readdir); + if (fsopt->max_readdir_bytes != CEPH_MAX_READDIR_BYTES_DEFAULT) + seq_printf(m, ",readdir_max_bytes=%d", fsopt->max_readdir_bytes); + if (strcmp(fsopt->snapdir_name, CEPH_SNAPDIRNAME_DEFAULT)) + seq_printf(m, ",snapdirname=%s", fsopt->snapdir_name); + return 0; } /* - * create a fresh client instance + * handle any mon messages the standard library doesn't understand. + * return error if we don't either. */ -static struct ceph_client *ceph_create_client(struct ceph_mount_args *args) +static int extra_mon_dispatch(struct ceph_client *client, struct ceph_msg *msg) { - struct ceph_client *client; + struct ceph_fs_client *fsc = client->private; + int type = le16_to_cpu(msg->hdr.type); + + switch (type) { + case CEPH_MSG_MDS_MAP: + ceph_mdsc_handle_map(fsc->mdsc, msg); + return 0; + + default: + return -1; + } +} + +/* + * create a new fs client + */ +struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt, + struct ceph_options *opt) +{ + struct ceph_fs_client *fsc; int err = -ENOMEM; - client = kzalloc(sizeof(*client), GFP_KERNEL); - if (client == NULL) + fsc = kzalloc(sizeof(*fsc), GFP_KERNEL); + if (!fsc) return ERR_PTR(-ENOMEM); - mutex_init(&client->mount_mutex); - - init_waitqueue_head(&client->auth_wq); + fsc->client = ceph_create_client(opt, fsc); + if (IS_ERR(fsc->client)) { + err = PTR_ERR(fsc->client); + goto fail; + } + fsc->client->extra_mon_dispatch = extra_mon_dispatch; + fsc->client->supported_features |= CEPH_FEATURE_FLOCK; + fsc->client->monc.want_mdsmap = 1; - client->sb = NULL; - client->mount_state = CEPH_MOUNT_MOUNTING; - client->mount_args = args; + fsc->mount_options = fsopt; - client->msgr = NULL; + fsc->sb = NULL; + fsc->mount_state = CEPH_MOUNT_MOUNTING; - client->auth_err = 0; - atomic_long_set(&client->writeback_count, 0); + atomic_long_set(&fsc->writeback_count, 0); - err = bdi_init(&client->backing_dev_info); + err = bdi_init(&fsc->backing_dev_info); if (err < 0) - goto fail; + goto fail_client; err = -ENOMEM; - client->wb_wq = create_workqueue("ceph-writeback"); - if (client->wb_wq == NULL) + fsc->wb_wq = create_workqueue("ceph-writeback"); + if (fsc->wb_wq == NULL) goto fail_bdi; - client->pg_inv_wq = create_singlethread_workqueue("ceph-pg-invalid"); - if (client->pg_inv_wq == NULL) + fsc->pg_inv_wq = create_singlethread_workqueue("ceph-pg-invalid"); + if (fsc->pg_inv_wq == NULL) goto fail_wb_wq; - client->trunc_wq = create_singlethread_workqueue("ceph-trunc"); - if (client->trunc_wq == NULL) + fsc->trunc_wq = create_singlethread_workqueue("ceph-trunc"); + if (fsc->trunc_wq == NULL) goto fail_pg_inv_wq; /* set up mempools */ err = -ENOMEM; - client->wb_pagevec_pool = mempool_create_kmalloc_pool(10, - client->mount_args->wsize >> PAGE_CACHE_SHIFT); - if (!client->wb_pagevec_pool) + fsc->wb_pagevec_pool = mempool_create_kmalloc_pool(10, + fsc->mount_options->wsize >> PAGE_CACHE_SHIFT); + if (!fsc->wb_pagevec_pool) goto fail_trunc_wq; /* caps */ - client->min_caps = args->max_readdir; + fsc->min_caps = fsopt->max_readdir; + + return fsc; - /* subsystems */ - err = ceph_monc_init(&client->monc, client); - if (err < 0) - goto fail_mempool; - err = ceph_osdc_init(&client->osdc, client); - if (err < 0) - goto fail_monc; - err = ceph_mdsc_init(&client->mdsc, client); - if (err < 0) - goto fail_osdc; - return client; - -fail_osdc: - ceph_osdc_stop(&client->osdc); -fail_monc: - ceph_monc_stop(&client->monc); -fail_mempool: - mempool_destroy(client->wb_pagevec_pool); fail_trunc_wq: - destroy_workqueue(client->trunc_wq); + destroy_workqueue(fsc->trunc_wq); fail_pg_inv_wq: - destroy_workqueue(client->pg_inv_wq); + destroy_workqueue(fsc->pg_inv_wq); fail_wb_wq: - destroy_workqueue(client->wb_wq); + destroy_workqueue(fsc->wb_wq); fail_bdi: - bdi_destroy(&client->backing_dev_info); + bdi_destroy(&fsc->backing_dev_info); +fail_client: + ceph_destroy_client(fsc->client); fail: - kfree(client); + kfree(fsc); return ERR_PTR(err); } -static void ceph_destroy_client(struct ceph_client *client) +void destroy_fs_client(struct ceph_fs_client *fsc) { - dout("destroy_client %p\n", client); + dout("destroy_fs_client %p\n", fsc); - /* unmount */ - ceph_mdsc_stop(&client->mdsc); - ceph_osdc_stop(&client->osdc); + destroy_workqueue(fsc->wb_wq); + destroy_workqueue(fsc->pg_inv_wq); + destroy_workqueue(fsc->trunc_wq); - /* - * make sure mds and osd connections close out before destroying - * the auth module, which is needed to free those connections' - * ceph_authorizers. - */ - ceph_msgr_flush(); - - ceph_monc_stop(&client->monc); + bdi_destroy(&fsc->backing_dev_info); - ceph_debugfs_client_cleanup(client); - destroy_workqueue(client->wb_wq); - destroy_workqueue(client->pg_inv_wq); - destroy_workqueue(client->trunc_wq); + mempool_destroy(fsc->wb_pagevec_pool); - bdi_destroy(&client->backing_dev_info); + destroy_mount_options(fsc->mount_options); - if (client->msgr) - ceph_messenger_destroy(client->msgr); - mempool_destroy(client->wb_pagevec_pool); + ceph_fs_debugfs_cleanup(fsc); - destroy_mount_args(client->mount_args); + ceph_destroy_client(fsc->client); - kfree(client); - dout("destroy_client %p done\n", client); + kfree(fsc); + dout("destroy_fs_client %p done\n", fsc); } /* - * Initially learn our fsid, or verify an fsid matches. + * caches */ -int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid) +struct kmem_cache *ceph_inode_cachep; +struct kmem_cache *ceph_cap_cachep; +struct kmem_cache *ceph_dentry_cachep; +struct kmem_cache *ceph_file_cachep; + +static void ceph_inode_init_once(void *foo) { - if (client->have_fsid) { - if (ceph_fsid_compare(&client->fsid, fsid)) { - pr_err("bad fsid, had %pU got %pU", - &client->fsid, fsid); - return -1; - } - } else { - pr_info("client%lld fsid %pU\n", client->monc.auth->global_id, - fsid); - memcpy(&client->fsid, fsid, sizeof(*fsid)); - ceph_debugfs_client_init(client); - client->have_fsid = true; - } + struct ceph_inode_info *ci = foo; + inode_init_once(&ci->vfs_inode); +} + +static int __init init_caches(void) +{ + ceph_inode_cachep = kmem_cache_create("ceph_inode_info", + sizeof(struct ceph_inode_info), + __alignof__(struct ceph_inode_info), + (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), + ceph_inode_init_once); + if (ceph_inode_cachep == NULL) + return -ENOMEM; + + ceph_cap_cachep = KMEM_CACHE(ceph_cap, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); + if (ceph_cap_cachep == NULL) + goto bad_cap; + + ceph_dentry_cachep = KMEM_CACHE(ceph_dentry_info, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); + if (ceph_dentry_cachep == NULL) + goto bad_dentry; + + ceph_file_cachep = KMEM_CACHE(ceph_file_info, + SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD); + if (ceph_file_cachep == NULL) + goto bad_file; + return 0; + +bad_file: + kmem_cache_destroy(ceph_dentry_cachep); +bad_dentry: + kmem_cache_destroy(ceph_cap_cachep); +bad_cap: + kmem_cache_destroy(ceph_inode_cachep); + return -ENOMEM; } +static void destroy_caches(void) +{ + kmem_cache_destroy(ceph_inode_cachep); + kmem_cache_destroy(ceph_cap_cachep); + kmem_cache_destroy(ceph_dentry_cachep); + kmem_cache_destroy(ceph_file_cachep); +} + + /* - * true if we have the mon map (and have thus joined the cluster) + * ceph_umount_begin - initiate forced umount. Tear down down the + * mount, skipping steps that may hang while waiting for server(s). */ -static int have_mon_and_osd_map(struct ceph_client *client) +static void ceph_umount_begin(struct super_block *sb) { - return client->monc.monmap && client->monc.monmap->epoch && - client->osdc.osdmap && client->osdc.osdmap->epoch; + struct ceph_fs_client *fsc = ceph_sb_to_client(sb); + + dout("ceph_umount_begin - starting forced umount\n"); + if (!fsc) + return; + fsc->mount_state = CEPH_MOUNT_SHUTDOWN; + return; } +static const struct super_operations ceph_super_ops = { + .alloc_inode = ceph_alloc_inode, + .destroy_inode = ceph_destroy_inode, + .write_inode = ceph_write_inode, + .sync_fs = ceph_sync_fs, + .put_super = ceph_put_super, + .show_options = ceph_show_options, + .statfs = ceph_statfs, + .umount_begin = ceph_umount_begin, +}; + /* * Bootstrap mount by opening the root directory. Note the mount * @started time from caller, and time out if this takes too long. */ -static struct dentry *open_root_dentry(struct ceph_client *client, +static struct dentry *open_root_dentry(struct ceph_fs_client *fsc, const char *path, unsigned long started) { - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req = NULL; int err; struct dentry *root; @@ -784,14 +609,14 @@ static struct dentry *open_root_dentry(struct ceph_client *client, req->r_ino1.ino = CEPH_INO_ROOT; req->r_ino1.snap = CEPH_NOSNAP; req->r_started = started; - req->r_timeout = client->mount_args->mount_timeout * HZ; + req->r_timeout = fsc->client->options->mount_timeout * HZ; req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE); req->r_num_caps = 2; err = ceph_mdsc_do_request(mdsc, NULL, req); if (err == 0) { dout("open_root_inode success\n"); if (ceph_ino(req->r_target_inode) == CEPH_INO_ROOT && - client->sb->s_root == NULL) + fsc->sb->s_root == NULL) root = d_alloc_root(req->r_target_inode); else root = d_obtain_alias(req->r_target_inode); @@ -804,105 +629,86 @@ static struct dentry *open_root_dentry(struct ceph_client *client, return root; } + + + /* * mount: join the ceph cluster, and open root directory. */ -static int ceph_mount(struct ceph_client *client, struct vfsmount *mnt, +static int ceph_mount(struct ceph_fs_client *fsc, struct vfsmount *mnt, const char *path) { - struct ceph_entity_addr *myaddr = NULL; int err; - unsigned long timeout = client->mount_args->mount_timeout * HZ; unsigned long started = jiffies; /* note the start time */ struct dentry *root; + int first = 0; /* first vfsmount for this super_block */ dout("mount start\n"); - mutex_lock(&client->mount_mutex); - - /* initialize the messenger */ - if (client->msgr == NULL) { - if (ceph_test_opt(client, MYIP)) - myaddr = &client->mount_args->my_addr; - client->msgr = ceph_messenger_create(myaddr); - if (IS_ERR(client->msgr)) { - err = PTR_ERR(client->msgr); - client->msgr = NULL; - goto out; - } - client->msgr->nocrc = ceph_test_opt(client, NOCRC); - } + mutex_lock(&fsc->client->mount_mutex); - /* open session, and wait for mon, mds, and osd maps */ - err = ceph_monc_open_session(&client->monc); + err = __ceph_open_session(fsc->client, started); if (err < 0) goto out; - while (!have_mon_and_osd_map(client)) { - err = -EIO; - if (timeout && time_after_eq(jiffies, started + timeout)) - goto out; - - /* wait */ - dout("mount waiting for mon_map\n"); - err = wait_event_interruptible_timeout(client->auth_wq, - have_mon_and_osd_map(client) || (client->auth_err < 0), - timeout); - if (err == -EINTR || err == -ERESTARTSYS) - goto out; - if (client->auth_err < 0) { - err = client->auth_err; - goto out; - } - } - dout("mount opening root\n"); - root = open_root_dentry(client, "", started); + root = open_root_dentry(fsc, "", started); if (IS_ERR(root)) { err = PTR_ERR(root); goto out; } - if (client->sb->s_root) + if (fsc->sb->s_root) { dput(root); - else - client->sb->s_root = root; + } else { + fsc->sb->s_root = root; + first = 1; + + err = ceph_fs_debugfs_init(fsc); + if (err < 0) + goto fail; + } if (path[0] == 0) { dget(root); } else { dout("mount opening base mountpoint\n"); - root = open_root_dentry(client, path, started); + root = open_root_dentry(fsc, path, started); if (IS_ERR(root)) { err = PTR_ERR(root); - dput(client->sb->s_root); - client->sb->s_root = NULL; - goto out; + goto fail; } } mnt->mnt_root = root; - mnt->mnt_sb = client->sb; + mnt->mnt_sb = fsc->sb; - client->mount_state = CEPH_MOUNT_MOUNTED; + fsc->mount_state = CEPH_MOUNT_MOUNTED; dout("mount success\n"); err = 0; out: - mutex_unlock(&client->mount_mutex); + mutex_unlock(&fsc->client->mount_mutex); return err; + +fail: + if (first) { + dput(fsc->sb->s_root); + fsc->sb->s_root = NULL; + } + goto out; } static int ceph_set_super(struct super_block *s, void *data) { - struct ceph_client *client = data; + struct ceph_fs_client *fsc = data; int ret; dout("set_super %p data %p\n", s, data); - s->s_flags = client->mount_args->sb_flags; + s->s_flags = fsc->mount_options->sb_flags; s->s_maxbytes = 1ULL << 40; /* temp value until we get mdsmap */ - s->s_fs_info = client; - client->sb = s; + s->s_fs_info = fsc; + fsc->sb = s; s->s_op = &ceph_super_ops; s->s_export_op = &ceph_export_ops; @@ -917,7 +723,7 @@ static int ceph_set_super(struct super_block *s, void *data) fail: s->s_fs_info = NULL; - client->sb = NULL; + fsc->sb = NULL; return ret; } @@ -926,30 +732,23 @@ fail: */ static int ceph_compare_super(struct super_block *sb, void *data) { - struct ceph_client *new = data; - struct ceph_mount_args *args = new->mount_args; - struct ceph_client *other = ceph_sb_to_client(sb); - int i; + struct ceph_fs_client *new = data; + struct ceph_mount_options *fsopt = new->mount_options; + struct ceph_options *opt = new->client->options; + struct ceph_fs_client *other = ceph_sb_to_client(sb); dout("ceph_compare_super %p\n", sb); - if (args->flags & CEPH_OPT_FSID) { - if (ceph_fsid_compare(&args->fsid, &other->fsid)) { - dout("fsid doesn't match\n"); - return 0; - } - } else { - /* do we share (a) monitor? */ - for (i = 0; i < new->monc.monmap->num_mon; i++) - if (ceph_monmap_contains(other->monc.monmap, - &new->monc.monmap->mon_inst[i].addr)) - break; - if (i == new->monc.monmap->num_mon) { - dout("mon ip not part of monmap\n"); - return 0; - } - dout("mon ip matches existing sb %p\n", sb); + + if (compare_mount_options(fsopt, opt, other)) { + dout("monitor(s)/mount options don't match\n"); + return 0; } - if (args->sb_flags != other->mount_args->sb_flags) { + if ((opt->flags & CEPH_OPT_FSID) && + ceph_fsid_compare(&opt->fsid, &other->client->fsid)) { + dout("fsid doesn't match\n"); + return 0; + } + if (fsopt->sb_flags != other->mount_options->sb_flags) { dout("flags differ\n"); return 0; } @@ -961,19 +760,20 @@ static int ceph_compare_super(struct super_block *sb, void *data) */ static atomic_long_t bdi_seq = ATOMIC_LONG_INIT(0); -static int ceph_register_bdi(struct super_block *sb, struct ceph_client *client) +static int ceph_register_bdi(struct super_block *sb, + struct ceph_fs_client *fsc) { int err; /* set ra_pages based on rsize mount option? */ - if (client->mount_args->rsize >= PAGE_CACHE_SIZE) - client->backing_dev_info.ra_pages = - (client->mount_args->rsize + PAGE_CACHE_SIZE - 1) + if (fsc->mount_options->rsize >= PAGE_CACHE_SIZE) + fsc->backing_dev_info.ra_pages = + (fsc->mount_options->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_SHIFT; - err = bdi_register(&client->backing_dev_info, NULL, "ceph-%d", + err = bdi_register(&fsc->backing_dev_info, NULL, "ceph-%d", atomic_long_inc_return(&bdi_seq)); if (!err) - sb->s_bdi = &client->backing_dev_info; + sb->s_bdi = &fsc->backing_dev_info; return err; } @@ -982,46 +782,52 @@ static int ceph_get_sb(struct file_system_type *fs_type, struct vfsmount *mnt) { struct super_block *sb; - struct ceph_client *client; + struct ceph_fs_client *fsc; int err; int (*compare_super)(struct super_block *, void *) = ceph_compare_super; const char *path = NULL; - struct ceph_mount_args *args; + struct ceph_mount_options *fsopt = NULL; + struct ceph_options *opt = NULL; dout("ceph_get_sb\n"); - args = parse_mount_args(flags, data, dev_name, &path); - if (IS_ERR(args)) { - err = PTR_ERR(args); + err = parse_mount_options(&fsopt, &opt, flags, data, dev_name, &path); + if (err < 0) goto out_final; - } /* create client (which we may/may not use) */ - client = ceph_create_client(args); - if (IS_ERR(client)) { - err = PTR_ERR(client); + fsc = create_fs_client(fsopt, opt); + if (IS_ERR(fsc)) { + err = PTR_ERR(fsc); + kfree(fsopt); + kfree(opt); goto out_final; } - if (client->mount_args->flags & CEPH_OPT_NOSHARE) + err = ceph_mdsc_init(fsc); + if (err < 0) + goto out; + + if (ceph_test_opt(fsc->client, NOSHARE)) compare_super = NULL; - sb = sget(fs_type, compare_super, ceph_set_super, client); + sb = sget(fs_type, compare_super, ceph_set_super, fsc); if (IS_ERR(sb)) { err = PTR_ERR(sb); goto out; } - if (ceph_sb_to_client(sb) != client) { - ceph_destroy_client(client); - client = ceph_sb_to_client(sb); - dout("get_sb got existing client %p\n", client); + if (ceph_sb_to_client(sb) != fsc) { + ceph_mdsc_destroy(fsc); + destroy_fs_client(fsc); + fsc = ceph_sb_to_client(sb); + dout("get_sb got existing client %p\n", fsc); } else { - dout("get_sb using new client %p\n", client); - err = ceph_register_bdi(sb, client); + dout("get_sb using new client %p\n", fsc); + err = ceph_register_bdi(sb, fsc); if (err < 0) goto out_splat; } - err = ceph_mount(client, mnt, path); + err = ceph_mount(fsc, mnt, path); if (err < 0) goto out_splat; dout("root %p inode %p ino %llx.%llx\n", mnt->mnt_root, @@ -1029,12 +835,13 @@ static int ceph_get_sb(struct file_system_type *fs_type, return 0; out_splat: - ceph_mdsc_close_sessions(&client->mdsc); + ceph_mdsc_close_sessions(fsc->mdsc); deactivate_locked_super(sb); goto out_final; out: - ceph_destroy_client(client); + ceph_mdsc_destroy(fsc); + destroy_fs_client(fsc); out_final: dout("ceph_get_sb fail %d\n", err); return err; @@ -1042,11 +849,12 @@ out_final: static void ceph_kill_sb(struct super_block *s) { - struct ceph_client *client = ceph_sb_to_client(s); + struct ceph_fs_client *fsc = ceph_sb_to_client(s); dout("kill_sb %p\n", s); - ceph_mdsc_pre_umount(&client->mdsc); + ceph_mdsc_pre_umount(fsc->mdsc); kill_anon_super(s); /* will call put_super after sb is r/o */ - ceph_destroy_client(client); + ceph_mdsc_destroy(fsc); + destroy_fs_client(fsc); } static struct file_system_type ceph_fs_type = { @@ -1062,36 +870,20 @@ static struct file_system_type ceph_fs_type = { static int __init init_ceph(void) { - int ret = 0; - - ret = ceph_debugfs_init(); - if (ret < 0) - goto out; - - ret = ceph_msgr_init(); - if (ret < 0) - goto out_debugfs; - - ret = init_caches(); + int ret = init_caches(); if (ret) - goto out_msgr; + goto out; ret = register_filesystem(&ceph_fs_type); if (ret) goto out_icache; - pr_info("loaded (mon/mds/osd proto %d/%d/%d, osdmap %d/%d %d/%d)\n", - CEPH_MONC_PROTOCOL, CEPH_MDSC_PROTOCOL, CEPH_OSDC_PROTOCOL, - CEPH_OSDMAP_VERSION, CEPH_OSDMAP_VERSION_EXT, - CEPH_OSDMAP_INC_VERSION, CEPH_OSDMAP_INC_VERSION_EXT); + pr_info("loaded (mds proto %d)\n", CEPH_MDSC_PROTOCOL); + return 0; out_icache: destroy_caches(); -out_msgr: - ceph_msgr_exit(); -out_debugfs: - ceph_debugfs_cleanup(); out: return ret; } @@ -1101,8 +893,6 @@ static void __exit exit_ceph(void) dout("exit_ceph\n"); unregister_filesystem(&ceph_fs_type); destroy_caches(); - ceph_msgr_exit(); - ceph_debugfs_cleanup(); } module_init(init_ceph); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index b87638e84c4..e2e904442ce 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -1,7 +1,7 @@ #ifndef _FS_CEPH_SUPER_H #define _FS_CEPH_SUPER_H -#include "ceph_debug.h" +#include #include #include @@ -14,13 +14,7 @@ #include #include -#include "types.h" -#include "messenger.h" -#include "msgpool.h" -#include "mon_client.h" -#include "mds_client.h" -#include "osd_client.h" -#include "ceph_fs.h" +#include /* f_type in struct statfs */ #define CEPH_SUPER_MAGIC 0x00c36400 @@ -30,42 +24,25 @@ #define CEPH_BLOCK_SHIFT 20 /* 1 MB */ #define CEPH_BLOCK (1 << CEPH_BLOCK_SHIFT) -/* - * Supported features - */ -#define CEPH_FEATURE_SUPPORTED CEPH_FEATURE_NOSRCADDR | CEPH_FEATURE_FLOCK -#define CEPH_FEATURE_REQUIRED CEPH_FEATURE_NOSRCADDR +#define CEPH_MOUNT_OPT_DIRSTAT (1<<4) /* `cat dirname` for stats */ +#define CEPH_MOUNT_OPT_RBYTES (1<<5) /* dir st_bytes = rbytes */ +#define CEPH_MOUNT_OPT_NOASYNCREADDIR (1<<7) /* no dcache readdir */ -/* - * mount options - */ -#define CEPH_OPT_FSID (1<<0) -#define CEPH_OPT_NOSHARE (1<<1) /* don't share client with other sbs */ -#define CEPH_OPT_MYIP (1<<2) /* specified my ip */ -#define CEPH_OPT_DIRSTAT (1<<4) /* funky `cat dirname` for stats */ -#define CEPH_OPT_RBYTES (1<<5) /* dir st_bytes = rbytes */ -#define CEPH_OPT_NOCRC (1<<6) /* no data crc on writes */ -#define CEPH_OPT_NOASYNCREADDIR (1<<7) /* no dcache readdir */ +#define CEPH_MOUNT_OPT_DEFAULT (CEPH_MOUNT_OPT_RBYTES) -#define CEPH_OPT_DEFAULT (CEPH_OPT_RBYTES) +#define ceph_set_mount_opt(fsc, opt) \ + (fsc)->mount_options->flags |= CEPH_MOUNT_OPT_##opt; +#define ceph_test_mount_opt(fsc, opt) \ + (!!((fsc)->mount_options->flags & CEPH_MOUNT_OPT_##opt)) -#define ceph_set_opt(client, opt) \ - (client)->mount_args->flags |= CEPH_OPT_##opt; -#define ceph_test_opt(client, opt) \ - (!!((client)->mount_args->flags & CEPH_OPT_##opt)) +#define CEPH_MAX_READDIR_DEFAULT 1024 +#define CEPH_MAX_READDIR_BYTES_DEFAULT (512*1024) +#define CEPH_SNAPDIRNAME_DEFAULT ".snap" - -struct ceph_mount_args { - int sb_flags; +struct ceph_mount_options { int flags; - struct ceph_fsid fsid; - struct ceph_entity_addr my_addr; - int num_mon; - struct ceph_entity_addr *mon_addr; - int mount_timeout; - int osd_idle_ttl; - int osd_timeout; - int osd_keepalive_timeout; + int sb_flags; + int wsize; int rsize; /* max readahead */ int congestion_kb; /* max writeback in flight */ @@ -73,82 +50,25 @@ struct ceph_mount_args { int cap_release_safety; int max_readdir; /* max readdir result (entires) */ int max_readdir_bytes; /* max readdir result (bytes) */ - char *snapdir_name; /* default ".snap" */ - char *name; - char *secret; -}; - -/* - * defaults - */ -#define CEPH_MOUNT_TIMEOUT_DEFAULT 60 -#define CEPH_OSD_TIMEOUT_DEFAULT 60 /* seconds */ -#define CEPH_OSD_KEEPALIVE_DEFAULT 5 -#define CEPH_OSD_IDLE_TTL_DEFAULT 60 -#define CEPH_MOUNT_RSIZE_DEFAULT (512*1024) /* readahead */ -#define CEPH_MAX_READDIR_DEFAULT 1024 -#define CEPH_MAX_READDIR_BYTES_DEFAULT (512*1024) - -#define CEPH_MSG_MAX_FRONT_LEN (16*1024*1024) -#define CEPH_MSG_MAX_DATA_LEN (16*1024*1024) - -#define CEPH_SNAPDIRNAME_DEFAULT ".snap" -#define CEPH_AUTH_NAME_DEFAULT "guest" -/* - * Delay telling the MDS we no longer want caps, in case we reopen - * the file. Delay a minimum amount of time, even if we send a cap - * message for some other reason. Otherwise, take the oppotunity to - * update the mds to avoid sending another message later. - */ -#define CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT 5 /* cap release delay */ -#define CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT 60 /* cap release delay */ - -#define CEPH_CAP_RELEASE_SAFETY_DEFAULT (CEPH_CAPS_PER_RELEASE * 4) - -/* mount state */ -enum { - CEPH_MOUNT_MOUNTING, - CEPH_MOUNT_MOUNTED, - CEPH_MOUNT_UNMOUNTING, - CEPH_MOUNT_UNMOUNTED, - CEPH_MOUNT_SHUTDOWN, -}; -/* - * subtract jiffies - */ -static inline unsigned long time_sub(unsigned long a, unsigned long b) -{ - BUG_ON(time_after(b, a)); - return (long)a - (long)b; -} - -/* - * per-filesystem client state - * - * possibly shared by multiple mount points, if they are - * mounting the same ceph filesystem/cluster. - */ -struct ceph_client { - struct ceph_fsid fsid; - bool have_fsid; + /* + * everything above this point can be memcmp'd; everything below + * is handled in compare_mount_options() + */ - struct mutex mount_mutex; /* serialize mount attempts */ - struct ceph_mount_args *mount_args; + char *snapdir_name; /* default ".snap" */ +}; +struct ceph_fs_client { struct super_block *sb; - unsigned long mount_state; - wait_queue_head_t auth_wq; - - int auth_err; + struct ceph_mount_options *mount_options; + struct ceph_client *client; + unsigned long mount_state; int min_caps; /* min caps i added */ - struct ceph_messenger *msgr; /* messenger instance */ - struct ceph_mon_client monc; - struct ceph_mds_client mdsc; - struct ceph_osd_client osdc; + struct ceph_mds_client *mdsc; /* writeback */ mempool_t *wb_pagevec_pool; @@ -160,14 +80,14 @@ struct ceph_client { struct backing_dev_info backing_dev_info; #ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_monmap; - struct dentry *debugfs_mdsmap, *debugfs_osdmap; - struct dentry *debugfs_dir, *debugfs_dentry_lru, *debugfs_caps; + struct dentry *debugfs_dentry_lru, *debugfs_caps; struct dentry *debugfs_congestion_kb; struct dentry *debugfs_bdi; + struct dentry *debugfs_mdsc, *debugfs_mdsmap; #endif }; + /* * File i/o capability. This tracks shared state with the metadata * server that allows us to cache or writeback attributes or to read @@ -275,6 +195,20 @@ struct ceph_inode_xattr { int should_free_val; }; +/* + * Ceph dentry state + */ +struct ceph_dentry_info { + struct ceph_mds_session *lease_session; + u32 lease_gen, lease_shared_gen; + u32 lease_seq; + unsigned long lease_renew_after, lease_renew_from; + struct list_head lru; + struct dentry *dentry; + u64 time; + u64 offset; +}; + struct ceph_inode_xattrs_info { /* * (still encoded) xattr blob. we avoid the overhead of parsing @@ -296,11 +230,6 @@ struct ceph_inode_xattrs_info { /* * Ceph inode. */ -#define CEPH_I_COMPLETE 1 /* we have complete directory cached */ -#define CEPH_I_NODELAY 4 /* do not delay cap release */ -#define CEPH_I_FLUSH 8 /* do not delay flush of dirty metadata */ -#define CEPH_I_NOFLUSH 16 /* do not flush dirty caps */ - struct ceph_inode_info { struct ceph_vino i_vino; /* ceph ino + snap */ @@ -391,6 +320,63 @@ static inline struct ceph_inode_info *ceph_inode(struct inode *inode) return container_of(inode, struct ceph_inode_info, vfs_inode); } +static inline struct ceph_vino ceph_vino(struct inode *inode) +{ + return ceph_inode(inode)->i_vino; +} + +/* + * ino_t is <64 bits on many architectures, blech. + * + * don't include snap in ino hash, at least for now. + */ +static inline ino_t ceph_vino_to_ino(struct ceph_vino vino) +{ + ino_t ino = (ino_t)vino.ino; /* ^ (vino.snap << 20); */ +#if BITS_PER_LONG == 32 + ino ^= vino.ino >> (sizeof(u64)-sizeof(ino_t)) * 8; + if (!ino) + ino = 1; +#endif + return ino; +} + +/* for printf-style formatting */ +#define ceph_vinop(i) ceph_inode(i)->i_vino.ino, ceph_inode(i)->i_vino.snap + +static inline u64 ceph_ino(struct inode *inode) +{ + return ceph_inode(inode)->i_vino.ino; +} +static inline u64 ceph_snap(struct inode *inode) +{ + return ceph_inode(inode)->i_vino.snap; +} + +static inline int ceph_ino_compare(struct inode *inode, void *data) +{ + struct ceph_vino *pvino = (struct ceph_vino *)data; + struct ceph_inode_info *ci = ceph_inode(inode); + return ci->i_vino.ino == pvino->ino && + ci->i_vino.snap == pvino->snap; +} + +static inline struct inode *ceph_find_inode(struct super_block *sb, + struct ceph_vino vino) +{ + ino_t t = ceph_vino_to_ino(vino); + return ilookup5(sb, t, ceph_ino_compare, &vino); +} + + +/* + * Ceph inode. + */ +#define CEPH_I_COMPLETE 1 /* we have complete directory cached */ +#define CEPH_I_NODELAY 4 /* do not delay cap release */ +#define CEPH_I_FLUSH 8 /* do not delay flush of dirty metadata */ +#define CEPH_I_NOFLUSH 16 /* do not flush dirty caps */ + static inline void ceph_i_clear(struct inode *inode, unsigned mask) { struct ceph_inode_info *ci = ceph_inode(inode); @@ -432,20 +418,6 @@ extern u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v, struct ceph_inode_frag *pfrag, int *found); -/* - * Ceph dentry state - */ -struct ceph_dentry_info { - struct ceph_mds_session *lease_session; - u32 lease_gen, lease_shared_gen; - u32 lease_seq; - unsigned long lease_renew_after, lease_renew_from; - struct list_head lru; - struct dentry *dentry; - u64 time; - u64 offset; -}; - static inline struct ceph_dentry_info *ceph_dentry(struct dentry *dentry) { return (struct ceph_dentry_info *)dentry->d_fsdata; @@ -456,22 +428,6 @@ static inline loff_t ceph_make_fpos(unsigned frag, unsigned off) return ((loff_t)frag << 32) | (loff_t)off; } -/* - * ino_t is <64 bits on many architectures, blech. - * - * don't include snap in ino hash, at least for now. - */ -static inline ino_t ceph_vino_to_ino(struct ceph_vino vino) -{ - ino_t ino = (ino_t)vino.ino; /* ^ (vino.snap << 20); */ -#if BITS_PER_LONG == 32 - ino ^= vino.ino >> (sizeof(u64)-sizeof(ino_t)) * 8; - if (!ino) - ino = 1; -#endif - return ino; -} - static inline int ceph_set_ino_cb(struct inode *inode, void *data) { ceph_inode(inode)->i_vino = *(struct ceph_vino *)data; @@ -479,39 +435,6 @@ static inline int ceph_set_ino_cb(struct inode *inode, void *data) return 0; } -static inline struct ceph_vino ceph_vino(struct inode *inode) -{ - return ceph_inode(inode)->i_vino; -} - -/* for printf-style formatting */ -#define ceph_vinop(i) ceph_inode(i)->i_vino.ino, ceph_inode(i)->i_vino.snap - -static inline u64 ceph_ino(struct inode *inode) -{ - return ceph_inode(inode)->i_vino.ino; -} -static inline u64 ceph_snap(struct inode *inode) -{ - return ceph_inode(inode)->i_vino.snap; -} - -static inline int ceph_ino_compare(struct inode *inode, void *data) -{ - struct ceph_vino *pvino = (struct ceph_vino *)data; - struct ceph_inode_info *ci = ceph_inode(inode); - return ci->i_vino.ino == pvino->ino && - ci->i_vino.snap == pvino->snap; -} - -static inline struct inode *ceph_find_inode(struct super_block *sb, - struct ceph_vino vino) -{ - ino_t t = ceph_vino_to_ino(vino); - return ilookup5(sb, t, ceph_ino_compare, &vino); -} - - /* * caps helpers */ @@ -576,18 +499,18 @@ extern int ceph_reserve_caps(struct ceph_mds_client *mdsc, struct ceph_cap_reservation *ctx, int need); extern int ceph_unreserve_caps(struct ceph_mds_client *mdsc, struct ceph_cap_reservation *ctx); -extern void ceph_reservation_status(struct ceph_client *client, +extern void ceph_reservation_status(struct ceph_fs_client *client, int *total, int *avail, int *used, int *reserved, int *min); -static inline struct ceph_client *ceph_inode_to_client(struct inode *inode) +static inline struct ceph_fs_client *ceph_inode_to_client(struct inode *inode) { - return (struct ceph_client *)inode->i_sb->s_fs_info; + return (struct ceph_fs_client *)inode->i_sb->s_fs_info; } -static inline struct ceph_client *ceph_sb_to_client(struct super_block *sb) +static inline struct ceph_fs_client *ceph_sb_to_client(struct super_block *sb) { - return (struct ceph_client *)sb->s_fs_info; + return (struct ceph_fs_client *)sb->s_fs_info; } @@ -616,51 +539,6 @@ struct ceph_file_info { -/* - * snapshots - */ - -/* - * A "snap context" is the set of existing snapshots when we - * write data. It is used by the OSD to guide its COW behavior. - * - * The ceph_snap_context is refcounted, and attached to each dirty - * page, indicating which context the dirty data belonged when it was - * dirtied. - */ -struct ceph_snap_context { - atomic_t nref; - u64 seq; - int num_snaps; - u64 snaps[]; -}; - -static inline struct ceph_snap_context * -ceph_get_snap_context(struct ceph_snap_context *sc) -{ - /* - printk("get_snap_context %p %d -> %d\n", sc, atomic_read(&sc->nref), - atomic_read(&sc->nref)+1); - */ - if (sc) - atomic_inc(&sc->nref); - return sc; -} - -static inline void ceph_put_snap_context(struct ceph_snap_context *sc) -{ - if (!sc) - return; - /* - printk("put_snap_context %p %d -> %d\n", sc, atomic_read(&sc->nref), - atomic_read(&sc->nref)-1); - */ - if (atomic_dec_and_test(&sc->nref)) { - /*printk(" deleting snap_context %p\n", sc);*/ - kfree(sc); - } -} - /* * A "snap realm" describes a subset of the file hierarchy sharing * the same set of snapshots that apply to it. The realms themselves @@ -699,16 +577,33 @@ struct ceph_snap_realm { spinlock_t inodes_with_caps_lock; }; - - -/* - * calculate the number of pages a given length and offset map onto, - * if we align the data. - */ -static inline int calc_pages_for(u64 off, u64 len) +static inline int default_congestion_kb(void) { - return ((off+len+PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT) - - (off >> PAGE_CACHE_SHIFT); + int congestion_kb; + + /* + * Copied from NFS + * + * congestion size, scale with available memory. + * + * 64MB: 8192k + * 128MB: 11585k + * 256MB: 16384k + * 512MB: 23170k + * 1GB: 32768k + * 2GB: 46340k + * 4GB: 65536k + * 8GB: 92681k + * 16GB: 131072k + * + * This allows larger machines to have larger/more transfers. + * Limit the default to 256M + */ + congestion_kb = (16*int_sqrt(totalram_pages)) << (PAGE_SHIFT-10); + if (congestion_kb > 256*1024) + congestion_kb = 256*1024; + + return congestion_kb; } @@ -741,16 +636,6 @@ static inline bool __ceph_have_pending_cap_snap(struct ceph_inode_info *ci) ci_item)->writing; } - -/* super.c */ -extern struct kmem_cache *ceph_inode_cachep; -extern struct kmem_cache *ceph_cap_cachep; -extern struct kmem_cache *ceph_dentry_cachep; -extern struct kmem_cache *ceph_file_cachep; - -extern const char *ceph_msg_type_name(int type); -extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid); - /* inode.c */ extern const struct inode_operations ceph_file_iops; @@ -857,12 +742,18 @@ extern int ceph_mmap(struct file *file, struct vm_area_struct *vma); /* file.c */ extern const struct file_operations ceph_file_fops; extern const struct address_space_operations ceph_aops; +extern int ceph_copy_to_page_vector(struct page **pages, + const char *data, + loff_t off, size_t len); +extern int ceph_copy_from_page_vector(struct page **pages, + char *data, + loff_t off, size_t len); +extern struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags); extern int ceph_open(struct inode *inode, struct file *file); extern struct dentry *ceph_lookup_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd, int mode, int locked_dir); extern int ceph_release(struct inode *inode, struct file *filp); -extern void ceph_release_page_vector(struct page **pages, int num_pages); /* dir.c */ extern const struct file_operations ceph_dir_fops; @@ -892,12 +783,6 @@ extern long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg); /* export.c */ extern const struct export_operations ceph_export_ops; -/* debugfs.c */ -extern int ceph_debugfs_init(void); -extern void ceph_debugfs_cleanup(void); -extern int ceph_debugfs_client_init(struct ceph_client *client); -extern void ceph_debugfs_client_cleanup(struct ceph_client *client); - /* locks.c */ extern int ceph_lock(struct file *file, int cmd, struct file_lock *fl); extern int ceph_flock(struct file *file, int cmd, struct file_lock *fl); @@ -914,4 +799,8 @@ static inline struct inode *get_dentry_parent_inode(struct dentry *dentry) return NULL; } +/* debugfs.c */ +extern int ceph_fs_debugfs_init(struct ceph_fs_client *client); +extern void ceph_fs_debugfs_cleanup(struct ceph_fs_client *client); + #endif /* _FS_CEPH_SUPER_H */ diff --git a/fs/ceph/types.h b/fs/ceph/types.h deleted file mode 100644 index 28b35a005ec..00000000000 --- a/fs/ceph/types.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _FS_CEPH_TYPES_H -#define _FS_CEPH_TYPES_H - -/* needed before including ceph_fs.h */ -#include -#include -#include -#include - -#include "ceph_fs.h" -#include "ceph_frag.h" -#include "ceph_hash.h" - -/* - * Identify inodes by both their ino AND snapshot id (a u64). - */ -struct ceph_vino { - u64 ino; - u64 snap; -}; - - -/* context for the caps reservation mechanism */ -struct ceph_cap_reservation { - int count; -}; - - -#endif diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 9578af610b7..70e919960ac 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -1,6 +1,9 @@ -#include "ceph_debug.h" +#include + #include "super.h" -#include "decode.h" +#include "mds_client.h" + +#include #include #include @@ -620,12 +623,12 @@ out: static int ceph_sync_setxattr(struct dentry *dentry, const char *name, const char *value, size_t size, int flags) { - struct ceph_client *client = ceph_sb_to_client(dentry->d_sb); + struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); struct inode *inode = dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); struct inode *parent_inode = dentry->d_parent->d_inode; struct ceph_mds_request *req; - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_mds_client *mdsc = fsc->mdsc; int err; int i, nr_pages; struct page **pages = NULL; @@ -777,8 +780,8 @@ out: static int ceph_send_removexattr(struct dentry *dentry, const char *name) { - struct ceph_client *client = ceph_sb_to_client(dentry->d_sb); - struct ceph_mds_client *mdsc = &client->mdsc; + struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; struct inode *inode = dentry->d_inode; struct inode *parent_inode = dentry->d_parent->d_inode; struct ceph_mds_request *req; -- cgit v1.2.3-18-g5258 From 4c32f5dda5ffe23687a55da1538b7cc426710d1a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 24 Aug 2010 16:27:36 -0700 Subject: ceph: do not hide .snap in root directory Snaps in the root directory are now supported by the MDS, and harmless on older versions. Signed-off-by: Sage Weil --- fs/ceph/dir.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index a8d2aacc612..495825268cb 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -493,7 +493,6 @@ struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, /* .snap dir? */ if (err == -ENOENT && - ceph_vino(parent).ino != CEPH_INO_ROOT && /* no .snap in root dir */ strcmp(dentry->d_name.name, fsc->mount_options->snapdir_name) == 0) { struct inode *inode = ceph_get_snapdir(parent); -- cgit v1.2.3-18-g5258 From 93afd449aa3c0430ef409c13e1cb2b3f0458fc10 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 17 Sep 2010 08:38:25 -0700 Subject: ceph: only invalidate on check_caps if we actually have pages The i_rdcache_gen value only implies we MAY have cached pages; actually check the mapping to see if it's worth bothering with an invalidate. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 3cff67cbb9c..3c03460f48b 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1534,7 +1534,7 @@ retry_locked: */ if ((!is_delayed || mdsc->stopping) && ci->i_wrbuffer_ref == 0 && /* no dirty pages... */ - ci->i_rdcache_gen && /* may have cached pages */ + inode->i_data.nrpages && /* have cached pages */ (file_wanted == 0 || /* no open files */ (revoking & (CEPH_CAP_FILE_CACHE| CEPH_CAP_FILE_LAZYIO))) && /* or revoking cache */ -- cgit v1.2.3-18-g5258 From 18a38193efcaac1fb3c94ad8fa04bb117850a3c2 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 17 Sep 2010 10:46:44 -0700 Subject: ceph: use mapping->nrpages to determine if mapping is empty This is simpler and faster. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 3c03460f48b..98ab13e2b71 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1417,17 +1417,6 @@ static int __mark_caps_flushing(struct inode *inode, /* * try to invalidate mapping pages without blocking. */ -static int mapping_is_empty(struct address_space *mapping) -{ - struct page *page = find_get_page(mapping, 0); - - if (!page) - return 1; - - put_page(page); - return 0; -} - static int try_nonblocking_invalidate(struct inode *inode) { struct ceph_inode_info *ci = ceph_inode(inode); @@ -1437,7 +1426,7 @@ static int try_nonblocking_invalidate(struct inode *inode) invalidate_mapping_pages(&inode->i_data, 0, -1); spin_lock(&inode->i_lock); - if (mapping_is_empty(&inode->i_data) && + if (inode->i_data.nrpages == 0 && invalidating_gen == ci->i_rdcache_gen) { /* success. */ dout("try_nonblocking_invalidate %p success\n", inode); -- cgit v1.2.3-18-g5258 From fca4451acfdcf894154e4809529ca28a09db88ff Mon Sep 17 00:00:00 2001 From: Greg Farnum Date: Fri, 17 Sep 2010 10:24:02 -0700 Subject: ceph: preallocate flock state without locks held When the lock_kernel() turns into lock_flocks() and a spinlock, we won't be able to do allocations with the lock held. Preallocate space without the lock, and retry if the lock state changes out from underneath us. Signed-off-by: Greg Farnum Signed-off-by: Sage Weil --- fs/ceph/locks.c | 17 +++++++++++++++-- fs/ceph/mds_client.c | 42 +++++++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c index 087afe9ca36..40abde93c34 100644 --- a/fs/ceph/locks.c +++ b/fs/ceph/locks.c @@ -181,8 +181,9 @@ void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count) * Encode the flock and fcntl locks for the given inode into the pagelist. * Format is: #fcntl locks, sequential fcntl locks, #flock locks, * sequential flock locks. - * Must be called with BLK already held, and the lock numbers should have - * been gathered under the same lock holding window. + * Must be called with lock_flocks() already held. + * If we encounter more of a specific lock type than expected, + * we return the value 1. */ int ceph_encode_locks(struct inode *inode, struct ceph_pagelist *pagelist, int num_fcntl_locks, int num_flock_locks) @@ -190,6 +191,8 @@ int ceph_encode_locks(struct inode *inode, struct ceph_pagelist *pagelist, struct file_lock *lock; struct ceph_filelock cephlock; int err = 0; + int seen_fcntl = 0; + int seen_flock = 0; dout("encoding %d flock and %d fcntl locks", num_flock_locks, num_fcntl_locks); @@ -198,6 +201,11 @@ int ceph_encode_locks(struct inode *inode, struct ceph_pagelist *pagelist, goto fail; for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) { if (lock->fl_flags & FL_POSIX) { + ++seen_fcntl; + if (seen_fcntl > num_fcntl_locks) { + err = -ENOSPC; + goto fail; + } err = lock_to_ceph_filelock(lock, &cephlock); if (err) goto fail; @@ -213,6 +221,11 @@ int ceph_encode_locks(struct inode *inode, struct ceph_pagelist *pagelist, goto fail; for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) { if (lock->fl_flags & FL_FLOCK) { + ++seen_flock; + if (seen_flock > num_flock_locks) { + err = -ENOSPC; + goto fail; + } err = lock_to_ceph_filelock(lock, &cephlock); if (err) goto fail; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 33568239a08..fbfc298ac55 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2365,19 +2365,35 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, if (recon_state->flock) { int num_fcntl_locks, num_flock_locks; - - lock_kernel(); - ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks); - rec.v2.flock_len = (2*sizeof(u32) + - (num_fcntl_locks+num_flock_locks) * - sizeof(struct ceph_filelock)); - - err = ceph_pagelist_append(pagelist, &rec, reclen); - if (!err) - err = ceph_encode_locks(inode, pagelist, - num_fcntl_locks, - num_flock_locks); - unlock_kernel(); + struct ceph_pagelist_cursor trunc_point; + + ceph_pagelist_set_cursor(pagelist, &trunc_point); + do { + lock_kernel(); + ceph_count_locks(inode, &num_fcntl_locks, + &num_flock_locks); + rec.v2.flock_len = (2*sizeof(u32) + + (num_fcntl_locks+num_flock_locks) * + sizeof(struct ceph_filelock)); + unlock_kernel(); + + /* pre-alloc pagelist */ + ceph_pagelist_truncate(pagelist, &trunc_point); + err = ceph_pagelist_append(pagelist, &rec, reclen); + if (!err) + err = ceph_pagelist_reserve(pagelist, + rec.v2.flock_len); + + /* encode locks */ + if (!err) { + lock_kernel(); + err = ceph_encode_locks(inode, + pagelist, + num_fcntl_locks, + num_flock_locks); + unlock_kernel(); + } + } while (err == -ENOSPC); } else { err = ceph_pagelist_append(pagelist, &rec, reclen); } -- cgit v1.2.3-18-g5258 From 496e59553c51ce18acc836de070106b583926b87 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 22 Sep 2010 19:57:10 -0700 Subject: ceph: switch from BKL to lock_flocks() Switch from using the BKL explicitly to the new lock_flocks() interface. Eventually this will turn into a spinlock. Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index fbfc298ac55..3142b15940c 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1,11 +1,12 @@ #include +#include #include #include #include -#include #include #include +#include #include "super.h" #include "mds_client.h" @@ -2369,13 +2370,13 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, ceph_pagelist_set_cursor(pagelist, &trunc_point); do { - lock_kernel(); + lock_flocks(); ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks); rec.v2.flock_len = (2*sizeof(u32) + (num_fcntl_locks+num_flock_locks) * sizeof(struct ceph_filelock)); - unlock_kernel(); + unlock_flocks(); /* pre-alloc pagelist */ ceph_pagelist_truncate(pagelist, &trunc_point); @@ -2386,12 +2387,12 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, /* encode locks */ if (!err) { - lock_kernel(); + lock_flocks(); err = ceph_encode_locks(inode, pagelist, num_fcntl_locks, num_flock_locks); - unlock_kernel(); + unlock_flocks(); } } while (err == -ENOSPC); } else { -- cgit v1.2.3-18-g5258 From 6f453ed6c07dbed83b368269c9c0fb170866ee71 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 28 Sep 2010 09:53:10 -0700 Subject: ceph: fix debugfs warnings Include "super.h" outside of CONFIG_DEBUG_FS to eliminate a compiler warning: fs/ceph/debugfs.c:266: warning: 'struct ceph_fs_client' declared inside parameter list fs/ceph/debugfs.c:266: warning: its scope is only this definition or declaration, which is probably not what you want fs/ceph/debugfs.c:271: warning: 'struct ceph_fs_client' declared inside parameter list Signed-off-by: Randy Dunlap Signed-off-by: Yehuda Sadeh --- fs/ceph/debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 94fe1d284c3..7ae1b3d55b5 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -12,9 +12,10 @@ #include #include +#include "super.h" + #ifdef CONFIG_DEBUG_FS -#include "super.h" #include "mds_client.h" static int mdsmap_show(struct seq_file *s, void *p) -- cgit v1.2.3-18-g5258 From 571dba52a34015a5a7aa5d480a86936878444a6f Mon Sep 17 00:00:00 2001 From: Greg Farnum Date: Fri, 24 Sep 2010 14:56:40 -0700 Subject: ceph: add CEPH_MDS_OP_SETDIRLAYOUT and associated ioctl. Signed-off-by: Sage Weil --- fs/ceph/ioctl.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ceph/ioctl.h | 4 +++- 2 files changed, 69 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index 899578b0c46..8888c9ba68d 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -91,6 +91,68 @@ static long ceph_ioctl_set_layout(struct file *file, void __user *arg) return err; } +/* + * Set a layout policy on a directory inode. All items in the tree + * rooted at this inode will inherit this layout on creation, + * (It doesn't apply retroactively ) + * unless a subdirectory has its own layout policy. + */ +static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg) +{ + struct inode *inode = file->f_dentry->d_inode; + struct ceph_mds_request *req; + struct ceph_ioctl_layout l; + int err, i; + struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; + + /* copy and validate */ + if (copy_from_user(&l, arg, sizeof(l))) + return -EFAULT; + + if ((l.object_size & ~PAGE_MASK) || + (l.stripe_unit & ~PAGE_MASK) || + !l.stripe_unit || + (l.object_size && + (unsigned)l.object_size % (unsigned)l.stripe_unit)) + return -EINVAL; + + /* make sure it's a valid data pool */ + if (l.data_pool > 0) { + mutex_lock(&mdsc->mutex); + err = -EINVAL; + for (i = 0; i < mdsc->mdsmap->m_num_data_pg_pools; i++) + if (mdsc->mdsmap->m_data_pg_pools[i] == l.data_pool) { + err = 0; + break; + } + mutex_unlock(&mdsc->mutex); + if (err) + return err; + } + + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETDIRLAYOUT, + USE_AUTH_MDS); + + if (IS_ERR(req)) + return PTR_ERR(req); + req->r_inode = igrab(inode); + + req->r_args.setlayout.layout.fl_stripe_unit = + cpu_to_le32(l.stripe_unit); + req->r_args.setlayout.layout.fl_stripe_count = + cpu_to_le32(l.stripe_count); + req->r_args.setlayout.layout.fl_object_size = + cpu_to_le32(l.object_size); + req->r_args.setlayout.layout.fl_pg_pool = + cpu_to_le32(l.data_pool); + req->r_args.setlayout.layout.fl_pg_preferred = + cpu_to_le32(l.preferred_osd); + + err = ceph_mdsc_do_request(mdsc, inode, req); + ceph_mdsc_put_request(req); + return err; +} + /* * Return object name, size/offset information, and location (OSD * number, network address) for a given file offset. @@ -177,11 +239,15 @@ long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case CEPH_IOC_SET_LAYOUT: return ceph_ioctl_set_layout(file, (void __user *)arg); + case CEPH_IOC_SET_LAYOUT_POLICY: + return ceph_ioctl_set_layout_policy(file, (void __user *)arg); + case CEPH_IOC_GET_DATALOC: return ceph_ioctl_get_dataloc(file, (void __user *)arg); case CEPH_IOC_LAZYIO: return ceph_ioctl_lazyio(file); } + return -ENOTTY; } diff --git a/fs/ceph/ioctl.h b/fs/ceph/ioctl.h index 88451a3b685..a6ce54e94eb 100644 --- a/fs/ceph/ioctl.h +++ b/fs/ceph/ioctl.h @@ -4,7 +4,7 @@ #include #include -#define CEPH_IOCTL_MAGIC 0x97 +#define CEPH_IOCTL_MAGIC 0x98 /* just use u64 to align sanely on all archs */ struct ceph_ioctl_layout { @@ -17,6 +17,8 @@ struct ceph_ioctl_layout { struct ceph_ioctl_layout) #define CEPH_IOC_SET_LAYOUT _IOW(CEPH_IOCTL_MAGIC, 2, \ struct ceph_ioctl_layout) +#define CEPH_IOC_SET_LAYOUT_POLICY _IOW(CEPH_IOCTL_MAGIC, 5, \ + struct ceph_ioctl_layout) /* * Extract identity, address of the OSD and object storing a given -- cgit v1.2.3-18-g5258 From 61413c2f594e6b63db2b14c70c2e7d8cf02f9c00 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 17 Oct 2010 21:55:21 +0200 Subject: fs/ceph/xattr.c: Use kmemdup Convert a sequence of kmalloc and memcpy to use kmemdup. The semantic patch that performs this transformation is: (http://coccinelle.lip6.fr/) // @@ expression a,flag,len; expression arg,e1,e2; statement S; @@ a = - \(kmalloc\|kzalloc\)(len,flag) + kmemdup(arg,len,flag) <... when != a if (a == NULL || ...) S ...> - memcpy(a,arg,len+1); // Signed-off-by: Julia Lawall Signed-off-by: Sage Weil --- fs/ceph/xattr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 70e919960ac..6e12a6ba5f7 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -716,10 +716,9 @@ int ceph_setxattr(struct dentry *dentry, const char *name, /* preallocate memory for xattr name, value, index node */ err = -ENOMEM; - newname = kmalloc(name_len + 1, GFP_NOFS); + newname = kmemdup(name, name_len + 1, GFP_NOFS); if (!newname) goto out; - memcpy(newname, name, name_len + 1); if (val_len) { newval = kmalloc(val_len + 1, GFP_NOFS); -- cgit v1.2.3-18-g5258 From efa4c1206eaff047c474af2136748a58eb8cc33b Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 18 Oct 2010 14:04:31 -0700 Subject: ceph: do not carry i_lock for readdir from dcache We were taking dcache_lock inside of i_lock, which introduces a dependency not found elsewhere in the kernel, complicationg the vfs locking scalability work. Since we don't actually need it here anyway, remove it. We only need i_lock to test for the I_COMPLETE flag, so be careful to do so without dcache_lock held. Signed-off-by: Sage Weil --- fs/ceph/dir.c | 41 ++++++++++++++++------------------------- fs/ceph/super.h | 3 ++- 2 files changed, 18 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 495825268cb..e0a2dc6fcaf 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -95,10 +95,7 @@ static unsigned fpos_off(loff_t p) */ static int __dcache_readdir(struct file *filp, void *dirent, filldir_t filldir) - __releases(inode->i_lock) - __acquires(inode->i_lock) { - struct inode *inode = filp->f_dentry->d_inode; struct ceph_file_info *fi = filp->private_data; struct dentry *parent = filp->f_dentry; struct inode *dir = parent->d_inode; @@ -154,7 +151,6 @@ more: atomic_inc(&dentry->d_count); spin_unlock(&dcache_lock); - spin_unlock(&inode->i_lock); dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos, dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); @@ -172,35 +168,30 @@ more: } else { dput(last); } - last = NULL; } - - spin_lock(&inode->i_lock); - spin_lock(&dcache_lock); - last = dentry; if (err < 0) - goto out_unlock; + goto out; - p = p->prev; filp->f_pos++; /* make sure a dentry wasn't dropped while we didn't have dcache_lock */ - if ((ceph_inode(dir)->i_ceph_flags & CEPH_I_COMPLETE)) - goto more; - dout(" lost I_COMPLETE on %p; falling back to mds\n", dir); - err = -EAGAIN; + if (!ceph_i_test(dir, CEPH_I_COMPLETE)) { + dout(" lost I_COMPLETE on %p; falling back to mds\n", dir); + err = -EAGAIN; + goto out; + } + + spin_lock(&dcache_lock); + p = p->prev; /* advance to next dentry */ + goto more; out_unlock: spin_unlock(&dcache_lock); - - if (last) { - spin_unlock(&inode->i_lock); +out: + if (last) dput(last); - spin_lock(&inode->i_lock); - } - return err; } @@ -272,13 +263,13 @@ static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir) ceph_snap(inode) != CEPH_SNAPDIR && (ci->i_ceph_flags & CEPH_I_COMPLETE) && __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) { + spin_unlock(&inode->i_lock); err = __dcache_readdir(filp, dirent, filldir); - if (err != -EAGAIN) { - spin_unlock(&inode->i_lock); + if (err != -EAGAIN) return err; - } + } else { + spin_unlock(&inode->i_lock); } - spin_unlock(&inode->i_lock); if (fi->dentry) { err = note_last_dentry(fi, fi->dentry->d_name.name, fi->dentry->d_name.len); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index e2e904442ce..1886294e12f 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -400,8 +400,9 @@ static inline bool ceph_i_test(struct inode *inode, unsigned mask) struct ceph_inode_info *ci = ceph_inode(inode); bool r; - smp_mb(); + spin_lock(&inode->i_lock); r = (ci->i_ceph_flags & mask) == mask; + spin_unlock(&inode->i_lock); return r; } -- cgit v1.2.3-18-g5258 From 39037559e651c417fb68b828926dc61cd5d6e5e2 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 18 Oct 2010 08:28:50 +0300 Subject: UBIFS: remove a bit of unneeded code This is a clean-up patch which: 1. Removes explicite 'hrtimer_cancel()' after 'ubifs_wbuf_sync()' in 'ubifs_remount_ro()', because the timers will be canceled by 'ubifs_wbuf_sync()', no need to cancel them for the second time. 2. Remove "if (c->jheads)" check from 'ubifs_put_super()', because at journal heads must always be allocated there, since we checked earlier that we were mounted R/W, and the olny situation when journal heads are not allocated is when mounter or re-mounted R/O. Signed-off-by: Artem Bityutskiy --- fs/ubifs/super.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index bb6ed5da2d1..6ff2f802bd7 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1686,10 +1686,8 @@ static void ubifs_remount_ro(struct ubifs_info *c) dbg_save_space_info(c); - for (i = 0; i < c->jhead_cnt; i++) { + for (i = 0; i < c->jhead_cnt; i++) ubifs_wbuf_sync(&c->jheads[i].wbuf); - hrtimer_cancel(&c->jheads[i].wbuf.timer); - } c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY); c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); @@ -1754,9 +1752,8 @@ static void ubifs_put_super(struct super_block *sb) int err; /* Synchronize write-buffers */ - if (c->jheads) - for (i = 0; i < c->jhead_cnt; i++) - ubifs_wbuf_sync(&c->jheads[i].wbuf); + for (i = 0; i < c->jhead_cnt; i++) + ubifs_wbuf_sync(&c->jheads[i].wbuf); /* * We are being cleanly unmounted which means the -- cgit v1.2.3-18-g5258 From 3601ba27353a968df843454e4b81155376682505 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 18 Oct 2010 08:32:35 +0300 Subject: UBIFS: do not forget to cancel timers This is a bug-fix: when we unmount, and we are currently in R/O mode because of an error - we do not sync write-buffers, which means we also do not cancel write-buffer timers we may possibly have armed. This patch fixes the issue. The issue can easily be reproduced by enabling UBIFS failure debug mode (echo 4 > /sys/module/ubifs/parameters/debug_tsts) and unmounting as soon as a failure happen. At some point the system oopses because we have an armed hrtimer but UBIFS is unmounted already. Signed-off-by: Artem Bityutskiy --- fs/ubifs/super.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 6ff2f802bd7..9a47c9f0ad0 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1772,6 +1772,10 @@ static void ubifs_put_super(struct super_block *sb) */ ubifs_err("failed to write master node, " "error %d", err); + } else { + for (i = 0; i < c->jhead_cnt; i++) + /* Make sure write-buffer timers are canceled */ + hrtimer_cancel(&c->jheads[i].wbuf.timer); } } -- cgit v1.2.3-18-g5258 From 6599fcbd01baf9d57e847db103d215ea4ec088f9 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 18 Oct 2010 10:00:40 +0300 Subject: UBIFS: do not allocate unneeded scan buffer In 'ubifs_replay_journal()' we allocate 'sbuf' for scanning the log. However, we already have 'c->sbuf' for these purposes, so do not allocate yet another one. This reduces UBIFS memory consumption while recovering. Signed-off-by: Artem Bityutskiy --- fs/ubifs/replay.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index 7df04ba4878..eed0fcff8d7 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -1013,7 +1013,6 @@ out: int ubifs_replay_journal(struct ubifs_info *c) { int err, i, lnum, offs, free; - void *sbuf = NULL; BUILD_BUG_ON(UBIFS_TRUN_KEY > 5); @@ -1028,10 +1027,6 @@ int ubifs_replay_journal(struct ubifs_info *c) return -EINVAL; } - sbuf = vmalloc(c->leb_size); - if (!sbuf) - return -ENOMEM; - dbg_mnt("start replaying the journal"); c->replaying = 1; lnum = c->ltail_lnum = c->lhead_lnum; @@ -1046,7 +1041,7 @@ int ubifs_replay_journal(struct ubifs_info *c) lnum = UBIFS_LOG_LNUM; offs = 0; } - err = replay_log_leb(c, lnum, offs, sbuf); + err = replay_log_leb(c, lnum, offs, c->sbuf); if (err == 1) /* We hit the end of the log */ break; @@ -1079,7 +1074,6 @@ int ubifs_replay_journal(struct ubifs_info *c) out: destroy_replay_tree(c); destroy_bud_list(c); - vfree(sbuf); c->replaying = 0; return err; } -- cgit v1.2.3-18-g5258 From e5953cbdff26f7cbae7eff30cd9b18c4e19b7594 Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Thu, 21 Oct 2010 14:56:00 +0200 Subject: pipe: fix failure to return error code on ->confirm() The arguments were transposed, we want to assign the error code to 'ret', which is being returned. Signed-off-by: Nicolas Kaiser Cc: stable@kernel.org Signed-off-by: Jens Axboe --- fs/pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/pipe.c b/fs/pipe.c index 279eef96c51..37eb1ebeaa9 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -382,7 +382,7 @@ pipe_read(struct kiocb *iocb, const struct iovec *_iov, error = ops->confirm(pipe, buf); if (error) { if (!ret) - error = ret; + ret = error; break; } -- cgit v1.2.3-18-g5258 From 3f9bcca7820a6711307b6499952b13cfcfc31dd6 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 18 Oct 2010 23:29:37 +0530 Subject: cifs: convert cifs_tcp_ses_lock from a rwlock to a spinlock cifs_tcp_ses_lock is a rwlock with protects the cifs_tcp_ses_list, server->smb_ses_list and the ses->tcon_list. It also protects a few ref counters in server, ses and tcon. In most cases the critical section doesn't seem to be large, in a few cases where it is slightly large, there seem to be really no benefit from concurrent access. I briefly considered RCU mechanism but it appears to me that there is no real need. Replace it with a spinlock and get rid of the last rwlock in the cifs code. Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 12 ++++----- fs/cifs/cifsfs.c | 8 +++--- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifssmb.c | 6 ++--- fs/cifs/connect.c | 70 ++++++++++++++++++++++++++-------------------------- fs/cifs/misc.c | 14 +++++------ fs/cifs/sess.c | 4 +-- 7 files changed, 58 insertions(+), 58 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index eb1ba493489..103ab8b605b 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -148,7 +148,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) seq_printf(m, "Servers:"); i = 0; - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp1, &cifs_tcp_ses_list) { server = list_entry(tmp1, struct TCP_Server_Info, tcp_ses_list); @@ -230,7 +230,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) spin_unlock(&GlobalMid_Lock); } } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); seq_putc(m, '\n'); /* BB add code to dump additional info such as TCP session info now */ @@ -270,7 +270,7 @@ static ssize_t cifs_stats_proc_write(struct file *file, atomic_set(&totBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0); #endif /* CONFIG_CIFS_STATS2 */ - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp1, &cifs_tcp_ses_list) { server = list_entry(tmp1, struct TCP_Server_Info, tcp_ses_list); @@ -303,7 +303,7 @@ static ssize_t cifs_stats_proc_write(struct file *file, } } } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); } return count; @@ -343,7 +343,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) GlobalCurrentXid, GlobalMaxActiveXid); i = 0; - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp1, &cifs_tcp_ses_list) { server = list_entry(tmp1, struct TCP_Server_Info, tcp_ses_list); @@ -397,7 +397,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) } } } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); seq_putc(m, '\n'); return 0; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f1d9c71e807..cb77915a445 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -482,16 +482,16 @@ static void cifs_umount_begin(struct super_block *sb) tcon = cifs_sb_master_tcon(cifs_sb); - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) { /* we have other mounts to same share or we have already tried to force umount this and woken up all waiting network requests, nothing to do */ - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return; } else if (tcon->tc_count == 1) tcon->tidStatus = CifsExiting; - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ /* cancel_notify_requests(tcon); */ @@ -940,7 +940,7 @@ init_cifs(void) GlobalTotalActiveXid = 0; GlobalMaxActiveXid = 0; memset(Local_System_Name, 0, 15); - rwlock_init(&cifs_tcp_ses_lock); + spin_lock_init(&cifs_tcp_ses_lock); spin_lock_init(&cifs_file_list_lock); spin_lock_init(&GlobalMid_Lock); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 18ee0adda30..28337cba029 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -703,7 +703,7 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; * the reference counters for the server, smb session, and tcon. Finally, * changes to the tcon->tidStatus should be done while holding this lock. */ -GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; +GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock; /* * This lock protects the cifs_file->llist and cifs_file->flist diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index bfb59a68e4f..e98f1f317b1 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -593,9 +593,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) rc = -EIO; goto neg_err_exit; } - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if (server->srv_count > 1) { - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); if (memcmp(server->server_GUID, pSMBr->u.extended_response. GUID, 16) != 0) { @@ -605,7 +605,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) 16); } } else { - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); memcpy(server->server_GUID, pSMBr->u.extended_response.GUID, 16); } diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 019f00380d1..7e73176acb5 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -150,7 +150,7 @@ cifs_reconnect(struct TCP_Server_Info *server) /* before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they are not used until reconnected */ - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses->need_reconnect = true; @@ -160,7 +160,7 @@ cifs_reconnect(struct TCP_Server_Info *server) tcon->need_reconnect = true; } } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); /* do not want to be sending data on a socket we are freeing */ mutex_lock(&server->srv_mutex); if (server->ssocket) { @@ -637,9 +637,9 @@ multi_t2_fnd: } /* end while !EXITING */ /* take it off the list, if it's not already */ - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_del_init(&server->tcp_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; @@ -677,7 +677,7 @@ multi_t2_fnd: * BB: we shouldn't have to do any of this. It shouldn't be * possible to exit from the thread with active SMB sessions */ - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if (list_empty(&server->pending_mid_q)) { /* loop through server session structures attached to this and mark them dead */ @@ -687,7 +687,7 @@ multi_t2_fnd: ses->status = CifsExiting; ses->server = NULL; } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); } else { /* although we can not zero the server struct pointer yet, since there are active requests which may depnd on them, @@ -710,7 +710,7 @@ multi_t2_fnd: } } spin_unlock(&GlobalMid_Lock); - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); /* 1/8th of sec is more than enough time for them to exit */ msleep(125); } @@ -733,12 +733,12 @@ multi_t2_fnd: if a crazy root user tried to kill cifsd kernel thread explicitly this might happen) */ /* BB: This shouldn't be necessary, see above */ - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses->server = NULL; } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); kfree(server->hostname); task_to_wake = xchg(&server->tsk, NULL); @@ -1524,7 +1524,7 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) { struct TCP_Server_Info *server; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { if (!match_address(server, addr, (struct sockaddr *)&vol->srcaddr)) @@ -1534,11 +1534,11 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) continue; ++server->srv_count; - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); cFYI(1, "Existing tcp session with server found"); return server; } - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return NULL; } @@ -1547,14 +1547,14 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) { struct task_struct *task; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if (--server->srv_count > 0) { - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return; } list_del_init(&server->tcp_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; @@ -1679,9 +1679,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) } /* thread spawned, put it on the list */ - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); cifs_fscache_get_client_cookie(tcp_ses); @@ -1703,7 +1703,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) { struct cifsSesInfo *ses; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { switch (server->secType) { case Kerberos: @@ -1723,10 +1723,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) continue; } ++ses->ses_count; - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return ses; } - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return NULL; } @@ -1737,14 +1737,14 @@ cifs_put_smb_ses(struct cifsSesInfo *ses) struct TCP_Server_Info *server = ses->server; cFYI(1, "%s: ses_count=%d\n", __func__, ses->ses_count); - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if (--ses->ses_count > 0) { - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return; } list_del_init(&ses->smb_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); if (ses->status == CifsGood) { xid = GetXid(); @@ -1841,9 +1841,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) goto get_ses_fail; /* success, put it on the list */ - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_add(&ses->smb_ses_list, &server->smb_ses_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); FreeXid(xid); return ses; @@ -1860,7 +1860,7 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) struct list_head *tmp; struct cifsTconInfo *tcon; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &ses->tcon_list) { tcon = list_entry(tmp, struct cifsTconInfo, tcon_list); if (tcon->tidStatus == CifsExiting) @@ -1869,10 +1869,10 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) continue; ++tcon->tc_count; - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return tcon; } - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return NULL; } @@ -1883,14 +1883,14 @@ cifs_put_tcon(struct cifsTconInfo *tcon) struct cifsSesInfo *ses = tcon->ses; cFYI(1, "%s: tc_count=%d\n", __func__, tcon->tc_count); - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if (--tcon->tc_count > 0) { - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return; } list_del_init(&tcon->tcon_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); xid = GetXid(); CIFSSMBTDis(xid, tcon); @@ -1963,9 +1963,9 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info) tcon->nocase = volume_info->nocase; tcon->local_lease = volume_info->local_lease; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_add(&tcon->tcon_list, &ses->tcon_list); - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); cifs_fscache_get_super_cookie(tcon); @@ -3225,9 +3225,9 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) vol_info->secFlg = CIFSSEC_MUST_KRB5; /* get a reference for the same TCP session */ - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); ++master_tcon->ses->server->srv_count; - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info); if (IS_ERR(ses)) { diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index de6073cccd9..a7b492c213c 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -347,7 +347,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , if (current_fsuid() != treeCon->ses->linux_uid) { cFYI(1, "Multiuser mode and UID " "did not match tcon uid"); - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) { ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list); if (ses->linux_uid == current_fsuid()) { @@ -361,7 +361,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , } } } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); } } } @@ -551,7 +551,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) return false; /* look up tcon based on tid & uid */ - read_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &srv->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); list_for_each(tmp1, &ses->tcon_list) { @@ -573,7 +573,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) */ if (netfile->closePend) { spin_unlock(&cifs_file_list_lock); - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return true; } @@ -595,16 +595,16 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) netfile->oplock_break_cancelled = false; spin_unlock(&cifs_file_list_lock); - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return true; } spin_unlock(&cifs_file_list_lock); - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); cFYI(1, "No matching file for oplock break"); return true; } } - read_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); cFYI(1, "Can not process oplock break for non-existent connection"); return true; } diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index e35dc60d325..2a11efd9659 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -80,7 +80,7 @@ static __le16 get_next_vcnum(struct cifsSesInfo *ses) if (max_vcs < 2) max_vcs = 0xFFFF; - write_lock(&cifs_tcp_ses_lock); + spin_lock(&cifs_tcp_ses_lock); if ((ses->need_reconnect) && is_first_ses_reconnect(ses)) goto get_vc_num_exit; /* vcnum will be zero */ for (i = ses->server->srv_count - 1; i < max_vcs; i++) { @@ -112,7 +112,7 @@ static __le16 get_next_vcnum(struct cifsSesInfo *ses) vcnum = i; ses->vcnum = vcnum; get_vc_num_exit: - write_unlock(&cifs_tcp_ses_lock); + spin_unlock(&cifs_tcp_ses_lock); return cpu_to_le16(vcnum); } -- cgit v1.2.3-18-g5258 From 6de5bd128d381ad88ac6d419a5e597048eb468cf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 11 Sep 2010 18:00:57 +0200 Subject: BKL: introduce CONFIG_BKL. With all the patches we have queued in the BKL removal tree, only a few dozen modules are left that actually rely on the BKL, and even there are lots of low-hanging fruit. We need to decide what to do about them, this patch illustrates one of the options: Every user of the BKL is marked as 'depends on BKL' in Kconfig, and the CONFIG_BKL becomes a user-visible option. If it gets disabled, no BKL using module can be built any more and the BKL code itself is compiled out. The one exception is file locking, which is practically always enabled and does a 'select BKL' instead. This effectively forces CONFIG_BKL to be enabled until we have solved the fs/lockd mess and can apply the patch that removes the BKL from fs/locks.c. Signed-off-by: Arnd Bergmann --- fs/Kconfig | 1 + fs/adfs/Kconfig | 1 + fs/autofs/Kconfig | 1 + fs/hpfs/Kconfig | 1 + fs/nfs/Kconfig | 1 + fs/nfsd/Kconfig | 1 + fs/smbfs/Kconfig | 1 + fs/udf/Kconfig | 1 + fs/ufs/Kconfig | 1 + 9 files changed, 9 insertions(+) (limited to 'fs') diff --git a/fs/Kconfig b/fs/Kconfig index 3d185308ec8..65781de44fc 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -50,6 +50,7 @@ endif # BLOCK config FILE_LOCKING bool "Enable POSIX file locking API" if EMBEDDED default y + select BKL # while lockd still uses it. help This option enables standard file locking support, required for filesystems like NFS and for the flock() system diff --git a/fs/adfs/Kconfig b/fs/adfs/Kconfig index e55182a7460..1dd5f34b3cf 100644 --- a/fs/adfs/Kconfig +++ b/fs/adfs/Kconfig @@ -1,6 +1,7 @@ config ADFS_FS tristate "ADFS file system support (EXPERIMENTAL)" depends on BLOCK && EXPERIMENTAL + depends on BKL # need to fix help The Acorn Disc Filing System is the standard file system of the RiscOS operating system which runs on Acorn's ARM-based Risc PC diff --git a/fs/autofs/Kconfig b/fs/autofs/Kconfig index 5f3bea90911..480e210c83a 100644 --- a/fs/autofs/Kconfig +++ b/fs/autofs/Kconfig @@ -1,5 +1,6 @@ config AUTOFS_FS tristate "Kernel automounter support" + depends on BKL # unfixable, just use autofs4 help The automounter is a tool to automatically mount remote file systems on demand. This implementation is partially kernel-based to reduce diff --git a/fs/hpfs/Kconfig b/fs/hpfs/Kconfig index 56bd15c5bf6..63b6f563231 100644 --- a/fs/hpfs/Kconfig +++ b/fs/hpfs/Kconfig @@ -1,6 +1,7 @@ config HPFS_FS tristate "OS/2 HPFS file system support" depends on BLOCK + depends on BKL # nontrivial to fix help OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS is the file system used for organizing files on OS/2 hard disk diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 6c2aad49d73..10b9524bb90 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -1,6 +1,7 @@ config NFS_FS tristate "NFS client support" depends on INET && FILE_LOCKING + depends on BKL # fix as soon as lockd is done select LOCKD select SUNRPC select NFS_ACL_SUPPORT if NFS_V3_ACL diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig index 95932f523ae..429d4a14227 100644 --- a/fs/nfsd/Kconfig +++ b/fs/nfsd/Kconfig @@ -2,6 +2,7 @@ config NFSD tristate "NFS server support" depends on INET depends on FILE_LOCKING + depends on BKL # fix as soon as lockd is done select LOCKD select SUNRPC select EXPORTFS diff --git a/fs/smbfs/Kconfig b/fs/smbfs/Kconfig index e668127c8b2..2bc24a8c403 100644 --- a/fs/smbfs/Kconfig +++ b/fs/smbfs/Kconfig @@ -1,5 +1,6 @@ config SMB_FS tristate "SMB file system support (OBSOLETE, please use CIFS)" + depends on BKL # probably unfixable depends on INET select NLS help diff --git a/fs/udf/Kconfig b/fs/udf/Kconfig index 0e0e99bd6bc..f8def3c8ea4 100644 --- a/fs/udf/Kconfig +++ b/fs/udf/Kconfig @@ -1,5 +1,6 @@ config UDF_FS tristate "UDF file system support" + depends on BKL # needs serious work to remove select CRC_ITU_T help This is the new file system used on some CD-ROMs and DVDs. Say Y if diff --git a/fs/ufs/Kconfig b/fs/ufs/Kconfig index e4f10a40768..30c8f223253 100644 --- a/fs/ufs/Kconfig +++ b/fs/ufs/Kconfig @@ -1,6 +1,7 @@ config UFS_FS tristate "UFS file system support (read only)" depends on BLOCK + depends on BKL # probably fixable help BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD, OpenBSD and NeXTstep) use a file system called UFS. Some System V -- cgit v1.2.3-18-g5258 From 073c21416268658bd1bc573af85eeac2ebb56ed5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 21 Oct 2010 15:53:15 +0200 Subject: BKL: remove BKL from qnx4 All uses of the BKL in qnx4 were the result of a pushdown into code that doesn't really need it. As Christoph points out, this is a read-only file system, which eliminates most of the races in readdir/lookup. Signed-off-by: Arnd Bergmann Acked-by: Anders Larsen Cc: Christoph Hellwig --- fs/qnx4/dir.c | 4 ---- fs/qnx4/inode.c | 14 +------------- fs/qnx4/namei.c | 4 ---- 3 files changed, 1 insertion(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c index 6e8fc62b40a..7b0329468a5 100644 --- a/fs/qnx4/dir.c +++ b/fs/qnx4/dir.c @@ -11,7 +11,6 @@ * 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support. */ -#include #include #include "qnx4.h" @@ -29,8 +28,6 @@ static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) QNX4DEBUG((KERN_INFO "qnx4_readdir:i_size = %ld\n", (long) inode->i_size)); QNX4DEBUG((KERN_INFO "filp->f_pos = %ld\n", (long) filp->f_pos)); - lock_kernel(); - while (filp->f_pos < inode->i_size) { blknum = qnx4_block_map( inode, filp->f_pos >> QNX4_BLOCK_SIZE_BITS ); bh = sb_bread(inode->i_sb, blknum); @@ -71,7 +68,6 @@ static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) brelse(bh); } out: - unlock_kernel(); return 0; } diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 86a7be1399a..01bad30026f 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -157,8 +156,6 @@ static int qnx4_statfs(struct dentry *dentry, struct kstatfs *buf) struct super_block *sb = dentry->d_sb; u64 id = huge_encode_dev(sb->s_bdev->bd_dev); - lock_kernel(); - buf->f_type = sb->s_magic; buf->f_bsize = sb->s_blocksize; buf->f_blocks = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size) * 8; @@ -168,8 +165,6 @@ static int qnx4_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); - unlock_kernel(); - return 0; } @@ -234,13 +229,9 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) struct qnx4_sb_info *qs; int ret = -EINVAL; - lock_kernel(); - qs = kzalloc(sizeof(struct qnx4_sb_info), GFP_KERNEL); - if (!qs) { - unlock_kernel(); + if (!qs) return -ENOMEM; - } s->s_fs_info = qs; sb_set_blocksize(s, QNX4_BLOCK_SIZE); @@ -287,8 +278,6 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) goto outi; brelse(bh); - - unlock_kernel(); return 0; outi: @@ -298,7 +287,6 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) outnobh: kfree(qs); s->s_fs_info = NULL; - unlock_kernel(); return ret; } diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c index 58703ebba87..275327b5615 100644 --- a/fs/qnx4/namei.c +++ b/fs/qnx4/namei.c @@ -12,7 +12,6 @@ * 04-07-1998 by Frank Denis : first step for rmdir/unlink. */ -#include #include #include "qnx4.h" @@ -109,7 +108,6 @@ struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry, struct nam int len = dentry->d_name.len; struct inode *foundinode = NULL; - lock_kernel(); if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) goto out; /* The entry is linked, let's get the real info */ @@ -123,13 +121,11 @@ struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry, struct nam foundinode = qnx4_iget(dir->i_sb, ino); if (IS_ERR(foundinode)) { - unlock_kernel(); QNX4DEBUG((KERN_ERR "qnx4: lookup->iget -> error %ld\n", PTR_ERR(foundinode))); return ERR_CAST(foundinode); } out: - unlock_kernel(); d_add(dentry, foundinode); return NULL; -- cgit v1.2.3-18-g5258 From 6d7bccc2215c37205ede6c9cf84db64e7c4f9443 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 21 Oct 2010 15:58:01 +0200 Subject: BKL: remove BKL from freevxfs All uses of the BKL in freevxfs were the result of a pushdown into code that doesn't really need it. As Christoph points out, this is a read-only file system, which eliminates most of the races in readdir/lookup. Signed-off-by: Arnd Bergmann Cc: Christoph Hellwig --- fs/freevxfs/vxfs_lookup.c | 14 ++------------ fs/freevxfs/vxfs_super.c | 10 ---------- 2 files changed, 2 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c index 0ec7bb2c95c..6c5131d592f 100644 --- a/fs/freevxfs/vxfs_lookup.c +++ b/fs/freevxfs/vxfs_lookup.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "vxfs.h" #include "vxfs_dir.h" @@ -212,16 +211,12 @@ vxfs_lookup(struct inode *dip, struct dentry *dp, struct nameidata *nd) if (dp->d_name.len > VXFS_NAMELEN) return ERR_PTR(-ENAMETOOLONG); - lock_kernel(); ino = vxfs_inode_by_name(dip, dp); if (ino) { ip = vxfs_iget(dip->i_sb, ino); - if (IS_ERR(ip)) { - unlock_kernel(); + if (IS_ERR(ip)) return ERR_CAST(ip); - } } - unlock_kernel(); d_add(dp, ip); return NULL; } @@ -248,8 +243,6 @@ vxfs_readdir(struct file *fp, void *retp, filldir_t filler) u_long page, npages, block, pblocks, nblocks, offset; loff_t pos; - lock_kernel(); - switch ((long)fp->f_pos) { case 0: if (filler(retp, ".", 1, fp->f_pos, ip->i_ino, DT_DIR) < 0) @@ -265,10 +258,8 @@ vxfs_readdir(struct file *fp, void *retp, filldir_t filler) pos = fp->f_pos - 2; - if (pos > VXFS_DIRROUND(ip->i_size)) { - unlock_kernel(); + if (pos > VXFS_DIRROUND(ip->i_size)) return 0; - } npages = dir_pages(ip); nblocks = dir_blocks(ip); @@ -327,6 +318,5 @@ vxfs_readdir(struct file *fp, void *retp, filldir_t filler) done: fp->f_pos = ((page << PAGE_CACHE_SHIFT) | offset) + 2; out: - unlock_kernel(); return 0; } diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c index eb2b9e09c99..71b0148b878 100644 --- a/fs/freevxfs/vxfs_super.c +++ b/fs/freevxfs/vxfs_super.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -81,16 +80,12 @@ vxfs_put_super(struct super_block *sbp) { struct vxfs_sb_info *infp = VXFS_SBI(sbp); - lock_kernel(); - vxfs_put_fake_inode(infp->vsi_fship); vxfs_put_fake_inode(infp->vsi_ilist); vxfs_put_fake_inode(infp->vsi_stilist); brelse(infp->vsi_bp); kfree(infp); - - unlock_kernel(); } /** @@ -159,14 +154,11 @@ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent) struct inode *root; int ret = -EINVAL; - lock_kernel(); - sbp->s_flags |= MS_RDONLY; infp = kzalloc(sizeof(*infp), GFP_KERNEL); if (!infp) { printk(KERN_WARNING "vxfs: unable to allocate incore superblock\n"); - unlock_kernel(); return -ENOMEM; } @@ -239,7 +231,6 @@ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent) goto out_free_ilist; } - unlock_kernel(); return 0; out_free_ilist: @@ -249,7 +240,6 @@ out_free_ilist: out: brelse(bp); kfree(infp); - unlock_kernel(); return ret; } -- cgit v1.2.3-18-g5258 From cdff08e76612e53580139653403aedea979aa639 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 21 Oct 2010 22:46:14 +0000 Subject: [CIFS] move close processing from cifs_close to cifsFileInfo_put Now that it's feasible for a cifsFileInfo to outlive the filp under which it was created, move the close processing into cifsFileInfo_put. This means that the last user of the filehandle always does the actual on the wire close call. This also allows us to get rid of the closePend flag from cifsFileInfo. If we have an active reference to the file then it's never going to have a close pending. cifs_close is converted to simply put the filehandle. Signed-off-by: Jeff Layton Reviewed-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 - fs/cifs/file.c | 187 +++++++++++++++++------------------------------------ fs/cifs/misc.c | 10 --- 3 files changed, 60 insertions(+), 138 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 28337cba029..3365e77f6f2 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -393,7 +393,6 @@ struct cifsFileInfo { struct tcon_link *tlink; struct mutex lock_mutex; struct list_head llist; /* list of byte range locks we have. */ - bool closePend:1; /* file is marked to close */ bool invalidHandle:1; /* file closed via session abend */ bool oplock_break_cancelled:1; atomic_t count; /* reference count */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index a3634e43bd4..8c81e7b14d5 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -238,7 +238,6 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, pCifsFile->dentry = dget(dentry); pCifsFile->f_flags = file->f_flags; pCifsFile->invalidHandle = false; - pCifsFile->closePend = false; pCifsFile->tlink = cifs_get_tlink(tlink); mutex_init(&pCifsFile->fh_mutex); mutex_init(&pCifsFile->lock_mutex); @@ -266,14 +265,55 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, return pCifsFile; } -/* Release a reference on the file private data */ +/* + * Release a reference on the file private data. This may involve closing + * the filehandle out on the server. + */ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) { - if (atomic_dec_and_test(&cifs_file->count)) { - cifs_put_tlink(cifs_file->tlink); - dput(cifs_file->dentry); - kfree(cifs_file); + struct cifsTconInfo *tcon = tlink_tcon(cifs_file->tlink); + struct cifsInodeInfo *cifsi = CIFS_I(cifs_file->dentry->d_inode); + struct cifsLockInfo *li, *tmp; + + spin_lock(&cifs_file_list_lock); + if (!atomic_dec_and_test(&cifs_file->count)) { + spin_unlock(&cifs_file_list_lock); + return; + } + + /* remove it from the lists */ + list_del(&cifs_file->flist); + list_del(&cifs_file->tlist); + + if (list_empty(&cifsi->openFileList)) { + cFYI(1, "closing last open instance for inode %p", + cifs_file->dentry->d_inode); + cifsi->clientCanCacheRead = false; + cifsi->clientCanCacheAll = false; + } + spin_unlock(&cifs_file_list_lock); + + if (!tcon->need_reconnect && !cifs_file->invalidHandle) { + int xid, rc; + + xid = GetXid(); + rc = CIFSSMBClose(xid, tcon, cifs_file->netfid); + FreeXid(xid); + } + + /* Delete any outstanding lock records. We'll lose them when the file + * is closed anyway. + */ + mutex_lock(&cifs_file->lock_mutex); + list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) { + list_del(&li->llist); + kfree(li); } + mutex_unlock(&cifs_file->lock_mutex); + + cifs_put_tlink(cifs_file->tlink); + dput(cifs_file->dentry); + kfree(cifs_file); } int cifs_open(struct inode *inode, struct file *file) @@ -605,79 +645,11 @@ reopen_error_exit: int cifs_close(struct inode *inode, struct file *file) { - int rc = 0; - int xid, timeout; - struct cifs_sb_info *cifs_sb; - struct cifsTconInfo *pTcon; - struct cifsFileInfo *pSMBFile = file->private_data; - - xid = GetXid(); - - cifs_sb = CIFS_SB(inode->i_sb); - pTcon = tlink_tcon(pSMBFile->tlink); - if (pSMBFile) { - struct cifsLockInfo *li, *tmp; - spin_lock(&cifs_file_list_lock); - pSMBFile->closePend = true; - if (pTcon) { - /* no sense reconnecting to close a file that is - already closed */ - if (!pTcon->need_reconnect) { - spin_unlock(&cifs_file_list_lock); - timeout = 2; - while ((atomic_read(&pSMBFile->count) != 1) - && (timeout <= 2048)) { - /* Give write a better chance to get to - server ahead of the close. We do not - want to add a wait_q here as it would - increase the memory utilization as - the struct would be in each open file, - but this should give enough time to - clear the socket */ - cFYI(DBG2, "close delay, write pending"); - msleep(timeout); - timeout *= 4; - } - if (!pTcon->need_reconnect && - !pSMBFile->invalidHandle) - rc = CIFSSMBClose(xid, pTcon, - pSMBFile->netfid); - } else - spin_unlock(&cifs_file_list_lock); - } else - spin_unlock(&cifs_file_list_lock); - - /* Delete any outstanding lock records. - We'll lose them when the file is closed anyway. */ - mutex_lock(&pSMBFile->lock_mutex); - list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) { - list_del(&li->llist); - kfree(li); - } - mutex_unlock(&pSMBFile->lock_mutex); + cifsFileInfo_put(file->private_data); + file->private_data = NULL; - spin_lock(&cifs_file_list_lock); - list_del(&pSMBFile->flist); - list_del(&pSMBFile->tlist); - spin_unlock(&cifs_file_list_lock); - cifsFileInfo_put(file->private_data); - file->private_data = NULL; - } else - rc = -EBADF; - - spin_lock(&cifs_file_list_lock); - if (list_empty(&(CIFS_I(inode)->openFileList))) { - cFYI(1, "closing last open instance for inode %p", inode); - /* if the file is not open we do not know if we can cache info - on this inode, much less write behind and read ahead */ - CIFS_I(inode)->clientCanCacheRead = false; - CIFS_I(inode)->clientCanCacheAll = false; - } - spin_unlock(&cifs_file_list_lock); - if ((rc == 0) && CIFS_I(inode)->write_behind_rc) - rc = CIFS_I(inode)->write_behind_rc; - FreeXid(xid); - return rc; + /* return code from the ->release op is always ignored */ + return 0; } int cifs_closedir(struct inode *inode, struct file *file) @@ -1024,13 +996,6 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, we blocked so return what we managed to write */ return total_written; } - if (open_file->closePend) { - FreeXid(xid); - if (total_written) - return total_written; - else - return -EBADF; - } if (open_file->invalidHandle) { /* we could deadlock if we called filemap_fdatawait from here so tell @@ -1111,13 +1076,6 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, total_written += bytes_written) { rc = -EAGAIN; while (rc == -EAGAIN) { - if (open_file->closePend) { - FreeXid(xid); - if (total_written) - return total_written; - else - return -EBADF; - } if (open_file->invalidHandle) { /* we could deadlock if we called filemap_fdatawait from here so tell @@ -1197,8 +1155,6 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, are always at the end of the list but since the first entry might have a close pending, we go through the whole list */ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (open_file->closePend) - continue; if (fsuid_only && open_file->uid != current_fsuid()) continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { @@ -1244,8 +1200,6 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, spin_lock(&cifs_file_list_lock); refind_writable: list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (open_file->closePend) - continue; if (!any_available && open_file->pid != current->tgid) continue; if (fsuid_only && open_file->uid != current_fsuid()) @@ -1260,34 +1214,18 @@ refind_writable: } spin_unlock(&cifs_file_list_lock); + /* Had to unlock since following call can block */ rc = cifs_reopen_file(open_file, false); - if (!rc) { - if (!open_file->closePend) - return open_file; - else { /* start over in case this was deleted */ - /* since the list could be modified */ - spin_lock(&cifs_file_list_lock); - cifsFileInfo_put(open_file); - goto refind_writable; - } - } + if (!rc) + return open_file; - /* if it fails, try another handle if possible - - (we can not do this if closePending since - loop could be modified - in which case we - have to start at the beginning of the list - again. Note that it would be bad - to hold up writepages here (rather than - in caller) with continuous retries */ + /* if it fails, try another handle if possible */ cFYI(1, "wp failed on reopen file"); - spin_lock(&cifs_file_list_lock); - /* can not use this handle, no write - pending on this one after all */ cifsFileInfo_put(open_file); - if (open_file->closePend) /* list could have changed */ - goto refind_writable; + spin_lock(&cifs_file_list_lock); + /* else we simply continue to the next entry. Thus we do not loop on reopen errors. If we can not reopen the file, for example if we @@ -1808,8 +1746,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, smb_read_data = NULL; while (rc == -EAGAIN) { int buf_type = CIFS_NO_BUFFER; - if ((open_file->invalidHandle) && - (!open_file->closePend)) { + if (open_file->invalidHandle) { rc = cifs_reopen_file(open_file, true); if (rc != 0) break; @@ -1894,8 +1831,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, } rc = -EAGAIN; while (rc == -EAGAIN) { - if ((open_file->invalidHandle) && - (!open_file->closePend)) { + if (open_file->invalidHandle) { rc = cifs_reopen_file(open_file, true); if (rc != 0) break; @@ -2059,8 +1995,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, read_size, contig_pages); rc = -EAGAIN; while (rc == -EAGAIN) { - if ((open_file->invalidHandle) && - (!open_file->closePend)) { + if (open_file->invalidHandle) { rc = cifs_reopen_file(open_file, true); if (rc != 0) break; @@ -2212,8 +2147,6 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode) spin_lock(&cifs_file_list_lock); list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (open_file->closePend) - continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { spin_unlock(&cifs_file_list_lock); return 1; @@ -2372,7 +2305,7 @@ void cifs_oplock_break(struct work_struct *work) * not bother sending an oplock release if session to server still is * disconnected since oplock already released by the server */ - if (!cfile->closePend && !cfile->oplock_break_cancelled) { + if (!cfile->oplock_break_cancelled) { rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid, 0, 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false); cFYI(1, "Oplock release rc = %d", rc); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index a7b492c213c..1c681f6a680 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -567,16 +567,6 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) if (pSMB->Fid != netfile->netfid) continue; - /* - * don't do anything if file is about to be - * closed anyway. - */ - if (netfile->closePend) { - spin_unlock(&cifs_file_list_lock); - spin_unlock(&cifs_tcp_ses_lock); - return true; - } - cFYI(1, "file id match, oplock break"); pCifsInode = CIFS_I(netfile->dentry->d_inode); pCifsInode->clientCanCacheAll = false; -- cgit v1.2.3-18-g5258 From e52eec13cd6b7f30ab19081b387813e03e592ae5 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 8 Sep 2010 16:54:17 +0200 Subject: SYSFS: Allow boot time switching between deprecated and modern sysfs layout I have some systems which need legacy sysfs due to old tools that are making assumptions that a directory can never be a symlink to another directory, and it's a big hazzle to compile separate kernels for them. This patch turns CONFIG_SYSFS_DEPRECATED into a run time option that can be switched on/off the kernel command line. This way the same binary can be used in both cases with just a option on the command line. The old CONFIG_SYSFS_DEPRECATED_V2 option is still there to set the default. I kept the weird name to not break existing config files. Also the compat code can be still completely disabled by undefining CONFIG_SYSFS_DEPRECATED_SWITCH -- just the optimizer takes care of this now instead of lots of ifdefs. This makes the code look nicer. v2: This is an updated version on top of Kay's patch to only handle the block devices. I tested it on my old systems and that seems to work. Cc: axboe@kernel.dk Signed-off-by: Andi Kleen Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- fs/partitions/check.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 79fbf3f390f..137bf978785 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -513,14 +513,14 @@ void register_disk(struct gendisk *disk) if (device_add(ddev)) return; -#ifndef CONFIG_SYSFS_DEPRECATED - err = sysfs_create_link(block_depr, &ddev->kobj, - kobject_name(&ddev->kobj)); - if (err) { - device_del(ddev); - return; + if (!sysfs_deprecated) { + err = sysfs_create_link(block_depr, &ddev->kobj, + kobject_name(&ddev->kobj)); + if (err) { + device_del(ddev); + return; + } } -#endif disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj); disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); @@ -737,8 +737,7 @@ void del_gendisk(struct gendisk *disk) kobject_put(disk->part0.holder_dir); kobject_put(disk->slave_dir); disk->driverfs_dev = NULL; -#ifndef CONFIG_SYSFS_DEPRECATED - sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); -#endif + if (!sysfs_deprecated) + sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); device_del(disk_to_dev(disk)); } -- cgit v1.2.3-18-g5258 From a6849fa1f7d7d7adbeb6a696beeabfa078acf173 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 20 Sep 2010 00:56:27 -0700 Subject: sysfs: Fail bin file mmap if vma close is implemented. It is not reasonably possible to wrap vma->close(). To correctly wrap close would imply calling close on any vmas that remain when sysfs_remove_bin_file is called. Finding the proper lists walking them getting the locking right etc, requires deep knowledge of the mm subsystem and as such would require assistence from the mm subsystem to implement. That assistence does not currently exist. Signed-off-by: Eric W. Biederman Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/bin.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 4e321f7353f..d31d7b7d542 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -190,23 +190,6 @@ static void bin_vma_open(struct vm_area_struct *vma) sysfs_put_active(attr_sd); } -static void bin_vma_close(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct bin_buffer *bb = file->private_data; - struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; - - if (!bb->vm_ops || !bb->vm_ops->close) - return; - - if (!sysfs_get_active(attr_sd)) - return; - - bb->vm_ops->close(vma); - - sysfs_put_active(attr_sd); -} - static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct file *file = vma->vm_file; @@ -331,7 +314,6 @@ static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, static const struct vm_operations_struct bin_vm_ops = { .open = bin_vma_open, - .close = bin_vma_close, .fault = bin_fault, .page_mkwrite = bin_page_mkwrite, .access = bin_access, @@ -377,6 +359,14 @@ static int mmap(struct file *file, struct vm_area_struct *vma) if (bb->mmapped && bb->vm_ops != vma->vm_ops) goto out_put; + /* + * It is not possible to successfully wrap close. + * So error if someone is trying to use close. + */ + rc = -EINVAL; + if (vma->vm_ops && vma->vm_ops->close) + goto out_put; + rc = 0; bb->mmapped = 1; bb->vm_ops = vma->vm_ops; -- cgit v1.2.3-18-g5258 From 38f49a5132f24d29236820eb5c7dd956e47f94a3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 20 Sep 2010 00:57:03 -0700 Subject: sysfs: only access bin file vm_ops with the active lock bb->vm_ops is a cached copy of the vm_ops of the underlying sysfs bin file, which means that after sysfs_bin_remove_file completes it is only longer valid to deference bb->vm_ops. So move all of the tests of bb->vm_ops inside of where we hold the sysfs active lock. Signed-off-by: Eric W. Biederman Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/bin.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index d31d7b7d542..a4759833d62 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -179,13 +179,14 @@ static void bin_vma_open(struct vm_area_struct *vma) struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; - if (!bb->vm_ops || !bb->vm_ops->open) + if (!bb->vm_ops) return; if (!sysfs_get_active(attr_sd)) return; - bb->vm_ops->open(vma); + if (bb->vm_ops->open) + bb->vm_ops->open(vma); sysfs_put_active(attr_sd); } @@ -197,13 +198,15 @@ static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; - if (!bb->vm_ops || !bb->vm_ops->fault) + if (!bb->vm_ops) return VM_FAULT_SIGBUS; if (!sysfs_get_active(attr_sd)) return VM_FAULT_SIGBUS; - ret = bb->vm_ops->fault(vma, vmf); + ret = VM_FAULT_SIGBUS; + if (bb->vm_ops->fault) + ret = bb->vm_ops->fault(vma, vmf); sysfs_put_active(attr_sd); return ret; @@ -219,13 +222,12 @@ static int bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) if (!bb->vm_ops) return VM_FAULT_SIGBUS; - if (!bb->vm_ops->page_mkwrite) - return 0; - if (!sysfs_get_active(attr_sd)) return VM_FAULT_SIGBUS; - ret = bb->vm_ops->page_mkwrite(vma, vmf); + ret = 0; + if (bb->vm_ops->page_mkwrite) + ret = bb->vm_ops->page_mkwrite(vma, vmf); sysfs_put_active(attr_sd); return ret; @@ -239,13 +241,15 @@ static int bin_access(struct vm_area_struct *vma, unsigned long addr, struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; - if (!bb->vm_ops || !bb->vm_ops->access) + if (!bb->vm_ops) return -EINVAL; if (!sysfs_get_active(attr_sd)) return -EINVAL; - ret = bb->vm_ops->access(vma, addr, buf, len, write); + ret = -EINVAL; + if (bb->vm_ops->access) + ret = bb->vm_ops->access(vma, addr, buf, len, write); sysfs_put_active(attr_sd); return ret; @@ -259,13 +263,15 @@ static int bin_set_policy(struct vm_area_struct *vma, struct mempolicy *new) struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; - if (!bb->vm_ops || !bb->vm_ops->set_policy) + if (!bb->vm_ops) return 0; if (!sysfs_get_active(attr_sd)) return -EINVAL; - ret = bb->vm_ops->set_policy(vma, new); + ret = 0; + if (bb->vm_ops->set_policy) + ret = bb->vm_ops->set_policy(vma, new); sysfs_put_active(attr_sd); return ret; @@ -279,13 +285,15 @@ static struct mempolicy *bin_get_policy(struct vm_area_struct *vma, struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct mempolicy *pol; - if (!bb->vm_ops || !bb->vm_ops->get_policy) + if (!bb->vm_ops) return vma->vm_policy; if (!sysfs_get_active(attr_sd)) return vma->vm_policy; - pol = bb->vm_ops->get_policy(vma, addr); + pol = vma->vm_policy; + if (bb->vm_ops->get_policy) + pol = bb->vm_ops->get_policy(vma, addr); sysfs_put_active(attr_sd); return pol; @@ -299,13 +307,15 @@ static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; int ret; - if (!bb->vm_ops || !bb->vm_ops->migrate) + if (!bb->vm_ops) return 0; if (!sysfs_get_active(attr_sd)) return 0; - ret = bb->vm_ops->migrate(vma, from, to, flags); + ret = 0; + if (bb->vm_ops->migrate) + ret = bb->vm_ops->migrate(vma, from, to, flags); sysfs_put_active(attr_sd); return ret; -- cgit v1.2.3-18-g5258 From f4a3e0bceb57466c31757f25e4e0ed108d1299ec Mon Sep 17 00:00:00 2001 From: "Dr. Werner Fink" Date: Wed, 22 Sep 2010 12:45:40 +0200 Subject: tty: Add a new file /proc/tty/consoles Add a new file /proc/tty/consoles to be able to determine the registered system console lines. If the reading process holds /dev/console open at the regular standard input stream the active device will be marked by an asterisk. Show possible operations and also decode the used flags of the listed console lines. Signed-off-by: Werner Fink Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- fs/proc/proc_tty.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) (limited to 'fs') diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index 83adcc86943..dc44f94022f 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -12,7 +12,10 @@ #include #include #include +#include +#include #include +#include #include /* @@ -136,6 +139,160 @@ static const struct file_operations proc_tty_drivers_operations = { .release = seq_release, }; +/* + * The device ID of file descriptor 0 of the current reading + * task if a character device... + */ +static dev_t current_dev; + +/* + * This is the handler for /proc/tty/consoles + */ +static int show_console_dev(struct seq_file *m, void *v) +{ + const struct tty_driver *driver; + struct console *con; + int index, len; + char flags[10]; + dev_t dev; + + if (v == SEQ_START_TOKEN) + return 0; + con = (struct console *)v; + if (!con) + return 0; + driver = con->device(con, &index); + if (!driver) + return 0; + dev = MKDEV(driver->major, driver->minor_start) + index; + + index = 0; + if (con->flags & CON_ENABLED) + flags[index++] = 'E'; + if (con->flags & CON_CONSDEV) + flags[index++] = 'C'; + if (con->flags & CON_BOOT) + flags[index++] = 'B'; + if (con->flags & CON_PRINTBUFFER) + flags[index++] = 'p'; + if (con->flags & CON_BRL) + flags[index++] = 'b'; + if (con->flags & CON_ANYTIME) + flags[index++] = 'a'; + if (current_dev == dev) + flags[index++] = '*'; + flags[index] = 0; + + seq_printf(m, "%s%d%n", con->name, con->index, &len); + len = 21 - len; + if (len < 1) + len = 1; + seq_printf(m, "%*c", len, ' '); + seq_printf(m, "%c%c%c (%s)%n", con->read ? 'R' : '-', + con->write ? 'W' : '-', con->unblank ? 'U' : '-', + flags, &len); + len = 13 - len; + if (len < 1) + len = 1; + seq_printf(m, "%*c%4d:%d\n", len, ' ', MAJOR(dev), MINOR(dev)); + + return 0; +} + +/* iterator for consoles */ +static void *c_start(struct seq_file *m, loff_t *pos) +{ + struct console *con; + loff_t off = 0; + + if (*pos == 0) + return SEQ_START_TOKEN; + + acquire_console_sem(); + for (con = console_drivers; con; con = con->next) { + if (!con->device) + continue; + if (++off == *pos) + break; + } + release_console_sem(); + + return con; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct console *con; + + acquire_console_sem(); + if (v == SEQ_START_TOKEN) + con = console_drivers; + else + con = ((struct console *)v)->next; + for (; con; con = con->next) { + if (!con->device) + continue; + ++*pos; + break; + } + release_console_sem(); + + return con; +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +static const struct seq_operations tty_consoles_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_console_dev +}; + +/* + * Used for open /proc/tty/consoles. Before this detect + * the device ID of file descriptor 0 of the current + * reading task if a character device... + */ +static int tty_consoles_open(struct inode *inode, struct file *file) +{ + struct files_struct *curfiles; + + current_dev = 0; + curfiles = get_files_struct(current); + if (curfiles) { + const struct file *curfp; + spin_lock(&curfiles->file_lock); + curfp = fcheck_files(curfiles, 0); + if (curfp && curfp->private_data) { + const struct inode *inode; + dget(curfp->f_dentry); + inode = curfp->f_dentry->d_inode; + if (S_ISCHR(inode->i_mode)) { + struct tty_struct *tty; + tty = (struct tty_struct *)curfp->private_data; + if (tty && tty->magic == TTY_MAGIC) { + tty = tty_pair_get_tty(tty); + current_dev = tty_devnum(tty); + } + } + dput(curfp->f_dentry); + } + spin_unlock(&curfiles->file_lock); + put_files_struct(curfiles); + } + return seq_open(file, &tty_consoles_op); +} + +static const struct file_operations proc_tty_consoles_operations = { + .open = tty_consoles_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + /* * This function is called by tty_register_driver() to handle * registering the driver's /proc handler into /proc/tty/driver/ @@ -186,4 +343,5 @@ void __init proc_tty_init(void) proc_tty_driver = proc_mkdir_mode("tty/driver", S_IRUSR|S_IXUSR, NULL); proc_create("tty/ldiscs", 0, NULL, &tty_ldiscs_proc_fops); proc_create("tty/drivers", 0, NULL, &proc_tty_drivers_operations); + proc_create("tty/consoles", 0, NULL, &proc_tty_consoles_operations); } -- cgit v1.2.3-18-g5258 From 9566a7a851eb7201e3207eab53ee81efd0850fee Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Tue, 10 Aug 2010 00:58:41 +0900 Subject: nilfs2: accept future revisions Compatibility of nilfs partitions is now managed with three feature sets. This changes old compatibility check with revision number so that it can accept future revisions. Note that we can stop support of experimental versions of nilfs that doesn't know the feature sets by incrementing NILFS_CURRENT_REV. We don't have to do it soon, but it would be a possible option whenever the need arises. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/the_nilfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index ba7c10c917f..461b7211e14 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -468,8 +468,8 @@ static unsigned long long nilfs_max_size(unsigned int blkbits) static int nilfs_store_disk_layout(struct the_nilfs *nilfs, struct nilfs_super_block *sbp) { - if (le32_to_cpu(sbp->s_rev_level) != NILFS_CURRENT_REV) { - printk(KERN_ERR "NILFS: revision mismatch " + if (le32_to_cpu(sbp->s_rev_level) < NILFS_MIN_SUPP_REV) { + printk(KERN_ERR "NILFS: unsupported revision " "(superblock rev.=%d.%d, current rev.=%d.%d). " "Please check the version of mkfs.nilfs.\n", le32_to_cpu(sbp->s_rev_level), -- cgit v1.2.3-18-g5258 From b91c9a97c9333c87fe2a0c94b3b22b24df1c5fc2 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 20 Aug 2010 23:46:06 +0900 Subject: nilfs2: allow nilfs_destroy_inode to destroy metadata file inodes The current nilfs_destroy_inode() doesn't handle metadata file inodes including gc inodes (dummy inodes used for garbage collection). This allows nilfs_destroy_inode() to destroy inodes of metadata files. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/mdt.c | 2 -- fs/nilfs2/super.c | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index d01aff4957d..ee943a342e1 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -577,7 +577,5 @@ void nilfs_mdt_destroy(struct inode *inode) nilfs_palloc_destroy_cache(inode); nilfs_mdt_clear(inode); - kfree(mdi->mi_bgl); /* kfree(NULL) is safe */ - kfree(mdi); nilfs_destroy_inode(inode); } diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 9f4913f7840..51576b4dbf7 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -167,6 +167,12 @@ struct inode *nilfs_alloc_inode(struct super_block *sb) void nilfs_destroy_inode(struct inode *inode) { + struct nilfs_mdt_info *mdi = NILFS_MDT(inode); + + if (mdi) { + kfree(mdi->mi_bgl); /* kfree(NULL) is safe */ + kfree(mdi); + } kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode)); } -- cgit v1.2.3-18-g5258 From 7d6cd92fe2de3c286b8e95f43223f09db9302635 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 21 Aug 2010 00:30:39 +0900 Subject: nilfs2: allow nilfs_dirty_inode to mark metadata file inodes dirty This allows sop->dirty_inode callback function (nilfs_dirty_inode) to handle metadata file inodes. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/inode.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index eccb2f2e231..f1750caa362 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -808,6 +808,7 @@ int nilfs_mark_inode_dirty(struct inode *inode) void nilfs_dirty_inode(struct inode *inode) { struct nilfs_transaction_info ti; + struct nilfs_mdt_info *mdi = NILFS_MDT(inode); if (is_bad_inode(inode)) { nilfs_warning(inode->i_sb, __func__, @@ -815,6 +816,10 @@ void nilfs_dirty_inode(struct inode *inode) dump_stack(); return; } + if (mdi) { + nilfs_mdt_mark_dirty(inode); + return; + } nilfs_transaction_begin(inode->i_sb, &ti, 0); nilfs_mark_inode_dirty(inode); nilfs_transaction_commit(inode->i_sb); /* never fails */ -- cgit v1.2.3-18-g5258 From 6c43f41000312fefa482c3bfdd97e7f81d6be0ec Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 20 Aug 2010 20:10:38 +0900 Subject: nilfs2: keep zero value in i_cno except for gc-inodes On-memory inode structures of nilfs have a member "i_cno" which stores a checkpoint number related to the inode. For gc-inodes, this field indicates version of data each gc-inode caches for GC. Log writer temporarily uses "i_cno" to transfer the latest checkpoint number. This stops the latter use and lets only gc-inodes use it. The purpose of this patch is to allow the successive change use "i_cno" for inode lookup. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/segment.c | 29 ++++++++++++++--------------- fs/nilfs2/segment.h | 3 ++- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 9fd051a33c4..eee4b223c29 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -366,8 +366,7 @@ static int nilfs_segctor_reset_segment_buffer(struct nilfs_sc_info *sci) if (nilfs_doing_gc()) flags = NILFS_SS_GC; - err = nilfs_segbuf_reset(segbuf, flags, sci->sc_seg_ctime, - sci->sc_sbi->s_nilfs->ns_cno); + err = nilfs_segbuf_reset(segbuf, flags, sci->sc_seg_ctime, sci->sc_cno); if (unlikely(err)) return err; @@ -440,17 +439,26 @@ static void nilfs_segctor_end_finfo(struct nilfs_sc_info *sci, struct nilfs_finfo *finfo; struct nilfs_inode_info *ii; struct nilfs_segment_buffer *segbuf; + __u64 cno; if (sci->sc_blk_cnt == 0) return; ii = NILFS_I(inode); + + if (test_bit(NILFS_I_GCINODE, &ii->i_state)) + cno = ii->i_cno; + else if (NILFS_ROOT_METADATA_FILE(inode->i_ino)) + cno = 0; + else + cno = sci->sc_cno; + finfo = nilfs_segctor_map_segsum_entry(sci, &sci->sc_finfo_ptr, sizeof(*finfo)); finfo->fi_ino = cpu_to_le64(inode->i_ino); finfo->fi_nblocks = cpu_to_le32(sci->sc_blk_cnt); finfo->fi_ndatablk = cpu_to_le32(sci->sc_datablk_cnt); - finfo->fi_cno = cpu_to_le64(ii->i_cno); + finfo->fi_cno = cpu_to_le64(cno); segbuf = sci->sc_curseg; segbuf->sb_sum.sumbytes = sci->sc_binfo_ptr.offset + @@ -1976,7 +1984,6 @@ static int nilfs_segctor_check_in_files(struct nilfs_sc_info *sci, struct nilfs_sb_info *sbi) { struct nilfs_inode_info *ii, *n; - __u64 cno = sbi->s_nilfs->ns_cno; spin_lock(&sbi->s_inode_lock); retry: @@ -2002,7 +2009,6 @@ static int nilfs_segctor_check_in_files(struct nilfs_sc_info *sci, brelse(ibh); goto retry; } - ii->i_cno = cno; clear_bit(NILFS_I_QUEUED, &ii->i_state); set_bit(NILFS_I_BUSY, &ii->i_state); @@ -2011,8 +2017,6 @@ static int nilfs_segctor_check_in_files(struct nilfs_sc_info *sci, } spin_unlock(&sbi->s_inode_lock); - NILFS_I(sbi->s_ifile)->i_cno = cno; - return 0; } @@ -2021,19 +2025,13 @@ static void nilfs_segctor_check_out_files(struct nilfs_sc_info *sci, { struct nilfs_transaction_info *ti = current->journal_info; struct nilfs_inode_info *ii, *n; - __u64 cno = sbi->s_nilfs->ns_cno; spin_lock(&sbi->s_inode_lock); list_for_each_entry_safe(ii, n, &sci->sc_dirty_files, i_dirty) { if (!test_and_clear_bit(NILFS_I_UPDATED, &ii->i_state) || - test_bit(NILFS_I_DIRTY, &ii->i_state)) { - /* The current checkpoint number (=nilfs->ns_cno) is - changed between check-in and check-out only if the - super root is written out. So, we can update i_cno - for the inodes that remain in the dirty list. */ - ii->i_cno = cno; + test_bit(NILFS_I_DIRTY, &ii->i_state)) continue; - } + clear_bit(NILFS_I_BUSY, &ii->i_state); brelse(ii->i_bh); ii->i_bh = NULL; @@ -2054,6 +2052,7 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) int err; sci->sc_stage.scnt = NILFS_ST_INIT; + sci->sc_cno = nilfs->ns_cno; err = nilfs_segctor_check_in_files(sci, sbi); if (unlikely(err)) diff --git a/fs/nilfs2/segment.h b/fs/nilfs2/segment.h index 17c487bd815..675d932148a 100644 --- a/fs/nilfs2/segment.h +++ b/fs/nilfs2/segment.h @@ -107,6 +107,7 @@ struct nilfs_segsum_pointer { * @sc_datablk_cnt: Data block count of a file * @sc_nblk_this_inc: Number of blocks included in the current logical segment * @sc_seg_ctime: Creation time + * @sc_cno: checkpoint number of current log * @sc_flags: Internal flags * @sc_state_lock: spinlock for sc_state and so on * @sc_state: Segctord state flags @@ -156,7 +157,7 @@ struct nilfs_sc_info { unsigned long sc_datablk_cnt; unsigned long sc_nblk_this_inc; time_t sc_seg_ctime; - + __u64 sc_cno; unsigned long sc_flags; spinlock_t sc_state_lock; -- cgit v1.2.3-18-g5258 From 0e14a3595bddedfb27b51a6b0a29b5173aa2511a Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 20 Aug 2010 21:20:29 +0900 Subject: nilfs2: use iget5_locked to get inode This uses iget5_locked instead of iget_locked so that gc cache can look up inodes with an inode number and an optional checkpoint number. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/inode.c | 37 ++++++++++++++++++++++++++++++++++--- fs/nilfs2/super.c | 1 + 2 files changed, 35 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index f1750caa362..6e9df85b582 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -34,6 +34,11 @@ #include "cpfile.h" #include "ifile.h" +struct nilfs_iget_args { + u64 ino; + __u64 cno; + int for_gc; +}; /** * nilfs_get_block() - get a file block on the filesystem (callback function) @@ -320,7 +325,6 @@ struct inode *nilfs_new_inode(struct inode *dir, int mode) /* ii->i_file_acl = 0; */ /* ii->i_dir_acl = 0; */ ii->i_dir_start_lookup = 0; - ii->i_cno = 0; nilfs_set_inode_flags(inode); spin_lock(&sbi->s_next_gen_lock); inode->i_generation = sbi->s_next_generation++; @@ -410,7 +414,6 @@ int nilfs_read_inode_common(struct inode *inode, 0 : le32_to_cpu(raw_inode->i_dir_acl); #endif ii->i_dir_start_lookup = 0; - ii->i_cno = 0; inode->i_generation = le32_to_cpu(raw_inode->i_generation); if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || @@ -476,12 +479,40 @@ static int __nilfs_read_inode(struct super_block *sb, unsigned long ino, return err; } +static int nilfs_iget_test(struct inode *inode, void *opaque) +{ + struct nilfs_iget_args *args = opaque; + struct nilfs_inode_info *ii; + + if (args->ino != inode->i_ino) + return 0; + + ii = NILFS_I(inode); + if (!test_bit(NILFS_I_GCINODE, &ii->i_state)) + return !args->for_gc; + + return args->for_gc && args->cno == ii->i_cno; +} + +static int nilfs_iget_set(struct inode *inode, void *opaque) +{ + struct nilfs_iget_args *args = opaque; + + inode->i_ino = args->ino; + if (args->for_gc) { + NILFS_I(inode)->i_state = 1 << NILFS_I_GCINODE; + NILFS_I(inode)->i_cno = args->cno; + } + return 0; +} + struct inode *nilfs_iget(struct super_block *sb, unsigned long ino) { + struct nilfs_iget_args args = { .ino = ino, .cno = 0, .for_gc = 0 }; struct inode *inode; int err; - inode = iget_locked(sb, ino); + inode = iget5_locked(sb, ino, nilfs_iget_test, nilfs_iget_set, &args); if (unlikely(!inode)) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 51576b4dbf7..f3a00a3b2a0 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -155,6 +155,7 @@ struct inode *nilfs_alloc_inode_common(struct the_nilfs *nilfs) return NULL; ii->i_bh = NULL; ii->i_state = 0; + ii->i_cno = 0; ii->vfs_inode.i_version = 1; nilfs_btnode_cache_init(&ii->i_btnode_cache, nilfs->ns_bdi); return &ii->vfs_inode; -- cgit v1.2.3-18-g5258 From 5e19a995f4ad8a8f20749a396bb01ebb6d4df96c Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 21 Aug 2010 22:01:51 +0900 Subject: nilfs2: separate initializer of metadata file inode This separates a part of initialization code of metadata file inode, and makes it available from the nilfs iget function that a later patch will add to. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/gcinode.c | 6 +++++- fs/nilfs2/mdt.c | 44 +++++++++++++++++++++++++++----------------- fs/nilfs2/mdt.h | 4 +++- 3 files changed, 35 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c index bed3a783129..cd19a3709bd 100644 --- a/fs/nilfs2/gcinode.c +++ b/fs/nilfs2/gcinode.c @@ -225,10 +225,14 @@ static struct inode *alloc_gcinode(struct the_nilfs *nilfs, ino_t ino, struct inode *inode; struct nilfs_inode_info *ii; - inode = nilfs_mdt_new_common(nilfs, NULL, ino, GFP_NOFS, 0); + inode = nilfs_mdt_new_common(nilfs, NULL, ino); if (!inode) return NULL; + if (nilfs_mdt_init(inode, nilfs, GFP_NOFS, 0) < 0) { + nilfs_destroy_inode(inode); + return NULL; + } inode->i_op = NULL; inode->i_fop = NULL; inode->i_mapping->a_ops = &def_gcinode_aops; diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index ee943a342e1..73e5da3b097 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -439,6 +439,27 @@ static const struct address_space_operations def_mdt_aops = { static const struct inode_operations def_mdt_iops; static const struct file_operations def_mdt_fops; + +int nilfs_mdt_init(struct inode *inode, struct the_nilfs *nilfs, + gfp_t gfp_mask, size_t objsz) +{ + struct nilfs_mdt_info *mi; + + mi = kzalloc(max(sizeof(*mi), objsz), GFP_NOFS); + if (!mi) + return -ENOMEM; + + mi->mi_nilfs = nilfs; + init_rwsem(&mi->mi_sem); + inode->i_private = mi; + + inode->i_mode = S_IFREG; + mapping_set_gfp_mask(inode->i_mapping, gfp_mask); + inode->i_mapping->backing_dev_info = nilfs->ns_bdi; + + return 0; +} + /* * NILFS2 uses pseudo inodes for meta data files such as DAT, cpfile, sufile, * ifile, or gcinodes. This allows the B-tree code and segment constructor @@ -454,12 +475,10 @@ static const struct file_operations def_mdt_fops; * @nilfs: nilfs object * @sb: super block instance the metadata file belongs to * @ino: inode number - * @gfp_mask: gfp mask for data pages - * @objsz: size of the private object attached to inode->i_private */ struct inode * nilfs_mdt_new_common(struct the_nilfs *nilfs, struct super_block *sb, - ino_t ino, gfp_t gfp_mask, size_t objsz) + ino_t ino) { struct inode *inode = nilfs_alloc_inode_common(nilfs); @@ -467,15 +486,6 @@ nilfs_mdt_new_common(struct the_nilfs *nilfs, struct super_block *sb, return NULL; else { struct address_space * const mapping = &inode->i_data; - struct nilfs_mdt_info *mi; - - mi = kzalloc(max(sizeof(*mi), objsz), GFP_NOFS); - if (!mi) { - nilfs_destroy_inode(inode); - return NULL; - } - mi->mi_nilfs = nilfs; - init_rwsem(&mi->mi_sem); inode->i_sb = sb; /* sb may be NULL for some meta data files */ inode->i_blkbits = nilfs->ns_blocksize_bits; @@ -483,8 +493,6 @@ nilfs_mdt_new_common(struct the_nilfs *nilfs, struct super_block *sb, atomic_set(&inode->i_count, 1); inode->i_nlink = 1; inode->i_ino = ino; - inode->i_mode = S_IFREG; - inode->i_private = mi; #ifdef INIT_UNUSED_INODE_FIELDS atomic_set(&inode->i_writecount, 0); @@ -515,9 +523,7 @@ nilfs_mdt_new_common(struct the_nilfs *nilfs, struct super_block *sb, mapping->host = NULL; /* instead of inode */ mapping->flags = 0; - mapping_set_gfp_mask(mapping, gfp_mask); mapping->assoc_mapping = NULL; - mapping->backing_dev_info = nilfs->ns_bdi; inode->i_mapping = mapping; } @@ -530,10 +536,14 @@ struct inode *nilfs_mdt_new(struct the_nilfs *nilfs, struct super_block *sb, { struct inode *inode; - inode = nilfs_mdt_new_common(nilfs, sb, ino, NILFS_MDT_GFP, objsz); + inode = nilfs_mdt_new_common(nilfs, sb, ino); if (!inode) return NULL; + if (nilfs_mdt_init(inode, nilfs, NILFS_MDT_GFP, objsz) < 0) { + nilfs_destroy_inode(inode); + return NULL; + } inode->i_op = &def_mdt_iops; inode->i_fop = &def_mdt_fops; inode->i_mapping->a_ops = &def_mdt_aops; diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h index 6c4bbb0470f..f44560224bd 100644 --- a/fs/nilfs2/mdt.h +++ b/fs/nilfs2/mdt.h @@ -76,10 +76,12 @@ int nilfs_mdt_forget_block(struct inode *, unsigned long); int nilfs_mdt_mark_block_dirty(struct inode *, unsigned long); int nilfs_mdt_fetch_dirty(struct inode *); +int nilfs_mdt_init(struct inode *inode, struct the_nilfs *nilfs, + gfp_t gfp_mask, size_t objsz); struct inode *nilfs_mdt_new(struct the_nilfs *, struct super_block *, ino_t, size_t); struct inode *nilfs_mdt_new_common(struct the_nilfs *, struct super_block *, - ino_t, gfp_t, size_t); + ino_t); void nilfs_mdt_destroy(struct inode *); void nilfs_mdt_set_entry_size(struct inode *, unsigned, unsigned); void nilfs_mdt_set_shadow(struct inode *, struct inode *); -- cgit v1.2.3-18-g5258 From 263d90cefc7d82a01c296c59532ff59d67c63509 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 20 Aug 2010 19:06:11 +0900 Subject: nilfs2: remove own inode hash used for GC This uses inode hash function that vfs provides instead of the own hash table for caching gc inodes. This finally removes the own inode hash from nilfs. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/gcinode.c | 140 +++++++++----------------------------------------- fs/nilfs2/inode.c | 22 ++++++++ fs/nilfs2/ioctl.c | 17 +++--- fs/nilfs2/nilfs.h | 9 ++-- fs/nilfs2/segment.c | 3 +- fs/nilfs2/the_nilfs.c | 8 +-- fs/nilfs2/the_nilfs.h | 7 +-- 7 files changed, 64 insertions(+), 142 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c index cd19a3709bd..34f8f84a22e 100644 --- a/fs/nilfs2/gcinode.c +++ b/fs/nilfs2/gcinode.c @@ -28,13 +28,6 @@ * gcinodes), and this file provides lookup function of the dummy * inodes and their buffer read function. * - * Since NILFS2 keeps up multiple checkpoints/snapshots across GC, it - * has to treat blocks that belong to a same file but have different - * checkpoint numbers. To avoid interference among generations, dummy - * inodes are managed separately from actual inodes, and their lookup - * function (nilfs_gc_iget) is designed to be specified with a - * checkpoint number argument as well as an inode number. - * * Buffers and pages held by the dummy inodes will be released each * time after they are copied to a new log. Dirty blocks made on the * current generation and the blocks to be moved by GC never overlap @@ -180,124 +173,41 @@ int nilfs_gccache_wait_and_mark_dirty(struct buffer_head *bh) return 0; } -/* - * nilfs_init_gccache() - allocate and initialize gc_inode hash table - * @nilfs - the_nilfs - * - * Return Value: On success, 0. - * On error, a negative error code is returned. - */ -int nilfs_init_gccache(struct the_nilfs *nilfs) -{ - int loop; - - BUG_ON(nilfs->ns_gc_inodes_h); - - INIT_LIST_HEAD(&nilfs->ns_gc_inodes); - - nilfs->ns_gc_inodes_h = - kmalloc(sizeof(struct hlist_head) * NILFS_GCINODE_HASH_SIZE, - GFP_NOFS); - if (nilfs->ns_gc_inodes_h == NULL) - return -ENOMEM; - - for (loop = 0; loop < NILFS_GCINODE_HASH_SIZE; loop++) - INIT_HLIST_HEAD(&nilfs->ns_gc_inodes_h[loop]); - return 0; -} - -/* - * nilfs_destroy_gccache() - free gc_inode hash table - * @nilfs - the nilfs - */ -void nilfs_destroy_gccache(struct the_nilfs *nilfs) +int nilfs_init_gcinode(struct inode *inode) { - if (nilfs->ns_gc_inodes_h) { - nilfs_remove_all_gcinode(nilfs); - kfree(nilfs->ns_gc_inodes_h); - nilfs->ns_gc_inodes_h = NULL; - } -} - -static struct inode *alloc_gcinode(struct the_nilfs *nilfs, ino_t ino, - __u64 cno) -{ - struct inode *inode; - struct nilfs_inode_info *ii; - - inode = nilfs_mdt_new_common(nilfs, NULL, ino); - if (!inode) - return NULL; - - if (nilfs_mdt_init(inode, nilfs, GFP_NOFS, 0) < 0) { - nilfs_destroy_inode(inode); - return NULL; - } - inode->i_op = NULL; - inode->i_fop = NULL; - inode->i_mapping->a_ops = &def_gcinode_aops; - - ii = NILFS_I(inode); - ii->i_cno = cno; - ii->i_flags = 0; - ii->i_state = 1 << NILFS_I_GCINODE; - ii->i_bh = NULL; - nilfs_bmap_init_gc(ii->i_bmap); + struct nilfs_inode_info *ii = NILFS_I(inode); + struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; + int ret; - return inode; -} + ret = nilfs_mdt_init(inode, nilfs, GFP_NOFS, 0); + if (!ret) { + inode->i_mapping->a_ops = &def_gcinode_aops; -static unsigned long ihash(ino_t ino, __u64 cno) -{ - return hash_long((unsigned long)((ino << 2) + cno), - NILFS_GCINODE_HASH_BITS); -} + ii->i_flags = 0; + nilfs_bmap_init_gc(ii->i_bmap); -/* - * nilfs_gc_iget() - find or create gc inode with specified (ino,cno) - */ -struct inode *nilfs_gc_iget(struct the_nilfs *nilfs, ino_t ino, __u64 cno) -{ - struct hlist_head *head = nilfs->ns_gc_inodes_h + ihash(ino, cno); - struct hlist_node *node; - struct inode *inode; - - hlist_for_each_entry(inode, node, head, i_hash) { - if (inode->i_ino == ino && NILFS_I(inode)->i_cno == cno) - return inode; - } - - inode = alloc_gcinode(nilfs, ino, cno); - if (likely(inode)) { - hlist_add_head(&inode->i_hash, head); + /* + * Add the inode to GC inode list. Garbage Collection + * is serialized and no two processes manipulate the + * list simultaneously. + */ + igrab(inode); list_add(&NILFS_I(inode)->i_dirty, &nilfs->ns_gc_inodes); } - return inode; -} - -/* - * nilfs_clear_gcinode() - clear and free a gc inode - */ -void nilfs_clear_gcinode(struct inode *inode) -{ - nilfs_mdt_destroy(inode); + return ret; } -/* - * nilfs_remove_all_gcinode() - remove all inodes from the_nilfs +/** + * nilfs_remove_all_gcinodes() - remove all unprocessed gc inodes */ -void nilfs_remove_all_gcinode(struct the_nilfs *nilfs) +void nilfs_remove_all_gcinodes(struct the_nilfs *nilfs) { - struct hlist_head *head = nilfs->ns_gc_inodes_h; - struct hlist_node *node, *n; - struct inode *inode; - int loop; + struct list_head *head = &nilfs->ns_gc_inodes; + struct nilfs_inode_info *ii; - for (loop = 0; loop < NILFS_GCINODE_HASH_SIZE; loop++, head++) { - hlist_for_each_entry_safe(inode, node, n, head, i_hash) { - hlist_del_init(&inode->i_hash); - list_del_init(&NILFS_I(inode)->i_dirty); - nilfs_clear_gcinode(inode); /* might sleep */ - } + while (!list_empty(head)) { + ii = list_first_entry(head, struct nilfs_inode_info, i_dirty); + list_del_init(&ii->i_dirty); + iput(&ii->vfs_inode); } } diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 6e9df85b582..82cfdbc43e1 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -527,6 +527,28 @@ struct inode *nilfs_iget(struct super_block *sb, unsigned long ino) return inode; } +struct inode *nilfs_iget_for_gc(struct super_block *sb, unsigned long ino, + __u64 cno) +{ + struct nilfs_iget_args args = { .ino = ino, .cno = cno, .for_gc = 1 }; + struct inode *inode; + int err; + + inode = iget5_locked(sb, ino, nilfs_iget_test, nilfs_iget_set, &args); + if (unlikely(!inode)) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + err = nilfs_init_gcinode(inode); + if (unlikely(err)) { + iget_failed(inode); + return ERR_PTR(err); + } + unlock_new_inode(inode); + return inode; +} + void nilfs_write_inode_common(struct inode *inode, struct nilfs_inode *raw_inode, int has_bmap) { diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index 0442ee3b394..2ee6843c2e8 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c @@ -333,7 +333,7 @@ static int nilfs_ioctl_move_inode_block(struct inode *inode, return 0; } -static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs, +static int nilfs_ioctl_move_blocks(struct super_block *sb, struct nilfs_argv *argv, void *buf) { size_t nmembs = argv->v_nmembs; @@ -348,7 +348,7 @@ static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs, for (i = 0, vdesc = buf; i < nmembs; ) { ino = vdesc->vd_ino; cno = vdesc->vd_cno; - inode = nilfs_gc_iget(nilfs, ino, cno); + inode = nilfs_iget_for_gc(sb, ino, cno); if (unlikely(inode == NULL)) { ret = -ENOMEM; goto failed; @@ -356,11 +356,15 @@ static int nilfs_ioctl_move_blocks(struct the_nilfs *nilfs, do { ret = nilfs_ioctl_move_inode_block(inode, vdesc, &buffers); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + iput(inode); goto failed; + } vdesc++; } while (++i < nmembs && vdesc->vd_ino == ino && vdesc->vd_cno == cno); + + iput(inode); /* The inode still remains in GC inode list */ } list_for_each_entry_safe(bh, n, &buffers, b_assoc_buffers) { @@ -566,7 +570,7 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, } /* - * nilfs_ioctl_move_blocks() will call nilfs_gc_iget(), + * nilfs_ioctl_move_blocks() will call nilfs_iget_for_gc(), * which will operates an inode list without blocking. * To protect the list from concurrent operations, * nilfs_ioctl_move_blocks should be atomic operation. @@ -576,15 +580,14 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, goto out_free; } - ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], kbufs[0]); + ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]); if (ret < 0) printk(KERN_ERR "NILFS: GC failed during preparation: " "cannot read source blocks: err=%d\n", ret); else ret = nilfs_clean_segments(inode->i_sb, argv, kbufs); - if (ret < 0) - nilfs_remove_all_gcinode(nilfs); + nilfs_remove_all_gcinodes(nilfs); clear_nilfs_gc_running(nilfs); out_free: diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index d3d54046e5f..797cd437970 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -248,6 +248,8 @@ extern void nilfs_set_inode_flags(struct inode *); extern int nilfs_read_inode_common(struct inode *, struct nilfs_inode *); extern void nilfs_write_inode_common(struct inode *, struct nilfs_inode *, int); extern struct inode *nilfs_iget(struct super_block *, unsigned long); +extern struct inode *nilfs_iget_for_gc(struct super_block *sb, + unsigned long ino, __u64 cno); extern void nilfs_update_inode(struct inode *, struct buffer_head *); extern void nilfs_truncate(struct inode *); extern void nilfs_evict_inode(struct inode *); @@ -292,11 +294,8 @@ int nilfs_gccache_submit_read_data(struct inode *, sector_t, sector_t, __u64, int nilfs_gccache_submit_read_node(struct inode *, sector_t, __u64, struct buffer_head **); int nilfs_gccache_wait_and_mark_dirty(struct buffer_head *); -int nilfs_init_gccache(struct the_nilfs *); -void nilfs_destroy_gccache(struct the_nilfs *); -void nilfs_clear_gcinode(struct inode *); -struct inode *nilfs_gc_iget(struct the_nilfs *, ino_t, __u64); -void nilfs_remove_all_gcinode(struct the_nilfs *); +int nilfs_init_gcinode(struct inode *inode); +void nilfs_remove_all_gcinodes(struct the_nilfs *nilfs); /* gcdat.c */ int nilfs_init_gcdat_inode(struct the_nilfs *); diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index eee4b223c29..9cf71389f36 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2451,9 +2451,8 @@ nilfs_remove_written_gcinodes(struct the_nilfs *nilfs, struct list_head *head) list_for_each_entry_safe(ii, n, head, i_dirty) { if (!test_bit(NILFS_I_UPDATED, &ii->i_state)) continue; - hlist_del_init(&ii->vfs_inode.i_hash); list_del_init(&ii->i_dirty); - nilfs_clear_gcinode(&ii->vfs_inode); + iput(&ii->vfs_inode); } } diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 461b7211e14..6a012b9e1b3 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -87,8 +87,8 @@ static struct the_nilfs *alloc_nilfs(struct block_device *bdev) init_rwsem(&nilfs->ns_writer_sem); INIT_LIST_HEAD(&nilfs->ns_list); INIT_LIST_HEAD(&nilfs->ns_supers); + INIT_LIST_HEAD(&nilfs->ns_gc_inodes); spin_lock_init(&nilfs->ns_last_segment_lock); - nilfs->ns_gc_inodes_h = NULL; init_rwsem(&nilfs->ns_segctor_sem); return nilfs; @@ -164,7 +164,6 @@ void put_nilfs(struct the_nilfs *nilfs) nilfs_mdt_destroy(nilfs->ns_gc_dat); } if (nilfs_init(nilfs)) { - nilfs_destroy_gccache(nilfs); brelse(nilfs->ns_sbh[0]); brelse(nilfs->ns_sbh[1]); } @@ -736,11 +735,6 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) if (err) goto failed_sbh; - /* Initialize gcinode cache */ - err = nilfs_init_gccache(nilfs); - if (err) - goto failed_sbh; - set_nilfs_init(nilfs); err = 0; out: diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index f785a7b0ab9..c7ecd0c623a 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -81,7 +81,6 @@ enum { * @ns_sufile: segusage file inode * @ns_gc_dat: shadow inode of the DAT file inode for GC * @ns_gc_inodes: dummy inodes to keep live blocks - * @ns_gc_inodes_h: hash list to keep dummy inode holding live blocks * @ns_blocksize_bits: bit length of block size * @ns_blocksize: block size * @ns_nsegments: number of segments in filesystem @@ -165,9 +164,8 @@ struct the_nilfs { struct inode *ns_sufile; struct inode *ns_gc_dat; - /* GC inode list and hash table head */ + /* GC inode list */ struct list_head ns_gc_inodes; - struct hlist_head *ns_gc_inodes_h; /* Disk layout information (static) */ unsigned int ns_blocksize_bits; @@ -182,9 +180,6 @@ struct the_nilfs { u32 ns_crc_seed; }; -#define NILFS_GCINODE_HASH_BITS 8 -#define NILFS_GCINODE_HASH_SIZE (1< Date: Sat, 14 Aug 2010 12:59:15 +0900 Subject: nilfs2: add checkpoint tree to nilfs object To hold multiple versions of a filesystem in one sb instance, a new on-memory structure is necessary to handle one or more checkpoints. This adds a red-black tree of checkpoints to nilfs object, and adds lookup and create functions for them. Each checkpoint is represented by "nilfs_root" structure, and this structure has rb_node to configure the rb-tree. The nilfs_root object is identified with a checkpoint number. For each snapshot, a nilfs_root object is allocated and the checkpoint number of snapshot is assigned to it. For a regular mount (i.e. current mode mount), NILFS_CPTREE_CURRENT_CNO constant is assigned to the corresponding nilfs_root object. Each nilfs_root object has an ifile inode and some counters. These items will displace those of nilfs_sb_info structure in successive patches. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/the_nilfs.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/nilfs2/the_nilfs.h | 42 +++++++++++++++++++++++ 2 files changed, 134 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 6a012b9e1b3..f1d599273d9 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -89,6 +89,8 @@ static struct the_nilfs *alloc_nilfs(struct block_device *bdev) INIT_LIST_HEAD(&nilfs->ns_supers); INIT_LIST_HEAD(&nilfs->ns_gc_inodes); spin_lock_init(&nilfs->ns_last_segment_lock); + nilfs->ns_cptree = RB_ROOT; + spin_lock_init(&nilfs->ns_cptree_lock); init_rwsem(&nilfs->ns_segctor_sem); return nilfs; @@ -809,6 +811,96 @@ int nilfs_near_disk_full(struct the_nilfs *nilfs) return ncleansegs <= nilfs->ns_nrsvsegs + nincsegs; } +struct nilfs_root *nilfs_lookup_root(struct the_nilfs *nilfs, __u64 cno) +{ + struct rb_node *n; + struct nilfs_root *root; + + spin_lock(&nilfs->ns_cptree_lock); + n = nilfs->ns_cptree.rb_node; + while (n) { + root = rb_entry(n, struct nilfs_root, rb_node); + + if (cno < root->cno) { + n = n->rb_left; + } else if (cno > root->cno) { + n = n->rb_right; + } else { + atomic_inc(&root->count); + spin_unlock(&nilfs->ns_cptree_lock); + return root; + } + } + spin_unlock(&nilfs->ns_cptree_lock); + + return NULL; +} + +struct nilfs_root * +nilfs_find_or_create_root(struct the_nilfs *nilfs, __u64 cno) +{ + struct rb_node **p, *parent; + struct nilfs_root *root, *new; + + root = nilfs_lookup_root(nilfs, cno); + if (root) + return root; + + new = kmalloc(sizeof(*root), GFP_KERNEL); + if (!new) + return NULL; + + spin_lock(&nilfs->ns_cptree_lock); + + p = &nilfs->ns_cptree.rb_node; + parent = NULL; + + while (*p) { + parent = *p; + root = rb_entry(parent, struct nilfs_root, rb_node); + + if (cno < root->cno) { + p = &(*p)->rb_left; + } else if (cno > root->cno) { + p = &(*p)->rb_right; + } else { + atomic_inc(&root->count); + spin_unlock(&nilfs->ns_cptree_lock); + kfree(new); + return root; + } + } + + new->cno = cno; + new->ifile = NULL; + new->nilfs = nilfs; + atomic_set(&new->count, 1); + atomic_set(&new->inodes_count, 0); + atomic_set(&new->blocks_count, 0); + + rb_link_node(&new->rb_node, parent, p); + rb_insert_color(&new->rb_node, &nilfs->ns_cptree); + + spin_unlock(&nilfs->ns_cptree_lock); + + return new; +} + +void nilfs_put_root(struct nilfs_root *root) +{ + if (atomic_dec_and_test(&root->count)) { + struct the_nilfs *nilfs = root->nilfs; + + spin_lock(&nilfs->ns_cptree_lock); + rb_erase(&root->rb_node, &nilfs->ns_cptree); + spin_unlock(&nilfs->ns_cptree_lock); + if (root->ifile) + nilfs_mdt_destroy(root->ifile); + + kfree(root); + } +} + /** * nilfs_find_sbinfo - find existing nilfs_sb_info structure * @nilfs: nilfs object diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index c7ecd0c623a..0afede68a9e 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -80,6 +81,8 @@ enum { * @ns_cpfile: checkpoint file inode * @ns_sufile: segusage file inode * @ns_gc_dat: shadow inode of the DAT file inode for GC + * @ns_cptree: rb-tree of all mounted checkpoints (nilfs_root) + * @ns_cptree_lock: lock protecting @ns_cptree * @ns_gc_inodes: dummy inodes to keep live blocks * @ns_blocksize_bits: bit length of block size * @ns_blocksize: block size @@ -164,6 +167,10 @@ struct the_nilfs { struct inode *ns_sufile; struct inode *ns_gc_dat; + /* Checkpoint tree */ + struct rb_root ns_cptree; + spinlock_t ns_cptree_lock; + /* GC inode list */ struct list_head ns_gc_inodes; @@ -200,6 +207,32 @@ THE_NILFS_FNS(DISCONTINUED, discontinued) THE_NILFS_FNS(GC_RUNNING, gc_running) THE_NILFS_FNS(SB_DIRTY, sb_dirty) +/** + * struct nilfs_root - nilfs root object + * @cno: checkpoint number + * @rb_node: red-black tree node + * @count: refcount of this structure + * @nilfs: nilfs object + * @ifile: inode file + * @root: root inode + * @inodes_count: number of inodes + * @blocks_count: number of blocks (Reserved) + */ +struct nilfs_root { + __u64 cno; + struct rb_node rb_node; + + atomic_t count; + struct the_nilfs *nilfs; + struct inode *ifile; + + atomic_t inodes_count; + atomic_t blocks_count; +}; + +/* Special checkpoint number */ +#define NILFS_CPTREE_CURRENT_CNO 0 + /* Minimum interval of periodical update of superblocks (in seconds) */ #define NILFS_SB_FREQ 10 @@ -222,6 +255,10 @@ int init_nilfs(struct the_nilfs *, struct nilfs_sb_info *, char *); int load_nilfs(struct the_nilfs *, struct nilfs_sb_info *); int nilfs_discard_segments(struct the_nilfs *, __u64 *, size_t); int nilfs_count_free_blocks(struct the_nilfs *, sector_t *); +struct nilfs_root *nilfs_lookup_root(struct the_nilfs *nilfs, __u64 cno); +struct nilfs_root *nilfs_find_or_create_root(struct the_nilfs *nilfs, + __u64 cno); +void nilfs_put_root(struct nilfs_root *root); struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *, int, __u64); int nilfs_checkpoint_is_mounted(struct the_nilfs *, __u64, int); int nilfs_near_disk_full(struct the_nilfs *); @@ -235,6 +272,11 @@ static inline void get_nilfs(struct the_nilfs *nilfs) atomic_inc(&nilfs->ns_count); } +static inline void nilfs_get_root(struct nilfs_root *root) +{ + atomic_inc(&root->count); +} + static inline void nilfs_attach_writer(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) { -- cgit v1.2.3-18-g5258 From 4d8d9293dce503eb0e083e17a02a328d397e7f00 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Wed, 25 Aug 2010 17:45:44 +0900 Subject: nilfs2: set pointer to root object in inodes This puts a pointer to nilfs_root object in the private part of on-memory inode, and makes nilfs_iget function pick up the inode with the same root object. Non-root inodes inherit its nilfs_root object from parent inode. That of the root inode is allocated through nilfs_attach_checkpoint() function. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/inode.c | 27 ++++++++++++++++++++++----- fs/nilfs2/namei.c | 5 +++-- fs/nilfs2/nilfs.h | 7 +++++-- fs/nilfs2/recovery.c | 12 ++++++++---- fs/nilfs2/super.c | 37 ++++++++++++++++++++++++++++++------- 5 files changed, 68 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 82cfdbc43e1..7306fc7c496 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -37,6 +37,7 @@ struct nilfs_iget_args { u64 ino; __u64 cno; + struct nilfs_root *root; int for_gc; }; @@ -284,6 +285,7 @@ struct inode *nilfs_new_inode(struct inode *dir, int mode) struct nilfs_sb_info *sbi = NILFS_SB(sb); struct inode *inode; struct nilfs_inode_info *ii; + struct nilfs_root *root; int err = -ENOMEM; ino_t ino; @@ -294,8 +296,10 @@ struct inode *nilfs_new_inode(struct inode *dir, int mode) mapping_set_gfp_mask(inode->i_mapping, mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); + root = NILFS_I(dir)->i_root; ii = NILFS_I(inode); ii->i_state = 1 << NILFS_I_NEW; + ii->i_root = root; err = nilfs_ifile_create_inode(sbi->s_ifile, &ino, &ii->i_bh); if (unlikely(err)) @@ -484,7 +488,7 @@ static int nilfs_iget_test(struct inode *inode, void *opaque) struct nilfs_iget_args *args = opaque; struct nilfs_inode_info *ii; - if (args->ino != inode->i_ino) + if (args->ino != inode->i_ino || args->root != NILFS_I(inode)->i_root) return 0; ii = NILFS_I(inode); @@ -502,13 +506,21 @@ static int nilfs_iget_set(struct inode *inode, void *opaque) if (args->for_gc) { NILFS_I(inode)->i_state = 1 << NILFS_I_GCINODE; NILFS_I(inode)->i_cno = args->cno; + NILFS_I(inode)->i_root = NULL; + } else { + if (args->root && args->ino == NILFS_ROOT_INO) + nilfs_get_root(args->root); + NILFS_I(inode)->i_root = args->root; } return 0; } -struct inode *nilfs_iget(struct super_block *sb, unsigned long ino) +struct inode *nilfs_iget(struct super_block *sb, struct nilfs_root *root, + unsigned long ino) { - struct nilfs_iget_args args = { .ino = ino, .cno = 0, .for_gc = 0 }; + struct nilfs_iget_args args = { + .ino = ino, .root = root, .cno = 0, .for_gc = 0 + }; struct inode *inode; int err; @@ -530,7 +542,9 @@ struct inode *nilfs_iget(struct super_block *sb, unsigned long ino) struct inode *nilfs_iget_for_gc(struct super_block *sb, unsigned long ino, __u64 cno) { - struct nilfs_iget_args args = { .ino = ino, .cno = cno, .for_gc = 1 }; + struct nilfs_iget_args args = { + .ino = ino, .root = NULL, .cno = cno, .for_gc = 1 + }; struct inode *inode; int err; @@ -682,6 +696,9 @@ static void nilfs_clear_inode(struct inode *inode) nilfs_bmap_clear(ii->i_bmap); nilfs_btnode_cache_clear(&ii->i_btnode_cache); + + if (ii->i_root && inode->i_ino == NILFS_ROOT_INO) + nilfs_put_root(ii->i_root); } void nilfs_evict_inode(struct inode *inode) @@ -690,7 +707,7 @@ void nilfs_evict_inode(struct inode *inode) struct super_block *sb = inode->i_sb; struct nilfs_inode_info *ii = NILFS_I(inode); - if (inode->i_nlink || unlikely(is_bad_inode(inode))) { + if (inode->i_nlink || !ii->i_root || unlikely(is_bad_inode(inode))) { if (inode->i_data.nrpages) truncate_inode_pages(&inode->i_data, 0); end_writeback(inode); diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index ad6ed2cf19b..1110d56a23f 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -70,7 +70,7 @@ nilfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) ino = nilfs_inode_by_name(dir, &dentry->d_name); inode = NULL; if (ino) { - inode = nilfs_iget(dir->i_sb, ino); + inode = nilfs_iget(dir->i_sb, NILFS_I(dir)->i_root, ino); if (IS_ERR(inode)) return ERR_CAST(inode); } @@ -87,7 +87,8 @@ struct dentry *nilfs_get_parent(struct dentry *child) if (!ino) return ERR_PTR(-ENOENT); - inode = nilfs_iget(child->d_inode->i_sb, ino); + inode = nilfs_iget(child->d_inode->i_sb, + NILFS_I(child->d_inode)->i_root, ino); if (IS_ERR(inode)) return ERR_CAST(inode); return d_obtain_alias(inode); diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 797cd437970..21d90c4b4e2 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -59,6 +59,7 @@ struct nilfs_inode_info { #endif struct buffer_head *i_bh; /* i_bh contains a new or dirty disk inode */ + struct nilfs_root *i_root; struct inode vfs_inode; }; @@ -247,7 +248,8 @@ extern int nilfs_get_block(struct inode *, sector_t, struct buffer_head *, int); extern void nilfs_set_inode_flags(struct inode *); extern int nilfs_read_inode_common(struct inode *, struct nilfs_inode *); extern void nilfs_write_inode_common(struct inode *, struct nilfs_inode *, int); -extern struct inode *nilfs_iget(struct super_block *, unsigned long); +struct inode *nilfs_iget(struct super_block *sb, struct nilfs_root *root, + unsigned long ino); extern struct inode *nilfs_iget_for_gc(struct super_block *sb, unsigned long ino, __u64 cno); extern void nilfs_update_inode(struct inode *, struct buffer_head *); @@ -285,7 +287,8 @@ extern struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *, int flip); extern int nilfs_commit_super(struct nilfs_sb_info *, int); extern int nilfs_cleanup_super(struct nilfs_sb_info *); -extern int nilfs_attach_checkpoint(struct nilfs_sb_info *, __u64); +int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, + struct nilfs_root **root); extern void nilfs_detach_checkpoint(struct nilfs_sb_info *); /* gcinode.c */ diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index d0c35ef39f6..a9a5ba8f57d 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -504,6 +504,7 @@ static int nilfs_recovery_copy_block(struct the_nilfs *nilfs, static int nilfs_recover_dsync_blocks(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, + struct nilfs_root *root, struct list_head *head, unsigned long *nr_salvaged_blocks) { @@ -515,7 +516,7 @@ static int nilfs_recover_dsync_blocks(struct the_nilfs *nilfs, int err = 0, err2 = 0; list_for_each_entry_safe(rb, n, head, list) { - inode = nilfs_iget(sbi->s_super, rb->ino); + inode = nilfs_iget(sbi->s_super, root, rb->ino); if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; @@ -578,6 +579,7 @@ static int nilfs_recover_dsync_blocks(struct the_nilfs *nilfs, */ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, + struct nilfs_root *root, struct nilfs_recovery_info *ri) { struct buffer_head *bh_sum = NULL; @@ -649,7 +651,7 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, goto failed; if (flags & NILFS_SS_LOGEND) { err = nilfs_recover_dsync_blocks( - nilfs, sbi, &dsync_blocks, + nilfs, sbi, root, &dsync_blocks, &nsalvaged_blocks); if (unlikely(err)) goto failed; @@ -746,19 +748,20 @@ int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, struct nilfs_recovery_info *ri) { + struct nilfs_root *root; int err; if (ri->ri_lsegs_start == 0 || ri->ri_lsegs_end == 0) return 0; - err = nilfs_attach_checkpoint(sbi, ri->ri_cno); + err = nilfs_attach_checkpoint(sbi, ri->ri_cno, true, &root); if (unlikely(err)) { printk(KERN_ERR "NILFS: error loading the latest checkpoint.\n"); return err; } - err = nilfs_do_roll_forward(nilfs, sbi, ri); + err = nilfs_do_roll_forward(nilfs, sbi, root, ri); if (unlikely(err)) goto failed; @@ -789,6 +792,7 @@ int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, failed: nilfs_detach_checkpoint(sbi); + nilfs_put_root(root); return err; } diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index f3a00a3b2a0..a1c0e38a770 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -391,18 +391,24 @@ static int nilfs_sync_fs(struct super_block *sb, int wait) return err; } -int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno) +int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, + struct nilfs_root **rootp) { struct the_nilfs *nilfs = sbi->s_nilfs; + struct nilfs_root *root; struct nilfs_checkpoint *raw_cp; struct buffer_head *bh_cp; - int err; + int err = -ENOMEM; + + root = nilfs_find_or_create_root( + nilfs, curr_mnt ? NILFS_CPTREE_CURRENT_CNO : cno); + if (!root) + return err; down_write(&nilfs->ns_super_sem); list_add(&sbi->s_list, &nilfs->ns_supers); up_write(&nilfs->ns_super_sem); - err = -ENOMEM; sbi->s_ifile = nilfs_ifile_new(sbi, nilfs->ns_inode_size); if (!sbi->s_ifile) goto delist; @@ -428,6 +434,8 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno) atomic_set(&sbi->s_blocks_count, le64_to_cpu(raw_cp->cp_blocks_count)); nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp); + + *rootp = root; return 0; failed_bh: @@ -440,6 +448,7 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno) down_write(&nilfs->ns_super_sem); list_del_init(&sbi->s_list); up_write(&nilfs->ns_super_sem); + nilfs_put_root(root); return err; } @@ -551,12 +560,20 @@ static struct inode * nilfs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation) { struct inode *inode; + struct nilfs_root *root; if (ino < NILFS_FIRST_INO(sb) && ino != NILFS_ROOT_INO && ino != NILFS_SKETCH_INO) return ERR_PTR(-ESTALE); - inode = nilfs_iget(sb, ino); + root = nilfs_lookup_root(NILFS_SB(sb)->s_nilfs, + NILFS_CPTREE_CURRENT_CNO); + if (!root) + return ERR_PTR(-ESTALE); + + /* new file handle type is required to export snapshots */ + inode = nilfs_iget(sb, root, ino); + nilfs_put_root(root); if (IS_ERR(inode)) return ERR_CAST(inode); if (generation && inode->i_generation != generation) { @@ -815,9 +832,10 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, struct the_nilfs *nilfs) { struct nilfs_sb_info *sbi; + struct nilfs_root *fsroot; struct inode *root; __u64 cno; - int err; + int err, curr_mnt; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -859,6 +877,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, goto failed_sbi; cno = nilfs_last_cno(nilfs); + curr_mnt = true; if (sb->s_flags & MS_RDONLY) { if (nilfs_test_opt(sbi, SNAPSHOT)) { @@ -881,10 +900,11 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, goto failed_sbi; } cno = sbi->s_snapshot_cno; + curr_mnt = false; } } - err = nilfs_attach_checkpoint(sbi, cno); + err = nilfs_attach_checkpoint(sbi, cno, curr_mnt, &fsroot); if (err) { printk(KERN_ERR "NILFS: error loading a checkpoint" " (checkpoint number=%llu).\n", (unsigned long long)cno); @@ -897,7 +917,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, goto failed_checkpoint; } - root = nilfs_iget(sb, NILFS_ROOT_INO); + root = nilfs_iget(sb, fsroot, NILFS_ROOT_INO); if (IS_ERR(root)) { printk(KERN_ERR "NILFS: get root inode failed\n"); err = PTR_ERR(root); @@ -917,6 +937,8 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, goto failed_segctor; } + nilfs_put_root(fsroot); + if (!(sb->s_flags & MS_RDONLY)) { down_write(&nilfs->ns_sem); nilfs_setup_super(sbi); @@ -935,6 +957,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, failed_checkpoint: nilfs_detach_checkpoint(sbi); + nilfs_put_root(fsroot); failed_sbi: put_nilfs(nilfs); -- cgit v1.2.3-18-g5258 From 8e656fd518784b49453f60c5f78b78703bc85cb2 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 27 Aug 2010 00:23:02 +0900 Subject: nilfs2: make snapshots in checkpoint tree exportable The previous export operations cannot handle multiple versions of a filesystem if they belong to the same sb instance. This adds a new type of file handle and extends export operations so that they can get the inode specified by a checkpoint number as well as an inode number and a generation number. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/export.h | 17 +++++++ fs/nilfs2/namei.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++------- fs/nilfs2/nilfs.h | 3 -- fs/nilfs2/super.c | 52 +------------------- 4 files changed, 138 insertions(+), 71 deletions(-) create mode 100644 fs/nilfs2/export.h (limited to 'fs') diff --git a/fs/nilfs2/export.h b/fs/nilfs2/export.h new file mode 100644 index 00000000000..a71cc412b65 --- /dev/null +++ b/fs/nilfs2/export.h @@ -0,0 +1,17 @@ +#ifndef NILFS_EXPORT_H +#define NILFS_EXPORT_H + +#include + +extern const struct export_operations nilfs_export_ops; + +struct nilfs_fid { + u64 cno; + u64 ino; + u32 gen; + + u32 parent_gen; + u64 parent_ino; +} __attribute__ ((packed)); + +#endif diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 1110d56a23f..a65f46560fb 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -40,7 +40,11 @@ #include #include "nilfs.h" +#include "export.h" +#define NILFS_FID_SIZE_NON_CONNECTABLE \ + (offsetof(struct nilfs_fid, parent_gen) / 4) +#define NILFS_FID_SIZE_CONNECTABLE (sizeof(struct nilfs_fid) / 4) static inline int nilfs_add_nondir(struct dentry *dentry, struct inode *inode) { @@ -77,23 +81,6 @@ nilfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) return d_splice_alias(inode, dentry); } -struct dentry *nilfs_get_parent(struct dentry *child) -{ - unsigned long ino; - struct inode *inode; - struct qstr dotdot = {.name = "..", .len = 2}; - - ino = nilfs_inode_by_name(child->d_inode, &dotdot); - if (!ino) - return ERR_PTR(-ENOENT); - - inode = nilfs_iget(child->d_inode->i_sb, - NILFS_I(child->d_inode)->i_root, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); - return d_obtain_alias(inode); -} - /* * By the time this is called, we already have created * the directory cache entry for the new file, but it @@ -469,6 +456,115 @@ out: return err; } +/* + * Export operations + */ +static struct dentry *nilfs_get_parent(struct dentry *child) +{ + unsigned long ino; + struct inode *inode; + struct qstr dotdot = {.name = "..", .len = 2}; + struct nilfs_root *root; + + ino = nilfs_inode_by_name(child->d_inode, &dotdot); + if (!ino) + return ERR_PTR(-ENOENT); + + root = NILFS_I(child->d_inode)->i_root; + + inode = nilfs_iget(child->d_inode->i_sb, root, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + + return d_obtain_alias(inode); +} + +static struct dentry *nilfs_get_dentry(struct super_block *sb, u64 cno, + u64 ino, u32 gen) +{ + struct nilfs_root *root; + struct inode *inode; + + if (ino < NILFS_FIRST_INO(sb) && ino != NILFS_ROOT_INO) + return ERR_PTR(-ESTALE); + + root = nilfs_lookup_root(NILFS_SB(sb)->s_nilfs, cno); + if (!root) + return ERR_PTR(-ESTALE); + + inode = nilfs_iget(sb, root, ino); + nilfs_put_root(root); + + if (IS_ERR(inode)) + return ERR_CAST(inode); + if (gen && inode->i_generation != gen) { + iput(inode); + return ERR_PTR(-ESTALE); + } + return d_obtain_alias(inode); +} + +static struct dentry *nilfs_fh_to_dentry(struct super_block *sb, struct fid *fh, + int fh_len, int fh_type) +{ + struct nilfs_fid *fid = (struct nilfs_fid *)fh; + + if ((fh_len != NILFS_FID_SIZE_NON_CONNECTABLE && + fh_len != NILFS_FID_SIZE_CONNECTABLE) || + (fh_type != FILEID_NILFS_WITH_PARENT && + fh_type != FILEID_NILFS_WITHOUT_PARENT)) + return NULL; + + return nilfs_get_dentry(sb, fid->cno, fid->ino, fid->gen); +} + +static struct dentry *nilfs_fh_to_parent(struct super_block *sb, struct fid *fh, + int fh_len, int fh_type) +{ + struct nilfs_fid *fid = (struct nilfs_fid *)fh; + + if (fh_len != NILFS_FID_SIZE_CONNECTABLE || + fh_type != FILEID_NILFS_WITH_PARENT) + return NULL; + + return nilfs_get_dentry(sb, fid->cno, fid->parent_ino, fid->parent_gen); +} + +static int nilfs_encode_fh(struct dentry *dentry, __u32 *fh, int *lenp, + int connectable) +{ + struct nilfs_fid *fid = (struct nilfs_fid *)fh; + struct inode *inode = dentry->d_inode; + struct nilfs_root *root = NILFS_I(inode)->i_root; + int type; + + if (*lenp < NILFS_FID_SIZE_NON_CONNECTABLE || + (connectable && *lenp < NILFS_FID_SIZE_CONNECTABLE)) + return 255; + + fid->cno = root->cno; + fid->ino = inode->i_ino; + fid->gen = inode->i_generation; + + if (connectable && !S_ISDIR(inode->i_mode)) { + struct inode *parent; + + spin_lock(&dentry->d_lock); + parent = dentry->d_parent->d_inode; + fid->parent_ino = parent->i_ino; + fid->parent_gen = parent->i_generation; + spin_unlock(&dentry->d_lock); + + type = FILEID_NILFS_WITH_PARENT; + *lenp = NILFS_FID_SIZE_CONNECTABLE; + } else { + type = FILEID_NILFS_WITHOUT_PARENT; + *lenp = NILFS_FID_SIZE_NON_CONNECTABLE; + } + + return type; +} + const struct inode_operations nilfs_dir_inode_operations = { .create = nilfs_create, .lookup = nilfs_lookup, @@ -493,3 +589,10 @@ const struct inode_operations nilfs_symlink_inode_operations = { .follow_link = page_follow_link_light, .put_link = page_put_link, }; + +const struct export_operations nilfs_export_ops = { + .encode_fh = nilfs_encode_fh, + .fh_to_dentry = nilfs_fh_to_dentry, + .fh_to_parent = nilfs_fh_to_parent, + .get_parent = nilfs_get_parent, +}; diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 21d90c4b4e2..aa7940f7ecf 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -264,9 +264,6 @@ extern int nilfs_set_file_dirty(struct nilfs_sb_info *, struct inode *, extern int nilfs_mark_inode_dirty(struct inode *); extern void nilfs_dirty_inode(struct inode *); -/* namei.c */ -extern struct dentry *nilfs_get_parent(struct dentry *); - /* super.c */ extern struct inode *nilfs_alloc_inode_common(struct the_nilfs *); extern struct inode *nilfs_alloc_inode(struct super_block *); diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index a1c0e38a770..adbf5826b83 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -48,10 +48,10 @@ #include #include #include -#include #include #include #include "nilfs.h" +#include "export.h" #include "mdt.h" #include "alloc.h" #include "btree.h" @@ -556,56 +556,6 @@ static const struct super_operations nilfs_sops = { .show_options = nilfs_show_options }; -static struct inode * -nilfs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation) -{ - struct inode *inode; - struct nilfs_root *root; - - if (ino < NILFS_FIRST_INO(sb) && ino != NILFS_ROOT_INO && - ino != NILFS_SKETCH_INO) - return ERR_PTR(-ESTALE); - - root = nilfs_lookup_root(NILFS_SB(sb)->s_nilfs, - NILFS_CPTREE_CURRENT_CNO); - if (!root) - return ERR_PTR(-ESTALE); - - /* new file handle type is required to export snapshots */ - inode = nilfs_iget(sb, root, ino); - nilfs_put_root(root); - if (IS_ERR(inode)) - return ERR_CAST(inode); - if (generation && inode->i_generation != generation) { - iput(inode); - return ERR_PTR(-ESTALE); - } - - return inode; -} - -static struct dentry * -nilfs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, - int fh_type) -{ - return generic_fh_to_dentry(sb, fid, fh_len, fh_type, - nilfs_nfs_get_inode); -} - -static struct dentry * -nilfs_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, - int fh_type) -{ - return generic_fh_to_parent(sb, fid, fh_len, fh_type, - nilfs_nfs_get_inode); -} - -static const struct export_operations nilfs_export_ops = { - .fh_to_dentry = nilfs_fh_to_dentry, - .fh_to_parent = nilfs_fh_to_parent, - .get_parent = nilfs_get_parent, -}; - enum { Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_barrier, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery, -- cgit v1.2.3-18-g5258 From e912a5b66837ee89fb025e67b5efeaa11930c2ce Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 14 Aug 2010 13:07:15 +0900 Subject: nilfs2: use root object to get ifile This rewrites functions using ifile so that they get ifile from nilfs_root object, and will remove sbi->s_ifile. Some functions that don't know the root object are extended to receive it from caller. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/inode.c | 48 +++++++++++++++++++++--------------------------- fs/nilfs2/nilfs.h | 1 - fs/nilfs2/recovery.c | 3 +-- fs/nilfs2/sb.h | 3 --- fs/nilfs2/segment.c | 43 ++++++++++++++++++++++++++----------------- fs/nilfs2/segment.h | 7 ++++++- fs/nilfs2/super.c | 42 +++++++++++++----------------------------- 7 files changed, 67 insertions(+), 80 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 7306fc7c496..7e883d5a503 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -301,7 +301,7 @@ struct inode *nilfs_new_inode(struct inode *dir, int mode) ii->i_state = 1 << NILFS_I_NEW; ii->i_root = root; - err = nilfs_ifile_create_inode(sbi->s_ifile, &ino, &ii->i_bh); + err = nilfs_ifile_create_inode(root->ifile, &ino, &ii->i_bh); if (unlikely(err)) goto failed_ifile_create_inode; /* reference count of i_bh inherits from nilfs_mdt_read_block() */ @@ -358,16 +358,6 @@ struct inode *nilfs_new_inode(struct inode *dir, int mode) return ERR_PTR(err); } -void nilfs_free_inode(struct inode *inode) -{ - struct super_block *sb = inode->i_sb; - struct nilfs_sb_info *sbi = NILFS_SB(sb); - - /* XXX: check error code? Is there any thing I can do? */ - (void) nilfs_ifile_delete_inode(sbi->s_ifile, inode->i_ino); - atomic_dec(&sbi->s_inodes_count); -} - void nilfs_set_inode_flags(struct inode *inode) { unsigned int flags = NILFS_I(inode)->i_flags; @@ -431,7 +421,8 @@ int nilfs_read_inode_common(struct inode *inode, return 0; } -static int __nilfs_read_inode(struct super_block *sb, unsigned long ino, +static int __nilfs_read_inode(struct super_block *sb, + struct nilfs_root *root, unsigned long ino, struct inode *inode) { struct nilfs_sb_info *sbi = NILFS_SB(sb); @@ -441,11 +432,11 @@ static int __nilfs_read_inode(struct super_block *sb, unsigned long ino, int err; down_read(&NILFS_MDT(dat)->mi_sem); /* XXX */ - err = nilfs_ifile_get_inode_block(sbi->s_ifile, ino, &bh); + err = nilfs_ifile_get_inode_block(root->ifile, ino, &bh); if (unlikely(err)) goto bad_inode; - raw_inode = nilfs_ifile_map_inode(sbi->s_ifile, ino, bh); + raw_inode = nilfs_ifile_map_inode(root->ifile, ino, bh); err = nilfs_read_inode_common(inode, raw_inode); if (err) @@ -468,14 +459,14 @@ static int __nilfs_read_inode(struct super_block *sb, unsigned long ino, inode, inode->i_mode, huge_decode_dev(le64_to_cpu(raw_inode->i_device_code))); } - nilfs_ifile_unmap_inode(sbi->s_ifile, ino, bh); + nilfs_ifile_unmap_inode(root->ifile, ino, bh); brelse(bh); up_read(&NILFS_MDT(dat)->mi_sem); /* XXX */ nilfs_set_inode_flags(inode); return 0; failed_unmap: - nilfs_ifile_unmap_inode(sbi->s_ifile, ino, bh); + nilfs_ifile_unmap_inode(root->ifile, ino, bh); brelse(bh); bad_inode: @@ -530,7 +521,7 @@ struct inode *nilfs_iget(struct super_block *sb, struct nilfs_root *root, if (!(inode->i_state & I_NEW)) return inode; - err = __nilfs_read_inode(sb, ino, inode); + err = __nilfs_read_inode(sb, root, ino, inode); if (unlikely(err)) { iget_failed(inode); return ERR_PTR(err); @@ -595,21 +586,20 @@ void nilfs_update_inode(struct inode *inode, struct buffer_head *ibh) { ino_t ino = inode->i_ino; struct nilfs_inode_info *ii = NILFS_I(inode); - struct super_block *sb = inode->i_sb; - struct nilfs_sb_info *sbi = NILFS_SB(sb); + struct inode *ifile = ii->i_root->ifile; struct nilfs_inode *raw_inode; - raw_inode = nilfs_ifile_map_inode(sbi->s_ifile, ino, ibh); + raw_inode = nilfs_ifile_map_inode(ifile, ino, ibh); if (test_and_clear_bit(NILFS_I_NEW, &ii->i_state)) - memset(raw_inode, 0, NILFS_MDT(sbi->s_ifile)->mi_entry_size); + memset(raw_inode, 0, NILFS_MDT(ifile)->mi_entry_size); set_bit(NILFS_I_INODE_DIRTY, &ii->i_state); nilfs_write_inode_common(inode, raw_inode, 0); /* XXX: call with has_bmap = 0 is a workaround to avoid deadlock of bmap. This delays update of i_bmap to just before writing */ - nilfs_ifile_unmap_inode(sbi->s_ifile, ino, ibh); + nilfs_ifile_unmap_inode(ifile, ino, ibh); } #define NILFS_MAX_TRUNCATE_BLOCKS 16384 /* 64MB for 4KB block */ @@ -719,12 +709,16 @@ void nilfs_evict_inode(struct inode *inode) if (inode->i_data.nrpages) truncate_inode_pages(&inode->i_data, 0); + /* TODO: some of the following operations may fail. */ nilfs_truncate_bmap(ii, 0); nilfs_mark_inode_dirty(inode); end_writeback(inode); + + nilfs_ifile_delete_inode(ii->i_root->ifile, inode->i_ino); + atomic_dec(&NILFS_SB(sb)->s_inodes_count); + nilfs_clear_inode(inode); - nilfs_free_inode(inode); - /* nilfs_free_inode() marks inode buffer dirty */ + if (IS_SYNC(inode)) nilfs_set_transaction_flag(NILFS_TI_SYNC); nilfs_transaction_commit(sb); @@ -779,8 +773,8 @@ int nilfs_load_inode_block(struct nilfs_sb_info *sbi, struct inode *inode, spin_lock(&sbi->s_inode_lock); if (ii->i_bh == NULL) { spin_unlock(&sbi->s_inode_lock); - err = nilfs_ifile_get_inode_block(sbi->s_ifile, inode->i_ino, - pbh); + err = nilfs_ifile_get_inode_block(ii->i_root->ifile, + inode->i_ino, pbh); if (unlikely(err)) return err; spin_lock(&sbi->s_inode_lock); @@ -860,7 +854,7 @@ int nilfs_mark_inode_dirty(struct inode *inode) } nilfs_update_inode(inode, ibh); nilfs_mdt_mark_buffer_dirty(ibh); - nilfs_mdt_mark_dirty(sbi->s_ifile); + nilfs_mdt_mark_dirty(NILFS_I(inode)->i_root->ifile); brelse(ibh); return 0; } diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index aa7940f7ecf..3870c109aba 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -286,7 +286,6 @@ extern int nilfs_commit_super(struct nilfs_sb_info *, int); extern int nilfs_cleanup_super(struct nilfs_sb_info *); int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, struct nilfs_root **root); -extern void nilfs_detach_checkpoint(struct nilfs_sb_info *); /* gcinode.c */ int nilfs_gccache_submit_read_data(struct inode *, sector_t, sector_t, __u64, diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index a9a5ba8f57d..dcb5a9812c6 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -773,7 +773,7 @@ int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, goto failed; } - err = nilfs_attach_segment_constructor(sbi); + err = nilfs_attach_segment_constructor(sbi, root); if (unlikely(err)) goto failed; @@ -791,7 +791,6 @@ int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, } failed: - nilfs_detach_checkpoint(sbi); nilfs_put_root(root); return err; } diff --git a/fs/nilfs2/sb.h b/fs/nilfs2/sb.h index 0776ccc2504..50c418e6438 100644 --- a/fs/nilfs2/sb.h +++ b/fs/nilfs2/sb.h @@ -68,9 +68,6 @@ struct nilfs_sb_info { spinlock_t s_inode_lock; /* Lock for the nilfs inode. It covers s_dirty_files list */ - /* Metadata files */ - struct inode *s_ifile; /* index file inode */ - /* Inode allocator */ spinlock_t s_next_gen_lock; u32 s_next_generation; diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 9cf71389f36..2a6b74e6699 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -763,12 +763,12 @@ static void nilfs_dispose_list(struct nilfs_sb_info *sbi, } } -static int nilfs_test_metadata_dirty(struct nilfs_sb_info *sbi) +static int nilfs_test_metadata_dirty(struct the_nilfs *nilfs, + struct nilfs_root *root) { - struct the_nilfs *nilfs = sbi->s_nilfs; int ret = 0; - if (nilfs_mdt_fetch_dirty(sbi->s_ifile)) + if (nilfs_mdt_fetch_dirty(root->ifile)) ret++; if (nilfs_mdt_fetch_dirty(nilfs->ns_cpfile)) ret++; @@ -793,7 +793,7 @@ static int nilfs_segctor_confirm(struct nilfs_sc_info *sci) struct nilfs_sb_info *sbi = sci->sc_sbi; int ret = 0; - if (nilfs_test_metadata_dirty(sbi)) + if (nilfs_test_metadata_dirty(sbi->s_nilfs, sci->sc_root)) set_bit(NILFS_SC_DIRTY, &sci->sc_flags); spin_lock(&sbi->s_inode_lock); @@ -809,7 +809,7 @@ static void nilfs_segctor_clear_metadata_dirty(struct nilfs_sc_info *sci) struct nilfs_sb_info *sbi = sci->sc_sbi; struct the_nilfs *nilfs = sbi->s_nilfs; - nilfs_mdt_clear_dirty(sbi->s_ifile); + nilfs_mdt_clear_dirty(sci->sc_root->ifile); nilfs_mdt_clear_dirty(nilfs->ns_cpfile); nilfs_mdt_clear_dirty(nilfs->ns_sufile); nilfs_mdt_clear_dirty(nilfs_dat_inode(nilfs)); @@ -869,7 +869,8 @@ static int nilfs_segctor_fill_in_checkpoint(struct nilfs_sc_info *sci) else nilfs_checkpoint_set_minor(raw_cp); - nilfs_write_inode_common(sbi->s_ifile, &raw_cp->cp_ifile_inode, 1); + nilfs_write_inode_common(sci->sc_root->ifile, + &raw_cp->cp_ifile_inode, 1); nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, nilfs->ns_cno, bh_cp); return 0; @@ -894,13 +895,12 @@ static void nilfs_fill_in_file_bmap(struct inode *ifile, } } -static void nilfs_segctor_fill_in_file_bmap(struct nilfs_sc_info *sci, - struct inode *ifile) +static void nilfs_segctor_fill_in_file_bmap(struct nilfs_sc_info *sci) { struct nilfs_inode_info *ii; list_for_each_entry(ii, &sci->sc_dirty_files, i_dirty) { - nilfs_fill_in_file_bmap(ifile, ii); + nilfs_fill_in_file_bmap(sci->sc_root->ifile, ii); set_bit(NILFS_I_COLLECTED, &ii->i_state); } } @@ -1143,7 +1143,7 @@ static int nilfs_segctor_collect_blocks(struct nilfs_sc_info *sci, int mode) sci->sc_stage.flags |= NILFS_CF_IFILE_STARTED; /* Fall through */ case NILFS_ST_IFILE: - err = nilfs_segctor_scan_file(sci, sbi->s_ifile, + err = nilfs_segctor_scan_file(sci, sci->sc_root->ifile, &nilfs_sc_file_ops); if (unlikely(err)) break; @@ -1984,6 +1984,7 @@ static int nilfs_segctor_check_in_files(struct nilfs_sc_info *sci, struct nilfs_sb_info *sbi) { struct nilfs_inode_info *ii, *n; + struct inode *ifile = sci->sc_root->ifile; spin_lock(&sbi->s_inode_lock); retry: @@ -1994,14 +1995,14 @@ static int nilfs_segctor_check_in_files(struct nilfs_sc_info *sci, spin_unlock(&sbi->s_inode_lock); err = nilfs_ifile_get_inode_block( - sbi->s_ifile, ii->vfs_inode.i_ino, &ibh); + ifile, ii->vfs_inode.i_ino, &ibh); if (unlikely(err)) { nilfs_warning(sbi->s_super, __func__, "failed to get inode block.\n"); return err; } nilfs_mdt_mark_buffer_dirty(ibh); - nilfs_mdt_mark_dirty(sbi->s_ifile); + nilfs_mdt_mark_dirty(ifile); spin_lock(&sbi->s_inode_lock); if (likely(!ii->i_bh)) ii->i_bh = ibh; @@ -2058,7 +2059,7 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) if (unlikely(err)) goto out; - if (nilfs_test_metadata_dirty(sbi)) + if (nilfs_test_metadata_dirty(nilfs, sci->sc_root)) set_bit(NILFS_SC_DIRTY, &sci->sc_flags); if (nilfs_segctor_clean(sci)) @@ -2090,7 +2091,7 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) goto failed; if (sci->sc_stage.flags & NILFS_CF_IFILE_STARTED) - nilfs_segctor_fill_in_file_bmap(sci, sbi->s_ifile); + nilfs_segctor_fill_in_file_bmap(sci); if (mode == SC_LSEG_SR && sci->sc_stage.scnt >= NILFS_ST_CPFILE) { @@ -2684,7 +2685,8 @@ static void nilfs_segctor_kill_thread(struct nilfs_sc_info *sci) /* * Setup & clean-up functions */ -static struct nilfs_sc_info *nilfs_segctor_new(struct nilfs_sb_info *sbi) +static struct nilfs_sc_info *nilfs_segctor_new(struct nilfs_sb_info *sbi, + struct nilfs_root *root) { struct nilfs_sc_info *sci; @@ -2695,6 +2697,9 @@ static struct nilfs_sc_info *nilfs_segctor_new(struct nilfs_sb_info *sbi) sci->sc_sbi = sbi; sci->sc_super = sbi->s_super; + nilfs_get_root(root); + sci->sc_root = root; + init_waitqueue_head(&sci->sc_wait_request); init_waitqueue_head(&sci->sc_wait_daemon); init_waitqueue_head(&sci->sc_wait_task); @@ -2769,6 +2774,8 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci) WARN_ON(!list_empty(&sci->sc_segbufs)); WARN_ON(!list_empty(&sci->sc_write_logs)); + nilfs_put_root(sci->sc_root); + down_write(&sbi->s_nilfs->ns_segctor_sem); del_timer_sync(&sci->sc_timer); @@ -2778,6 +2785,7 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci) /** * nilfs_attach_segment_constructor - attach a segment constructor * @sbi: nilfs_sb_info + * @root: root object of the current filesystem tree * * nilfs_attach_segment_constructor() allocates a struct nilfs_sc_info, * initializes it, and starts the segment constructor. @@ -2787,7 +2795,8 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci) * * %-ENOMEM - Insufficient memory available. */ -int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi) +int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi, + struct nilfs_root *root) { struct the_nilfs *nilfs = sbi->s_nilfs; int err; @@ -2801,7 +2810,7 @@ int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi) nilfs_detach_segment_constructor(sbi); } - sbi->s_sc_info = nilfs_segctor_new(sbi); + sbi->s_sc_info = nilfs_segctor_new(sbi, root); if (!sbi->s_sc_info) return -ENOMEM; diff --git a/fs/nilfs2/segment.h b/fs/nilfs2/segment.h index 675d932148a..cd8056e7cbe 100644 --- a/fs/nilfs2/segment.h +++ b/fs/nilfs2/segment.h @@ -29,6 +29,8 @@ #include #include "sb.h" +struct nilfs_root; + /** * struct nilfs_recovery_info - Recovery information * @ri_need_recovery: Recovery status @@ -87,6 +89,7 @@ struct nilfs_segsum_pointer { * struct nilfs_sc_info - Segment constructor information * @sc_super: Back pointer to super_block struct * @sc_sbi: Back pointer to nilfs_sb_info struct + * @sc_root: root object of the current filesystem tree * @sc_nblk_inc: Block count of current generation * @sc_dirty_files: List of files to be written * @sc_gc_inodes: List of GC inodes having blocks to be written @@ -129,6 +132,7 @@ struct nilfs_segsum_pointer { struct nilfs_sc_info { struct super_block *sc_super; struct nilfs_sb_info *sc_sbi; + struct nilfs_root *sc_root; unsigned long sc_nblk_inc; @@ -231,7 +235,8 @@ extern void nilfs_flush_segment(struct super_block *, ino_t); extern int nilfs_clean_segments(struct super_block *, struct nilfs_argv *, void **); -extern int nilfs_attach_segment_constructor(struct nilfs_sb_info *); +int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi, + struct nilfs_root *root); extern void nilfs_detach_segment_constructor(struct nilfs_sb_info *); /* recovery.c */ diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index adbf5826b83..87c57810ae8 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -358,9 +358,9 @@ static void nilfs_put_super(struct super_block *sb) down_write(&nilfs->ns_super_sem); if (nilfs->ns_current == sbi) nilfs->ns_current = NULL; + list_del_init(&sbi->s_list); up_write(&nilfs->ns_super_sem); - nilfs_detach_checkpoint(sbi); put_nilfs(sbi->s_nilfs); sbi->s_super = NULL; sb->s_fs_info = NULL; @@ -405,13 +405,12 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, if (!root) return err; - down_write(&nilfs->ns_super_sem); - list_add(&sbi->s_list, &nilfs->ns_supers); - up_write(&nilfs->ns_super_sem); + if (root->ifile) + goto reuse; /* already attached checkpoint */ - sbi->s_ifile = nilfs_ifile_new(sbi, nilfs->ns_inode_size); - if (!sbi->s_ifile) - goto delist; + root->ifile = nilfs_ifile_new(sbi, nilfs->ns_inode_size); + if (!root->ifile) + goto failed; down_read(&nilfs->ns_segctor_sem); err = nilfs_cpfile_get_checkpoint(nilfs->ns_cpfile, cno, 0, &raw_cp, @@ -427,7 +426,7 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, } goto failed; } - err = nilfs_read_inode_common(sbi->s_ifile, &raw_cp->cp_ifile_inode); + err = nilfs_read_inode_common(root->ifile, &raw_cp->cp_ifile_inode); if (unlikely(err)) goto failed_bh; atomic_set(&sbi->s_inodes_count, le64_to_cpu(raw_cp->cp_inodes_count)); @@ -435,35 +434,18 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp); + reuse: *rootp = root; return 0; failed_bh: nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp); failed: - nilfs_mdt_destroy(sbi->s_ifile); - sbi->s_ifile = NULL; - - delist: - down_write(&nilfs->ns_super_sem); - list_del_init(&sbi->s_list); - up_write(&nilfs->ns_super_sem); nilfs_put_root(root); return err; } -void nilfs_detach_checkpoint(struct nilfs_sb_info *sbi) -{ - struct the_nilfs *nilfs = sbi->s_nilfs; - - nilfs_mdt_destroy(sbi->s_ifile); - sbi->s_ifile = NULL; - down_write(&nilfs->ns_super_sem); - list_del_init(&sbi->s_list); - up_write(&nilfs->ns_super_sem); -} - static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; @@ -862,7 +844,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, } if (!(sb->s_flags & MS_RDONLY)) { - err = nilfs_attach_segment_constructor(sbi); + err = nilfs_attach_segment_constructor(sbi, fsroot); if (err) goto failed_checkpoint; } @@ -896,6 +878,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, } down_write(&nilfs->ns_super_sem); + list_add(&sbi->s_list, &nilfs->ns_supers); if (!nilfs_test_opt(sbi, SNAPSHOT)) nilfs->ns_current = sbi; up_write(&nilfs->ns_super_sem); @@ -906,7 +889,6 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, nilfs_detach_segment_constructor(sbi); failed_checkpoint: - nilfs_detach_checkpoint(sbi); nilfs_put_root(fsroot); failed_sbi: @@ -966,6 +948,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) up_write(&nilfs->ns_sem); } else { __u64 features; + struct nilfs_root *root; /* * Mounting a RDONLY partition read-write, so reread and @@ -987,7 +970,8 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) sb->s_flags &= ~MS_RDONLY; - err = nilfs_attach_segment_constructor(sbi); + root = NILFS_I(sb->s_root->d_inode)->i_root; + err = nilfs_attach_segment_constructor(sbi, root); if (err) goto restore_opts; -- cgit v1.2.3-18-g5258 From b7c0634204993d7c6678c852e4bd118426599111 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 14 Aug 2010 14:48:32 +0900 Subject: nilfs2: move inode count and block count into root object This moves sbi->s_inodes_count and sbi->s_blocks_count into nilfs_root object. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/inode.c | 4 ++-- fs/nilfs2/sb.h | 2 -- fs/nilfs2/segment.c | 4 ++-- fs/nilfs2/super.c | 11 ++++++----- 4 files changed, 10 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 7e883d5a503..ca09e4362d6 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -306,7 +306,7 @@ struct inode *nilfs_new_inode(struct inode *dir, int mode) goto failed_ifile_create_inode; /* reference count of i_bh inherits from nilfs_mdt_read_block() */ - atomic_inc(&sbi->s_inodes_count); + atomic_inc(&root->inodes_count); inode_init_owner(inode, dir, mode); inode->i_ino = ino; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; @@ -715,7 +715,7 @@ void nilfs_evict_inode(struct inode *inode) end_writeback(inode); nilfs_ifile_delete_inode(ii->i_root->ifile, inode->i_ino); - atomic_dec(&NILFS_SB(sb)->s_inodes_count); + atomic_dec(&ii->i_root->inodes_count); nilfs_clear_inode(inode); diff --git a/fs/nilfs2/sb.h b/fs/nilfs2/sb.h index 50c418e6438..3cc3675c3ab 100644 --- a/fs/nilfs2/sb.h +++ b/fs/nilfs2/sb.h @@ -44,8 +44,6 @@ struct nilfs_sc_info; struct nilfs_sb_info { /* Snapshot status */ __u64 s_snapshot_cno; /* Checkpoint number */ - atomic_t s_inodes_count; - atomic_t s_blocks_count; /* Reserved (might be deleted) */ /* Mount options */ unsigned long s_mount_opt; diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 2a6b74e6699..b75306d642c 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -856,9 +856,9 @@ static int nilfs_segctor_fill_in_checkpoint(struct nilfs_sc_info *sci) raw_cp->cp_snapshot_list.ssl_next = 0; raw_cp->cp_snapshot_list.ssl_prev = 0; raw_cp->cp_inodes_count = - cpu_to_le64(atomic_read(&sbi->s_inodes_count)); + cpu_to_le64(atomic_read(&sci->sc_root->inodes_count)); raw_cp->cp_blocks_count = - cpu_to_le64(atomic_read(&sbi->s_blocks_count)); + cpu_to_le64(atomic_read(&sci->sc_root->blocks_count)); raw_cp->cp_nblk_inc = cpu_to_le64(sci->sc_nblk_inc + sci->sc_nblk_this_inc); raw_cp->cp_create = cpu_to_le64(sci->sc_seg_ctime); diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 87c57810ae8..acfa74e45f0 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -429,8 +429,9 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, err = nilfs_read_inode_common(root->ifile, &raw_cp->cp_ifile_inode); if (unlikely(err)) goto failed_bh; - atomic_set(&sbi->s_inodes_count, le64_to_cpu(raw_cp->cp_inodes_count)); - atomic_set(&sbi->s_blocks_count, le64_to_cpu(raw_cp->cp_blocks_count)); + + atomic_set(&root->inodes_count, le64_to_cpu(raw_cp->cp_inodes_count)); + atomic_set(&root->blocks_count, le64_to_cpu(raw_cp->cp_blocks_count)); nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp); @@ -449,8 +450,8 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; - struct nilfs_sb_info *sbi = NILFS_SB(sb); - struct the_nilfs *nilfs = sbi->s_nilfs; + struct nilfs_root *root = NILFS_I(dentry->d_inode)->i_root; + struct the_nilfs *nilfs = root->nilfs; u64 id = huge_encode_dev(sb->s_bdev->bd_dev); unsigned long long blocks; unsigned long overhead; @@ -486,7 +487,7 @@ static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bfree = nfreeblocks; buf->f_bavail = (buf->f_bfree >= nrsvblocks) ? (buf->f_bfree - nrsvblocks) : 0; - buf->f_files = atomic_read(&sbi->s_inodes_count); + buf->f_files = atomic_read(&root->inodes_count); buf->f_ffree = 0; /* nilfs_count_free_inodes(sb); */ buf->f_namelen = NILFS_NAME_LEN; buf->f_fsid.val[0] = (u32)id; -- cgit v1.2.3-18-g5258 From fd52202930b7e8db48bee5a6fc6b1f438e822a23 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 14 Aug 2010 21:44:51 +0900 Subject: nilfs2: use checkpoint tree for mount check of snapshots This rewrites nilfs_checkpoint_is_mounted() function so that it decides whether a checkpoint is mounted by whether the corresponding root object is found in checkpoint tree. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/the_nilfs.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index f1d599273d9..89c78562d0e 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -954,26 +954,20 @@ struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *nilfs, int nilfs_checkpoint_is_mounted(struct the_nilfs *nilfs, __u64 cno, int snapshot_mount) { - struct nilfs_sb_info *sbi; - int ret = 0; + struct nilfs_root *root; + int ret; - down_read(&nilfs->ns_super_sem); - if (cno == 0 || cno > nilfs->ns_cno) - goto out_unlock; + if (cno < 0 || cno > nilfs->ns_cno) + return false; - list_for_each_entry(sbi, &nilfs->ns_supers, s_list) { - if (sbi->s_snapshot_cno == cno && - (!snapshot_mount || nilfs_test_opt(sbi, SNAPSHOT))) { - /* exclude read-only mounts */ - ret++; - break; - } - } - /* for protecting recent checkpoints */ if (cno >= nilfs_last_cno(nilfs)) - ret++; + return true; /* protect recent checkpoints */ - out_unlock: - up_read(&nilfs->ns_super_sem); + ret = false; + root = nilfs_lookup_root(nilfs, cno); + if (root) { + ret = true; + nilfs_put_root(root); + } return ret; } -- cgit v1.2.3-18-g5258 From dc3d3b810a644dfa329efaa230cd514226f8981d Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 15 Aug 2010 23:33:57 +0900 Subject: nilfs2: deny write access to inodes in snapshots Snapshots of nilfs are read-only. After super block instances (sb) will be unified, nilfs will need to check write access by a way other than implicit test with IS_RDONLY(inode). This is because IS_RDONLY() refers to MS_RDONLY bit of inode->i_sb->s_flags and it will become inaccurate after the unification of sb. To prepare for the issue, this uses i_op->permission to deny write access to inodes in snapshots. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/inode.c | 11 +++++++++++ fs/nilfs2/namei.c | 1 + fs/nilfs2/nilfs.h | 4 +--- 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index ca09e4362d6..3efef0ecfa2 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -764,6 +764,17 @@ out_err: return err; } +int nilfs_permission(struct inode *inode, int mask) +{ + struct nilfs_root *root = NILFS_I(inode)->i_root; + + if ((mask & MAY_WRITE) && root && + root->cno != NILFS_CPTREE_CURRENT_CNO) + return -EROFS; /* snapshot is not writable */ + + return generic_permission(inode, mask, NULL); +} + int nilfs_load_inode_block(struct nilfs_sb_info *sbi, struct inode *inode, struct buffer_head **pbh) { diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index a65f46560fb..185d1607cb0 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -588,6 +588,7 @@ const struct inode_operations nilfs_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = page_follow_link_light, .put_link = page_put_link, + .permission = nilfs_permission, }; const struct export_operations nilfs_export_ops = { diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 3870c109aba..cf5507a2a17 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -201,12 +201,9 @@ static inline struct inode *nilfs_dat_inode(const struct the_nilfs *nilfs) */ #ifdef CONFIG_NILFS_POSIX_ACL #error "NILFS: not yet supported POSIX ACL" -extern int nilfs_permission(struct inode *, int, struct nameidata *); extern int nilfs_acl_chmod(struct inode *); extern int nilfs_init_acl(struct inode *, struct inode *); #else -#define nilfs_permission NULL - static inline int nilfs_acl_chmod(struct inode *inode) { return 0; @@ -256,6 +253,7 @@ extern void nilfs_update_inode(struct inode *, struct buffer_head *); extern void nilfs_truncate(struct inode *); extern void nilfs_evict_inode(struct inode *); extern int nilfs_setattr(struct dentry *, struct iattr *); +int nilfs_permission(struct inode *inode, int mask); extern int nilfs_load_inode_block(struct nilfs_sb_info *, struct inode *, struct buffer_head **); extern int nilfs_inode_dirty(struct inode *); -- cgit v1.2.3-18-g5258 From 367ea33486a68f935a01311a3be9b7e97d2e5ead Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 26 Aug 2010 01:52:51 +0900 Subject: nilfs2: split out nilfs_get_root_dentry This splits the code to allocate root dentry into a separate routine for convenience in successive changes. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/super.c | 54 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index acfa74e45f0..1e12930f8b9 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -750,6 +750,39 @@ int nilfs_check_feature_compatibility(struct super_block *sb, return 0; } +static int nilfs_get_root_dentry(struct super_block *sb, + struct nilfs_root *root, + struct dentry **root_dentry) +{ + struct inode *inode; + struct dentry *dentry; + int ret = 0; + + inode = nilfs_iget(sb, root, NILFS_ROOT_INO); + if (IS_ERR(inode)) { + printk(KERN_ERR "NILFS: get root inode failed\n"); + ret = PTR_ERR(inode); + goto out; + } + if (!S_ISDIR(inode->i_mode) || !inode->i_blocks || !inode->i_size) { + iput(inode); + printk(KERN_ERR "NILFS: corrupt root inode.\n"); + ret = -EINVAL; + goto out; + } + + dentry = d_alloc_root(inode); + if (!dentry) { + iput(inode); + printk(KERN_ERR "NILFS: get root dentry failed\n"); + ret = -ENOMEM; + goto out; + } + *root_dentry = dentry; + out: + return ret; +} + /** * nilfs_fill_super() - initialize a super block instance * @sb: super_block @@ -766,7 +799,6 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, { struct nilfs_sb_info *sbi; struct nilfs_root *fsroot; - struct inode *root; __u64 cno; int err, curr_mnt; @@ -850,25 +882,9 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, goto failed_checkpoint; } - root = nilfs_iget(sb, fsroot, NILFS_ROOT_INO); - if (IS_ERR(root)) { - printk(KERN_ERR "NILFS: get root inode failed\n"); - err = PTR_ERR(root); - goto failed_segctor; - } - if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { - iput(root); - printk(KERN_ERR "NILFS: corrupt root inode.\n"); - err = -EINVAL; - goto failed_segctor; - } - sb->s_root = d_alloc_root(root); - if (!sb->s_root) { - iput(root); - printk(KERN_ERR "NILFS: get root dentry failed\n"); - err = -ENOMEM; + err = nilfs_get_root_dentry(sb, fsroot, &sb->s_root); + if (err) goto failed_segctor; - } nilfs_put_root(fsroot); -- cgit v1.2.3-18-g5258 From ab4d8f7ebf33beff97e766d18db47f1ea9635769 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 26 Aug 2010 02:15:41 +0900 Subject: nilfs2: split out nilfs_attach_snapshot This splits the code to attach snapshots into a separate routine for convenience sake. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/super.c | 73 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 1e12930f8b9..ebeb746c484 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -783,6 +783,40 @@ static int nilfs_get_root_dentry(struct super_block *sb, return ret; } +static int nilfs_attach_snapshot(struct super_block *s, __u64 cno, + struct dentry **root_dentry) +{ + struct the_nilfs *nilfs = NILFS_SB(s)->s_nilfs; + struct nilfs_root *root; + int ret; + + down_read(&nilfs->ns_segctor_sem); + ret = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile, cno); + up_read(&nilfs->ns_segctor_sem); + if (ret < 0) { + ret = (ret == -ENOENT) ? -EINVAL : ret; + goto out; + } else if (!ret) { + printk(KERN_ERR "NILFS: The specified checkpoint is " + "not a snapshot (checkpoint number=%llu).\n", + (unsigned long long)cno); + ret = -EINVAL; + goto out; + } + + ret = nilfs_attach_checkpoint(NILFS_SB(s), cno, false, &root); + if (ret) { + printk(KERN_ERR "NILFS: error loading snapshot " + "(checkpoint number=%llu).\n", + (unsigned long long)cno); + goto out; + } + ret = nilfs_get_root_dentry(s, root, root_dentry); + nilfs_put_root(root); + out: + return ret; +} + /** * nilfs_fill_super() - initialize a super block instance * @sb: super_block @@ -800,7 +834,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, struct nilfs_sb_info *sbi; struct nilfs_root *fsroot; __u64 cno; - int err, curr_mnt; + int err; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -841,35 +875,17 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, if (err) goto failed_sbi; - cno = nilfs_last_cno(nilfs); - curr_mnt = true; - - if (sb->s_flags & MS_RDONLY) { - if (nilfs_test_opt(sbi, SNAPSHOT)) { - down_read(&nilfs->ns_segctor_sem); - err = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile, - sbi->s_snapshot_cno); - up_read(&nilfs->ns_segctor_sem); - if (err < 0) { - if (err == -ENOENT) - err = -EINVAL; - goto failed_sbi; - } - if (!err) { - printk(KERN_ERR - "NILFS: The specified checkpoint is " - "not a snapshot " - "(checkpoint number=%llu).\n", - (unsigned long long)sbi->s_snapshot_cno); - err = -EINVAL; - goto failed_sbi; - } - cno = sbi->s_snapshot_cno; - curr_mnt = false; - } + if (nilfs_test_opt(sbi, SNAPSHOT)) { + err = nilfs_attach_snapshot(sb, sbi->s_snapshot_cno, + &sb->s_root); + if (err) + goto failed_sbi; + + goto add_to_supers; } - err = nilfs_attach_checkpoint(sbi, cno, curr_mnt, &fsroot); + cno = nilfs_last_cno(nilfs); + err = nilfs_attach_checkpoint(sbi, cno, true, &fsroot); if (err) { printk(KERN_ERR "NILFS: error loading a checkpoint" " (checkpoint number=%llu).\n", (unsigned long long)cno); @@ -894,6 +910,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, up_write(&nilfs->ns_sem); } + add_to_supers: down_write(&nilfs->ns_super_sem); list_add(&sbi->s_list, &nilfs->ns_supers); if (!nilfs_test_opt(sbi, SNAPSHOT)) -- cgit v1.2.3-18-g5258 From f11459ad7dab9e9eb5a05b8bd3bec338ea8f485d Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 16 Aug 2010 01:54:52 +0900 Subject: nilfs2: do not allocate multiple super block instances for a device This stops allocating multiple super block instances for a device. All snapshots and a current mode mount (i.e. latest tree) will be controlled with nilfs_root objects that are kept within an sb instance. nilfs_get_sb() is rewritten so that it always has a root object for the latest tree and snapshots make additional root objects. The root dentry of the latest tree is binded to sb->s_root even if it isn't attached on a directory. Root dentries of snapshots or the latest tree are binded to mnt->mnt_root on which they are mounted. With this patch, nilfs_find_sbinfo() function, nilfs->ns_supers list, and nilfs->ns_current back pointer, are deleted. In addition, init_nilfs() and load_nilfs() are simplified since they will be called once for a device, not repeatedly called for mount points. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/sb.h | 5 -- fs/nilfs2/super.c | 216 +++++++++++++++++++++++--------------------------- fs/nilfs2/the_nilfs.c | 89 +-------------------- fs/nilfs2/the_nilfs.h | 16 ---- 4 files changed, 101 insertions(+), 225 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/sb.h b/fs/nilfs2/sb.h index 3cc3675c3ab..35a07157b98 100644 --- a/fs/nilfs2/sb.h +++ b/fs/nilfs2/sb.h @@ -42,9 +42,6 @@ struct nilfs_sc_info; * NILFS super-block data in memory */ struct nilfs_sb_info { - /* Snapshot status */ - __u64 s_snapshot_cno; /* Checkpoint number */ - /* Mount options */ unsigned long s_mount_opt; uid_t s_resuid; @@ -57,8 +54,6 @@ struct nilfs_sb_info { /* Fundamental members */ struct super_block *s_super; /* reverse pointer to super_block */ struct the_nilfs *s_nilfs; - struct list_head s_list; /* list head for nilfs->ns_supers */ - atomic_t s_count; /* reference count */ /* Segment constructor */ struct list_head s_dirty_files; /* dirty files list */ diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index ebeb746c484..2e58e7c629b 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -355,16 +355,11 @@ static void nilfs_put_super(struct super_block *sb) nilfs_cleanup_super(sbi); up_write(&nilfs->ns_sem); } - down_write(&nilfs->ns_super_sem); - if (nilfs->ns_current == sbi) - nilfs->ns_current = NULL; - list_del_init(&sbi->s_list); - up_write(&nilfs->ns_super_sem); put_nilfs(sbi->s_nilfs); sbi->s_super = NULL; sb->s_fs_info = NULL; - nilfs_put_sbinfo(sbi); + kfree(sbi); } static int nilfs_sync_fs(struct super_block *sb, int wait) @@ -500,12 +495,12 @@ static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs) { struct super_block *sb = vfs->mnt_sb; struct nilfs_sb_info *sbi = NILFS_SB(sb); + struct nilfs_root *root = NILFS_I(vfs->mnt_root->d_inode)->i_root; if (!nilfs_test_opt(sbi, BARRIER)) seq_puts(seq, ",nobarrier"); - if (nilfs_test_opt(sbi, SNAPSHOT)) - seq_printf(seq, ",cp=%llu", - (unsigned long long int)sbi->s_snapshot_cno); + if (root->cno != NILFS_CPTREE_CURRENT_CNO) + seq_printf(seq, ",cp=%llu", (unsigned long long)root->cno); if (nilfs_test_opt(sbi, ERRORS_PANIC)) seq_puts(seq, ",errors=panic"); if (nilfs_test_opt(sbi, ERRORS_CONT)) @@ -605,27 +600,11 @@ static int parse_options(char *options, struct super_block *sb, int is_remount) if (match_int(&args[0], &option) || option <= 0) return 0; if (is_remount) { - if (!nilfs_test_opt(sbi, SNAPSHOT)) { - printk(KERN_ERR - "NILFS: cannot change regular " - "mount to snapshot.\n"); - return 0; - } else if (option != sbi->s_snapshot_cno) { - printk(KERN_ERR - "NILFS: cannot remount to a " - "different snapshot.\n"); - return 0; - } - break; - } - if (!(sb->s_flags & MS_RDONLY)) { - printk(KERN_ERR "NILFS: cannot mount snapshot " - "read/write. A read-only option is " - "required.\n"); + printk(KERN_ERR + "NILFS: \"%s\" option is invalid " + "for remount.\n", p); return 0; } - sbi->s_snapshot_cno = option; - nilfs_set_opt(sbi, SNAPSHOT); break; case Opt_norecovery: nilfs_set_opt(sbi, NORECOVERY); @@ -771,16 +750,32 @@ static int nilfs_get_root_dentry(struct super_block *sb, goto out; } - dentry = d_alloc_root(inode); - if (!dentry) { - iput(inode); - printk(KERN_ERR "NILFS: get root dentry failed\n"); - ret = -ENOMEM; - goto out; + if (root->cno == NILFS_CPTREE_CURRENT_CNO) { + dentry = d_find_alias(inode); + if (!dentry) { + dentry = d_alloc_root(inode); + if (!dentry) { + iput(inode); + ret = -ENOMEM; + goto failed_dentry; + } + } else { + iput(inode); + } + } else { + dentry = d_obtain_alias(inode); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto failed_dentry; + } } *root_dentry = dentry; out: return ret; + + failed_dentry: + printk(KERN_ERR "NILFS: get root dentry failed\n"); + goto out; } static int nilfs_attach_snapshot(struct super_block *s, __u64 cno, @@ -817,6 +812,25 @@ static int nilfs_attach_snapshot(struct super_block *s, __u64 cno, return ret; } +static int nilfs_tree_was_touched(struct dentry *root_dentry) +{ + return atomic_read(&root_dentry->d_count) > 1; +} + +/** + * nilfs_try_to_shrink_tree() - try to shrink dentries of a checkpoint + * @root_dentry: root dentry of the tree to be shrunk + * + * This function returns true if the tree was in-use. + */ +static int nilfs_try_to_shrink_tree(struct dentry *root_dentry) +{ + if (have_submounts(root_dentry)) + return true; + shrink_dcache_parent(root_dentry); + return nilfs_tree_was_touched(root_dentry); +} + /** * nilfs_fill_super() - initialize a super block instance * @sb: super_block @@ -845,7 +859,6 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, get_nilfs(nilfs); sbi->s_nilfs = nilfs; sbi->s_super = sb; - atomic_set(&sbi->s_count, 1); err = init_nilfs(nilfs, sbi, (char *)data); if (err) @@ -853,7 +866,6 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, spin_lock_init(&sbi->s_inode_lock); INIT_LIST_HEAD(&sbi->s_dirty_files); - INIT_LIST_HEAD(&sbi->s_list); /* * Following initialization is overlapped because @@ -875,20 +887,11 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, if (err) goto failed_sbi; - if (nilfs_test_opt(sbi, SNAPSHOT)) { - err = nilfs_attach_snapshot(sb, sbi->s_snapshot_cno, - &sb->s_root); - if (err) - goto failed_sbi; - - goto add_to_supers; - } - cno = nilfs_last_cno(nilfs); err = nilfs_attach_checkpoint(sbi, cno, true, &fsroot); if (err) { - printk(KERN_ERR "NILFS: error loading a checkpoint" - " (checkpoint number=%llu).\n", (unsigned long long)cno); + printk(KERN_ERR "NILFS: error loading last checkpoint " + "(checkpoint number=%llu).\n", (unsigned long long)cno); goto failed_sbi; } @@ -910,13 +913,6 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, up_write(&nilfs->ns_sem); } - add_to_supers: - down_write(&nilfs->ns_super_sem); - list_add(&sbi->s_list, &nilfs->ns_supers); - if (!nilfs_test_opt(sbi, SNAPSHOT)) - nilfs->ns_current = sbi; - up_write(&nilfs->ns_super_sem); - return 0; failed_segctor: @@ -928,7 +924,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, failed_sbi: put_nilfs(nilfs); sb->s_fs_info = NULL; - nilfs_put_sbinfo(sbi); + kfree(sbi); return err; } @@ -938,13 +934,10 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) struct the_nilfs *nilfs = sbi->s_nilfs; unsigned long old_sb_flags; struct nilfs_mount_options old_opts; - int was_snapshot, err; + int err; - down_write(&nilfs->ns_super_sem); old_sb_flags = sb->s_flags; old_opts.mount_opt = sbi->s_mount_opt; - old_opts.snapshot_cno = sbi->s_snapshot_cno; - was_snapshot = nilfs_test_opt(sbi, SNAPSHOT); if (!parse_options(data, sb, 1)) { err = -EINVAL; @@ -953,11 +946,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) sb->s_flags = (sb->s_flags & ~MS_POSIXACL); err = -EINVAL; - if (was_snapshot && !(*flags & MS_RDONLY)) { - printk(KERN_ERR "NILFS (device %s): cannot remount snapshot " - "read/write.\n", sb->s_id); - goto restore_opts; - } if (!nilfs_valid_fs(nilfs)) { printk(KERN_WARNING "NILFS (device %s): couldn't " @@ -1014,14 +1002,11 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) up_write(&nilfs->ns_sem); } out: - up_write(&nilfs->ns_super_sem); return 0; restore_opts: sb->s_flags = old_sb_flags; sbi->s_mount_opt = old_opts.mount_opt; - sbi->s_snapshot_cno = old_opts.snapshot_cno; - up_write(&nilfs->ns_super_sem); return err; } @@ -1075,18 +1060,14 @@ static int nilfs_identify(char *data, struct nilfs_super_data *sd) static int nilfs_set_bdev_super(struct super_block *s, void *data) { - struct nilfs_super_data *sd = data; - - s->s_bdev = sd->bdev; + s->s_bdev = data; s->s_dev = s->s_bdev->bd_dev; return 0; } static int nilfs_test_bdev_super(struct super_block *s, void *data) { - struct nilfs_super_data *sd = data; - - return sd->sbi && s->s_fs_info == (void *)sd->sbi; + return (void *)s->s_bdev == data; } static int @@ -1097,7 +1078,8 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, struct super_block *s; fmode_t mode = FMODE_READ; struct the_nilfs *nilfs; - int err, need_to_close = 1; + struct dentry *root_dentry; + int err, s_new = false; if (!(flags & MS_RDONLY)) mode |= FMODE_WRITE; @@ -1106,12 +1088,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, if (IS_ERR(sd.bdev)) return PTR_ERR(sd.bdev); - /* - * To get mount instance using sget() vfs-routine, NILFS needs - * much more information than normal filesystems to identify mount - * instance. For snapshot mounts, not only a mount type (ro-mount - * or rw-mount) but also a checkpoint number is required. - */ sd.cno = 0; sd.flags = flags; if (nilfs_identify((char *)data, &sd)) { @@ -1127,38 +1103,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, mutex_lock(&nilfs->ns_mount_mutex); - if (!sd.cno) { - /* - * Check if an exclusive mount exists or not. - * Snapshot mounts coexist with a current mount - * (i.e. rw-mount or ro-mount), whereas rw-mount and - * ro-mount are mutually exclusive. - */ - down_read(&nilfs->ns_super_sem); - if (nilfs->ns_current && - ((nilfs->ns_current->s_super->s_flags ^ flags) - & MS_RDONLY)) { - up_read(&nilfs->ns_super_sem); - err = -EBUSY; - goto failed_unlock; - } - up_read(&nilfs->ns_super_sem); - } - - /* - * Find existing nilfs_sb_info struct - */ - sd.sbi = nilfs_find_sbinfo(nilfs, !(flags & MS_RDONLY), sd.cno); - - /* - * Get super block instance holding the nilfs_sb_info struct. - * A new instance is allocated if no existing mount is present or - * existing instance has been unmounted. - */ - s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, &sd); - if (sd.sbi) - nilfs_put_sbinfo(sd.sbi); - + s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, sd.bdev); if (IS_ERR(s)) { err = PTR_ERR(s); goto failed_unlock; @@ -1167,6 +1112,8 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, if (!s->s_root) { char b[BDEVNAME_SIZE]; + s_new = true; + /* New superblock instance created */ s->s_flags = flags; s->s_mode = mode; @@ -1179,16 +1126,53 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, goto cancel_new; s->s_flags |= MS_ACTIVE; - need_to_close = 0; + } else if (!sd.cno) { + int busy = false; + + if (nilfs_tree_was_touched(s->s_root)) { + busy = nilfs_try_to_shrink_tree(s->s_root); + if (busy && (flags ^ s->s_flags) & MS_RDONLY) { + printk(KERN_ERR "NILFS: the device already " + "has a %s mount.\n", + (s->s_flags & MS_RDONLY) ? + "read-only" : "read/write"); + err = -EBUSY; + goto failed_super; + } + } + if (!busy) { + /* + * Try remount to setup mount states if the current + * tree is not mounted and only snapshots use this sb. + */ + err = nilfs_remount(s, &flags, data); + if (err) + goto failed_super; + } + } + + if (sd.cno) { + err = nilfs_attach_snapshot(s, sd.cno, &root_dentry); + if (err) { + if (s_new) + goto cancel_new; + goto failed_super; + } + } else { + root_dentry = dget(s->s_root); } mutex_unlock(&nilfs->ns_mount_mutex); put_nilfs(nilfs); - if (need_to_close) + if (!s_new) close_bdev_exclusive(sd.bdev, mode); - simple_set_mnt(mnt, s); + + mnt->mnt_sb = s; + mnt->mnt_root = root_dentry; return 0; + failed_super: + deactivate_locked_super(s); failed_unlock: mutex_unlock(&nilfs->ns_mount_mutex); put_nilfs(nilfs); @@ -1202,7 +1186,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, put_nilfs(nilfs); deactivate_locked_super(s); /* - * deactivate_locked_super() invokes close_bdev_exclusive(). + * This deactivate_locked_super() invokes close_bdev_exclusive(). * We must finish all post-cleaning before this call; * put_nilfs() needs the block device. */ diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 89c78562d0e..960c28797bb 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -82,11 +82,9 @@ static struct the_nilfs *alloc_nilfs(struct block_device *bdev) atomic_set(&nilfs->ns_count, 1); atomic_set(&nilfs->ns_ndirtyblks, 0); init_rwsem(&nilfs->ns_sem); - init_rwsem(&nilfs->ns_super_sem); mutex_init(&nilfs->ns_mount_mutex); init_rwsem(&nilfs->ns_writer_sem); INIT_LIST_HEAD(&nilfs->ns_list); - INIT_LIST_HEAD(&nilfs->ns_supers); INIT_LIST_HEAD(&nilfs->ns_gc_inodes); spin_lock_init(&nilfs->ns_last_segment_lock); nilfs->ns_cptree = RB_ROOT; @@ -307,15 +305,6 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) int valid_fs = nilfs_valid_fs(nilfs); int err; - if (nilfs_loaded(nilfs)) { - if (valid_fs || - ((s_flags & MS_RDONLY) && nilfs_test_opt(sbi, NORECOVERY))) - return 0; - printk(KERN_ERR "NILFS: the filesystem is in an incomplete " - "recovery state.\n"); - return -EINVAL; - } - if (!valid_fs) { printk(KERN_WARNING "NILFS warning: mounting unchecked fs\n"); if (s_flags & MS_RDONLY) { @@ -632,12 +621,7 @@ static int nilfs_load_super_block(struct the_nilfs *nilfs, * * init_nilfs() performs common initialization per block device (e.g. * reading the super block, getting disk layout information, initializing - * shared fields in the_nilfs). It takes on some portion of the jobs - * typically done by a fill_super() routine. This division arises from - * the nature that multiple NILFS instances may be simultaneously - * mounted on a device. - * For multiple mounts on the same device, only the first mount - * invokes these tasks. + * shared fields in the_nilfs). * * Return Value: On success, 0 is returned. On error, a negative error * code is returned. @@ -651,27 +635,6 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) int err; down_write(&nilfs->ns_sem); - if (nilfs_init(nilfs)) { - /* Load values from existing the_nilfs */ - sbp = nilfs->ns_sbp[0]; - err = nilfs_store_magic_and_option(sb, sbp, data); - if (err) - goto out; - - err = nilfs_check_feature_compatibility(sb, sbp); - if (err) - goto out; - - blocksize = BLOCK_SIZE << le32_to_cpu(sbp->s_log_block_size); - if (sb->s_blocksize != blocksize && - !sb_set_blocksize(sb, blocksize)) { - printk(KERN_ERR "NILFS: blocksize %d unfit to device\n", - blocksize); - err = -EINVAL; - } - sb->s_maxbytes = nilfs_max_size(sb->s_blocksize_bits); - goto out; - } blocksize = sb_min_blocksize(sb, NILFS_MIN_BLOCK_SIZE); if (!blocksize) { @@ -901,56 +864,6 @@ void nilfs_put_root(struct nilfs_root *root) } } -/** - * nilfs_find_sbinfo - find existing nilfs_sb_info structure - * @nilfs: nilfs object - * @rw_mount: mount type (non-zero value for read/write mount) - * @cno: checkpoint number (zero for read-only mount) - * - * nilfs_find_sbinfo() returns the nilfs_sb_info structure which - * @rw_mount and @cno (in case of snapshots) matched. If no instance - * was found, NULL is returned. Although the super block instance can - * be unmounted after this function returns, the nilfs_sb_info struct - * is kept on memory until nilfs_put_sbinfo() is called. - */ -struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *nilfs, - int rw_mount, __u64 cno) -{ - struct nilfs_sb_info *sbi; - - down_read(&nilfs->ns_super_sem); - /* - * The SNAPSHOT flag and sb->s_flags are supposed to be - * protected with nilfs->ns_super_sem. - */ - sbi = nilfs->ns_current; - if (rw_mount) { - if (sbi && !(sbi->s_super->s_flags & MS_RDONLY)) - goto found; /* read/write mount */ - else - goto out; - } else if (cno == 0) { - if (sbi && (sbi->s_super->s_flags & MS_RDONLY)) - goto found; /* read-only mount */ - else - goto out; - } - - list_for_each_entry(sbi, &nilfs->ns_supers, s_list) { - if (nilfs_test_opt(sbi, SNAPSHOT) && - sbi->s_snapshot_cno == cno) - goto found; /* snapshot mount */ - } - out: - up_read(&nilfs->ns_super_sem); - return NULL; - - found: - atomic_inc(&sbi->s_count); - up_read(&nilfs->ns_super_sem); - return sbi; -} - int nilfs_checkpoint_is_mounted(struct the_nilfs *nilfs, __u64 cno, int snapshot_mount) { diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index 0afede68a9e..7b43693a69c 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -52,16 +52,13 @@ enum { * @ns_bdi: backing dev info * @ns_writer: back pointer to writable nilfs_sb_info * @ns_sem: semaphore for shared states - * @ns_super_sem: semaphore for global operations across super block instances * @ns_mount_mutex: mutex protecting mount process of nilfs * @ns_writer_sem: semaphore protecting ns_writer attach/detach - * @ns_current: back pointer to current mount * @ns_sbh: buffer heads of on-disk super blocks * @ns_sbp: pointers to super block data * @ns_sbwtime: previous write time of super block * @ns_sbwcount: write count of super block * @ns_sbsize: size of valid data in super block - * @ns_supers: list of nilfs super block structs * @ns_seg_seq: segment sequence counter * @ns_segnum: index number of the latest full segment. * @ns_nextnum: index number of the full segment index to be used next @@ -104,16 +101,9 @@ struct the_nilfs { struct backing_dev_info *ns_bdi; struct nilfs_sb_info *ns_writer; struct rw_semaphore ns_sem; - struct rw_semaphore ns_super_sem; struct mutex ns_mount_mutex; struct rw_semaphore ns_writer_sem; - /* - * components protected by ns_super_sem - */ - struct nilfs_sb_info *ns_current; - struct list_head ns_supers; - /* * used for * - loading the latest checkpoint exclusively. @@ -294,12 +284,6 @@ nilfs_detach_writer(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) up_write(&nilfs->ns_writer_sem); } -static inline void nilfs_put_sbinfo(struct nilfs_sb_info *sbi) -{ - if (atomic_dec_and_test(&sbi->s_count)) - kfree(sbi); -} - static inline int nilfs_valid_fs(struct the_nilfs *nilfs) { unsigned valid_fs; -- cgit v1.2.3-18-g5258 From 348fe8da13621b3d14ab2d156e74551611997017 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 9 Sep 2010 02:07:56 +0900 Subject: nilfs2: simplify life cycle management of nilfs object This stops pre-allocating nilfs object in nilfs_get_sb routine, and stops managing its life cycle by reference counting. nilfs_find_or_create_nilfs() function, nilfs->ns_mount_mutex, nilfs_objects list, and the reference counter will be removed through the simplification. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/ioctl.c | 4 +-- fs/nilfs2/super.c | 67 +++++++++++++++------------------------------ fs/nilfs2/the_nilfs.c | 75 ++++----------------------------------------------- fs/nilfs2/the_nilfs.h | 16 ++--------- 4 files changed, 31 insertions(+), 131 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index 2ee6843c2e8..338858fc907 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c @@ -117,7 +117,7 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, if (copy_from_user(&cpmode, argp, sizeof(cpmode))) goto out; - mutex_lock(&nilfs->ns_mount_mutex); + down_read(&inode->i_sb->s_umount); nilfs_transaction_begin(inode->i_sb, &ti, 0); ret = nilfs_cpfile_change_cpmode( @@ -127,7 +127,7 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, else nilfs_transaction_commit(inode->i_sb); /* never fails */ - mutex_unlock(&nilfs->ns_mount_mutex); + up_read(&inode->i_sb->s_umount); out: mnt_drop_write(filp->f_path.mnt); return ret; diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 2e58e7c629b..5893cb27c90 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -356,7 +356,7 @@ static void nilfs_put_super(struct super_block *sb) up_write(&nilfs->ns_sem); } - put_nilfs(sbi->s_nilfs); + destroy_nilfs(nilfs); sbi->s_super = NULL; sb->s_fs_info = NULL; kfree(sbi); @@ -836,15 +836,14 @@ static int nilfs_try_to_shrink_tree(struct dentry *root_dentry) * @sb: super_block * @data: mount options * @silent: silent mode flag - * @nilfs: the_nilfs struct * * This function is called exclusively by nilfs->ns_mount_mutex. * So, the recovery process is protected from other simultaneous mounts. */ static int -nilfs_fill_super(struct super_block *sb, void *data, int silent, - struct the_nilfs *nilfs) +nilfs_fill_super(struct super_block *sb, void *data, int silent) { + struct the_nilfs *nilfs; struct nilfs_sb_info *sbi; struct nilfs_root *fsroot; __u64 cno; @@ -855,14 +854,18 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, return -ENOMEM; sb->s_fs_info = sbi; + sbi->s_super = sb; - get_nilfs(nilfs); + nilfs = alloc_nilfs(sb->s_bdev); + if (!nilfs) { + err = -ENOMEM; + goto failed_sbi; + } sbi->s_nilfs = nilfs; - sbi->s_super = sb; err = init_nilfs(nilfs, sbi, (char *)data); if (err) - goto failed_sbi; + goto failed_nilfs; spin_lock_init(&sbi->s_inode_lock); INIT_LIST_HEAD(&sbi->s_dirty_files); @@ -885,14 +888,14 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, err = load_nilfs(nilfs, sbi); if (err) - goto failed_sbi; + goto failed_nilfs; cno = nilfs_last_cno(nilfs); err = nilfs_attach_checkpoint(sbi, cno, true, &fsroot); if (err) { printk(KERN_ERR "NILFS: error loading last checkpoint " "(checkpoint number=%llu).\n", (unsigned long long)cno); - goto failed_sbi; + goto failed_nilfs; } if (!(sb->s_flags & MS_RDONLY)) { @@ -921,8 +924,10 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, failed_checkpoint: nilfs_put_root(fsroot); + failed_nilfs: + destroy_nilfs(nilfs); + failed_sbi: - put_nilfs(nilfs); sb->s_fs_info = NULL; kfree(sbi); return err; @@ -1077,7 +1082,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, struct nilfs_super_data sd; struct super_block *s; fmode_t mode = FMODE_READ; - struct the_nilfs *nilfs; struct dentry *root_dentry; int err, s_new = false; @@ -1095,18 +1099,10 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, goto failed; } - nilfs = find_or_create_nilfs(sd.bdev); - if (!nilfs) { - err = -ENOMEM; - goto failed; - } - - mutex_lock(&nilfs->ns_mount_mutex); - s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, sd.bdev); if (IS_ERR(s)) { err = PTR_ERR(s); - goto failed_unlock; + goto failed; } if (!s->s_root) { @@ -1120,10 +1116,9 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, strlcpy(s->s_id, bdevname(sd.bdev, b), sizeof(s->s_id)); sb_set_blocksize(s, block_size(sd.bdev)); - err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0, - nilfs); + err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0); if (err) - goto cancel_new; + goto failed_super; s->s_flags |= MS_ACTIVE; } else if (!sd.cno) { @@ -1153,17 +1148,12 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, if (sd.cno) { err = nilfs_attach_snapshot(s, sd.cno, &root_dentry); - if (err) { - if (s_new) - goto cancel_new; + if (err) goto failed_super; - } } else { root_dentry = dget(s->s_root); } - mutex_unlock(&nilfs->ns_mount_mutex); - put_nilfs(nilfs); if (!s_new) close_bdev_exclusive(sd.bdev, mode); @@ -1173,23 +1163,10 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, failed_super: deactivate_locked_super(s); - failed_unlock: - mutex_unlock(&nilfs->ns_mount_mutex); - put_nilfs(nilfs); - failed: - close_bdev_exclusive(sd.bdev, mode); - return err; - cancel_new: - /* Abandoning the newly allocated superblock */ - mutex_unlock(&nilfs->ns_mount_mutex); - put_nilfs(nilfs); - deactivate_locked_super(s); - /* - * This deactivate_locked_super() invokes close_bdev_exclusive(). - * We must finish all post-cleaning before this call; - * put_nilfs() needs the block device. - */ + failed: + if (!s_new) + close_bdev_exclusive(sd.bdev, mode); return err; } diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 960c28797bb..6eeb4f072f8 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -35,9 +35,6 @@ #include "segbuf.h" -static LIST_HEAD(nilfs_objects); -static DEFINE_SPINLOCK(nilfs_lock); - static int nilfs_valid_sb(struct nilfs_super_block *sbp); void nilfs_set_last_segment(struct the_nilfs *nilfs, @@ -61,16 +58,13 @@ void nilfs_set_last_segment(struct the_nilfs *nilfs, } /** - * alloc_nilfs - allocate the_nilfs structure + * alloc_nilfs - allocate a nilfs object * @bdev: block device to which the_nilfs is related * - * alloc_nilfs() allocates memory for the_nilfs and - * initializes its reference count and locks. - * * Return Value: On success, pointer to the_nilfs is returned. * On error, NULL is returned. */ -static struct the_nilfs *alloc_nilfs(struct block_device *bdev) +struct the_nilfs *alloc_nilfs(struct block_device *bdev) { struct the_nilfs *nilfs; @@ -79,12 +73,9 @@ static struct the_nilfs *alloc_nilfs(struct block_device *bdev) return NULL; nilfs->ns_bdev = bdev; - atomic_set(&nilfs->ns_count, 1); atomic_set(&nilfs->ns_ndirtyblks, 0); init_rwsem(&nilfs->ns_sem); - mutex_init(&nilfs->ns_mount_mutex); init_rwsem(&nilfs->ns_writer_sem); - INIT_LIST_HEAD(&nilfs->ns_list); INIT_LIST_HEAD(&nilfs->ns_gc_inodes); spin_lock_init(&nilfs->ns_last_segment_lock); nilfs->ns_cptree = RB_ROOT; @@ -95,67 +86,11 @@ static struct the_nilfs *alloc_nilfs(struct block_device *bdev) } /** - * find_or_create_nilfs - find or create nilfs object - * @bdev: block device to which the_nilfs is related - * - * find_nilfs() looks up an existent nilfs object created on the - * device and gets the reference count of the object. If no nilfs object - * is found on the device, a new nilfs object is allocated. - * - * Return Value: On success, pointer to the nilfs object is returned. - * On error, NULL is returned. - */ -struct the_nilfs *find_or_create_nilfs(struct block_device *bdev) -{ - struct the_nilfs *nilfs, *new = NULL; - - retry: - spin_lock(&nilfs_lock); - list_for_each_entry(nilfs, &nilfs_objects, ns_list) { - if (nilfs->ns_bdev == bdev) { - get_nilfs(nilfs); - spin_unlock(&nilfs_lock); - if (new) - put_nilfs(new); - return nilfs; /* existing object */ - } - } - if (new) { - list_add_tail(&new->ns_list, &nilfs_objects); - spin_unlock(&nilfs_lock); - return new; /* new object */ - } - spin_unlock(&nilfs_lock); - - new = alloc_nilfs(bdev); - if (new) - goto retry; - return NULL; /* insufficient memory */ -} - -/** - * put_nilfs - release a reference to the_nilfs - * @nilfs: the_nilfs structure to be released - * - * put_nilfs() decrements a reference counter of the_nilfs. - * If the reference count reaches zero, the_nilfs is freed. + * destroy_nilfs - destroy nilfs object + * @nilfs: nilfs object to be released */ -void put_nilfs(struct the_nilfs *nilfs) +void destroy_nilfs(struct the_nilfs *nilfs) { - spin_lock(&nilfs_lock); - if (!atomic_dec_and_test(&nilfs->ns_count)) { - spin_unlock(&nilfs_lock); - return; - } - list_del_init(&nilfs->ns_list); - spin_unlock(&nilfs_lock); - - /* - * Increment of ns_count never occurs below because the caller - * of get_nilfs() holds at least one reference to the_nilfs. - * Thus its exclusion control is not required here. - */ - might_sleep(); if (nilfs_loaded(nilfs)) { nilfs_mdt_destroy(nilfs->ns_sufile); diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index 7b43693a69c..1908dc7bbd8 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -46,13 +46,10 @@ enum { /** * struct the_nilfs - struct to supervise multiple nilfs mount points * @ns_flags: flags - * @ns_count: reference count - * @ns_list: list head for nilfs_list * @ns_bdev: block device * @ns_bdi: backing dev info * @ns_writer: back pointer to writable nilfs_sb_info * @ns_sem: semaphore for shared states - * @ns_mount_mutex: mutex protecting mount process of nilfs * @ns_writer_sem: semaphore protecting ns_writer attach/detach * @ns_sbh: buffer heads of on-disk super blocks * @ns_sbp: pointers to super block data @@ -94,14 +91,11 @@ enum { */ struct the_nilfs { unsigned long ns_flags; - atomic_t ns_count; - struct list_head ns_list; struct block_device *ns_bdev; struct backing_dev_info *ns_bdi; struct nilfs_sb_info *ns_writer; struct rw_semaphore ns_sem; - struct mutex ns_mount_mutex; struct rw_semaphore ns_writer_sem; /* @@ -239,8 +233,8 @@ static inline int nilfs_sb_will_flip(struct the_nilfs *nilfs) } void nilfs_set_last_segment(struct the_nilfs *, sector_t, u64, __u64); -struct the_nilfs *find_or_create_nilfs(struct block_device *); -void put_nilfs(struct the_nilfs *); +struct the_nilfs *alloc_nilfs(struct block_device *bdev); +void destroy_nilfs(struct the_nilfs *nilfs); int init_nilfs(struct the_nilfs *, struct nilfs_sb_info *, char *); int load_nilfs(struct the_nilfs *, struct nilfs_sb_info *); int nilfs_discard_segments(struct the_nilfs *, __u64 *, size_t); @@ -256,12 +250,6 @@ void nilfs_fall_back_super_block(struct the_nilfs *); void nilfs_swap_super_block(struct the_nilfs *); -static inline void get_nilfs(struct the_nilfs *nilfs) -{ - /* Caller must have at least one reference of the_nilfs. */ - atomic_inc(&nilfs->ns_count); -} - static inline void nilfs_get_root(struct nilfs_root *root) { atomic_inc(&root->count); -- cgit v1.2.3-18-g5258 From 518d1a6a1d6610a014875a2fe3252e89b6260d2f Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 20 Aug 2010 23:40:54 +0900 Subject: nilfs2: allow nilfs_clear_inode to clear metadata file inodes Allows clear inode function (nilfs_clear_inode) to handle metadata files that uses bitmap-based object alloctor. DAT and ifile correspond to this. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/inode.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 3efef0ecfa2..5485dd12da6 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -674,6 +674,7 @@ void nilfs_truncate(struct inode *inode) static void nilfs_clear_inode(struct inode *inode) { struct nilfs_inode_info *ii = NILFS_I(inode); + struct nilfs_mdt_info *mdi = NILFS_MDT(inode); /* * Free resources allocated in nilfs_read_inode(), here. @@ -682,6 +683,9 @@ static void nilfs_clear_inode(struct inode *inode) brelse(ii->i_bh); ii->i_bh = NULL; + if (mdi && mdi->mi_palloc_cache) + nilfs_palloc_destroy_cache(inode); + if (test_bit(NILFS_I_BMAP, &ii->i_state)) nilfs_bmap_clear(ii->i_bmap); -- cgit v1.2.3-18-g5258 From adbb39b5485b72dca963a2bc9b1b22bfc19d4967 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 5 Sep 2010 10:14:43 +0900 Subject: nilfs2: do not allocate nilfs_mdt_info structure to gc-inodes GC-inode now doesn't need the nilfs_mdt_info structure and there is no reason that it is a sort of metadata files. This stops the allocation and makes them not dependent on metadata file routines. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/gcinode.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c index 34f8f84a22e..33ad25ddd5c 100644 --- a/fs/nilfs2/gcinode.c +++ b/fs/nilfs2/gcinode.c @@ -168,7 +168,7 @@ int nilfs_gccache_wait_and_mark_dirty(struct buffer_head *bh) } nilfs_btnode_mark_dirty(bh); } else { - nilfs_mdt_mark_buffer_dirty(bh); + nilfs_mark_buffer_dirty(bh); } return 0; } @@ -177,24 +177,24 @@ int nilfs_init_gcinode(struct inode *inode) { struct nilfs_inode_info *ii = NILFS_I(inode); struct the_nilfs *nilfs = NILFS_SB(inode->i_sb)->s_nilfs; - int ret; - ret = nilfs_mdt_init(inode, nilfs, GFP_NOFS, 0); - if (!ret) { - inode->i_mapping->a_ops = &def_gcinode_aops; + inode->i_mode = S_IFREG; + mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); + inode->i_mapping->a_ops = &def_gcinode_aops; + inode->i_mapping->backing_dev_info = inode->i_sb->s_bdi; - ii->i_flags = 0; - nilfs_bmap_init_gc(ii->i_bmap); + ii->i_flags = 0; + nilfs_bmap_init_gc(ii->i_bmap); - /* - * Add the inode to GC inode list. Garbage Collection - * is serialized and no two processes manipulate the - * list simultaneously. - */ - igrab(inode); - list_add(&NILFS_I(inode)->i_dirty, &nilfs->ns_gc_inodes); - } - return ret; + /* + * Add the inode to GC inode list. Garbage Collection + * is serialized and no two processes manipulate the + * list simultaneously. + */ + igrab(inode); + list_add(&NILFS_I(inode)->i_dirty, &nilfs->ns_gc_inodes); + + return 0; } /** -- cgit v1.2.3-18-g5258 From a8070dd365dd995f6139a2fc3aeee10159bdcc45 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 30 Aug 2010 23:42:18 +0900 Subject: nilfs2: add routines to save and restore bmap state This adds routines to save and restore the state of bmap structure. The bmap state is stored in a given nilfs_bmap_store object. These routines will be used to roll back the state of dat inode without using gcdat inode. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/bmap.c | 18 ++++++++++++++++++ fs/nilfs2/bmap.h | 8 ++++++++ 2 files changed, 26 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c index 3dbdc1d356b..00244402d59 100644 --- a/fs/nilfs2/bmap.c +++ b/fs/nilfs2/bmap.c @@ -548,3 +548,21 @@ void nilfs_bmap_commit_gcdat(struct nilfs_bmap *gcbmap, struct nilfs_bmap *bmap) lockdep_set_class(&bmap->b_sem, &nilfs_bmap_dat_lock_key); bmap->b_inode = &NILFS_BMAP_I(bmap)->vfs_inode; } + +void nilfs_bmap_save(const struct nilfs_bmap *bmap, + struct nilfs_bmap_store *store) +{ + memcpy(store->data, bmap->b_u.u_data, sizeof(store->data)); + store->last_allocated_key = bmap->b_last_allocated_key; + store->last_allocated_ptr = bmap->b_last_allocated_ptr; + store->state = bmap->b_state; +} + +void nilfs_bmap_restore(struct nilfs_bmap *bmap, + const struct nilfs_bmap_store *store) +{ + memcpy(bmap->b_u.u_data, store->data, sizeof(store->data)); + bmap->b_last_allocated_key = store->last_allocated_key; + bmap->b_last_allocated_ptr = store->last_allocated_ptr; + bmap->b_state = store->state; +} diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h index a20569b1992..5f3339e3eac 100644 --- a/fs/nilfs2/bmap.h +++ b/fs/nilfs2/bmap.h @@ -135,6 +135,12 @@ struct nilfs_bmap { /* state */ #define NILFS_BMAP_DIRTY 0x00000001 +struct nilfs_bmap_store { + __le64 data[NILFS_BMAP_SIZE / sizeof(__le64)]; + __u64 last_allocated_key; + __u64 last_allocated_ptr; + int state; +}; int nilfs_bmap_test_and_clear_dirty(struct nilfs_bmap *); int nilfs_bmap_read(struct nilfs_bmap *, struct nilfs_inode *); @@ -156,6 +162,8 @@ void nilfs_bmap_init_gc(struct nilfs_bmap *); void nilfs_bmap_init_gcdat(struct nilfs_bmap *, struct nilfs_bmap *); void nilfs_bmap_commit_gcdat(struct nilfs_bmap *, struct nilfs_bmap *); +void nilfs_bmap_save(const struct nilfs_bmap *, struct nilfs_bmap_store *); +void nilfs_bmap_restore(struct nilfs_bmap *, const struct nilfs_bmap_store *); static inline int nilfs_bmap_lookup(struct nilfs_bmap *bmap, __u64 key, __u64 *ptr) -- cgit v1.2.3-18-g5258 From ebdfed4dc59d177cf26013a0c9b8ee9652e9a140 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 6 Sep 2010 12:05:43 +0900 Subject: nilfs2: add routines to roll back state of DAT file This adds optional function to metadata files which makes a copy of bmap, page caches, and b-tree node cache, and rolls back to the copy as needed. This enhancement is intended to displace gcdat inode that provides a similar function in a different way. In this patch, nilfs_shadow_map structure is added to store a copy of the foregoing states. nilfs_mdt_setup_shadow_map relates this structure to a metadata file. And, nilfs_mdt_save_to_shadow_map() and nilfs_mdt_restore_from_shadow_map() provides save and restore functions respectively. Finally, nilfs_mdt_clear_shadow_map() clears states of nilfs_shadow_map. The copy of b-tree node cache and page cache is made by duplicating only dirty pages into corresponding caches in nilfs_shadow_map. Their restoration is done by clearing dirty pages from original caches and by copying dirty pages back from nilfs_shadow_map. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btnode.c | 17 ++------- fs/nilfs2/mdt.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++--- fs/nilfs2/mdt.h | 14 ++++++++ fs/nilfs2/page.c | 25 +++++++++++++ fs/nilfs2/page.h | 4 +++ 5 files changed, 145 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c index f78ab1044d1..5115814cb74 100644 --- a/fs/nilfs2/btnode.c +++ b/fs/nilfs2/btnode.c @@ -37,15 +37,7 @@ void nilfs_btnode_cache_init_once(struct address_space *btnc) { - memset(btnc, 0, sizeof(*btnc)); - INIT_RADIX_TREE(&btnc->page_tree, GFP_ATOMIC); - spin_lock_init(&btnc->tree_lock); - INIT_LIST_HEAD(&btnc->private_list); - spin_lock_init(&btnc->private_lock); - - spin_lock_init(&btnc->i_mmap_lock); - INIT_RAW_PRIO_TREE_ROOT(&btnc->i_mmap); - INIT_LIST_HEAD(&btnc->i_mmap_nonlinear); + nilfs_mapping_init_once(btnc); } static const struct address_space_operations def_btnode_aops = { @@ -55,12 +47,7 @@ static const struct address_space_operations def_btnode_aops = { void nilfs_btnode_cache_init(struct address_space *btnc, struct backing_dev_info *bdi) { - btnc->host = NULL; /* can safely set to host inode ? */ - btnc->flags = 0; - mapping_set_gfp_mask(btnc, GFP_NOFS); - btnc->assoc_mapping = NULL; - btnc->backing_dev_info = bdi; - btnc->a_ops = &def_btnode_aops; + nilfs_mapping_init(btnc, bdi, &def_btnode_aops); } void nilfs_btnode_cache_clear(struct address_space *btnc) diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 73e5da3b097..0066468609d 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -398,16 +398,22 @@ int nilfs_mdt_fetch_dirty(struct inode *inode) static int nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc) { - struct inode *inode = container_of(page->mapping, - struct inode, i_data); - struct super_block *sb = inode->i_sb; - struct the_nilfs *nilfs = NILFS_MDT(inode)->mi_nilfs; + struct inode *inode; + struct super_block *sb; + struct the_nilfs *nilfs; struct nilfs_sb_info *writer = NULL; int err = 0; redirty_page_for_writepage(wbc, page); unlock_page(page); + inode = page->mapping->host; + if (!inode) + return 0; + + sb = inode->i_sb; + nilfs = NILFS_MDT(inode)->mi_nilfs; + if (page->mapping->assoc_mapping) return 0; /* Do not request flush for shadow page cache */ if (!sb) { @@ -567,6 +573,96 @@ void nilfs_mdt_set_shadow(struct inode *orig, struct inode *shadow) &NILFS_I(orig)->i_btnode_cache; } +static const struct address_space_operations shadow_map_aops = { + .sync_page = block_sync_page, +}; + +/** + * nilfs_mdt_setup_shadow_map - setup shadow map and bind it to metadata file + * @inode: inode of the metadata file + * @shadow: shadow mapping + */ +int nilfs_mdt_setup_shadow_map(struct inode *inode, + struct nilfs_shadow_map *shadow) +{ + struct nilfs_mdt_info *mi = NILFS_MDT(inode); + struct backing_dev_info *bdi = NILFS_I_NILFS(inode)->ns_bdi; + + INIT_LIST_HEAD(&shadow->frozen_buffers); + nilfs_mapping_init_once(&shadow->frozen_data); + nilfs_mapping_init(&shadow->frozen_data, bdi, &shadow_map_aops); + nilfs_mapping_init_once(&shadow->frozen_btnodes); + nilfs_mapping_init(&shadow->frozen_btnodes, bdi, &shadow_map_aops); + mi->mi_shadow = shadow; + return 0; +} + +/** + * nilfs_mdt_save_to_shadow_map - copy bmap and dirty pages to shadow map + * @inode: inode of the metadata file + */ +int nilfs_mdt_save_to_shadow_map(struct inode *inode) +{ + struct nilfs_mdt_info *mi = NILFS_MDT(inode); + struct nilfs_inode_info *ii = NILFS_I(inode); + struct nilfs_shadow_map *shadow = mi->mi_shadow; + int ret; + + ret = nilfs_copy_dirty_pages(&shadow->frozen_data, inode->i_mapping); + if (ret) + goto out; + + ret = nilfs_copy_dirty_pages(&shadow->frozen_btnodes, + &ii->i_btnode_cache); + if (ret) + goto out; + + nilfs_bmap_save(ii->i_bmap, &shadow->bmap_store); + out: + return ret; +} + +/** + * nilfs_mdt_restore_from_shadow_map - restore dirty pages and bmap state + * @inode: inode of the metadata file + */ +void nilfs_mdt_restore_from_shadow_map(struct inode *inode) +{ + struct nilfs_mdt_info *mi = NILFS_MDT(inode); + struct nilfs_inode_info *ii = NILFS_I(inode); + struct nilfs_shadow_map *shadow = mi->mi_shadow; + + down_write(&mi->mi_sem); + + if (mi->mi_palloc_cache) + nilfs_palloc_clear_cache(inode); + + nilfs_clear_dirty_pages(inode->i_mapping); + nilfs_copy_back_pages(inode->i_mapping, &shadow->frozen_data); + + nilfs_clear_dirty_pages(&ii->i_btnode_cache); + nilfs_copy_back_pages(&ii->i_btnode_cache, &shadow->frozen_btnodes); + + nilfs_bmap_restore(ii->i_bmap, &shadow->bmap_store); + + up_write(&mi->mi_sem); +} + +/** + * nilfs_mdt_clear_shadow_map - truncate pages in shadow map caches + * @inode: inode of the metadata file + */ +void nilfs_mdt_clear_shadow_map(struct inode *inode) +{ + struct nilfs_mdt_info *mi = NILFS_MDT(inode); + struct nilfs_shadow_map *shadow = mi->mi_shadow; + + down_write(&mi->mi_sem); + truncate_inode_pages(&shadow->frozen_data, 0); + truncate_inode_pages(&shadow->frozen_btnodes, 0); + up_write(&mi->mi_sem); +} + static void nilfs_mdt_clear(struct inode *inode) { struct nilfs_inode_info *ii = NILFS_I(inode); diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h index f44560224bd..e7f0d158c52 100644 --- a/fs/nilfs2/mdt.h +++ b/fs/nilfs2/mdt.h @@ -28,6 +28,13 @@ #include "nilfs.h" #include "page.h" +struct nilfs_shadow_map { + struct nilfs_bmap_store bmap_store; + struct address_space frozen_data; + struct address_space frozen_btnodes; + struct list_head frozen_buffers; +}; + /** * struct nilfs_mdt_info - on-memory private data of meta data files * @mi_nilfs: back pointer to the_nilfs struct @@ -37,6 +44,7 @@ * @mi_first_entry_offset: offset to the first entry * @mi_entries_per_block: number of entries in a block * @mi_palloc_cache: persistent object allocator cache + * @mi_shadow: shadow of bmap and page caches * @mi_blocks_per_group: number of blocks in a group * @mi_blocks_per_desc_block: number of blocks per descriptor block */ @@ -48,6 +56,7 @@ struct nilfs_mdt_info { unsigned mi_first_entry_offset; unsigned long mi_entries_per_block; struct nilfs_palloc_cache *mi_palloc_cache; + struct nilfs_shadow_map *mi_shadow; unsigned long mi_blocks_per_group; unsigned long mi_blocks_per_desc_block; }; @@ -86,6 +95,11 @@ void nilfs_mdt_destroy(struct inode *); void nilfs_mdt_set_entry_size(struct inode *, unsigned, unsigned); void nilfs_mdt_set_shadow(struct inode *, struct inode *); +int nilfs_mdt_setup_shadow_map(struct inode *inode, + struct nilfs_shadow_map *shadow); +int nilfs_mdt_save_to_shadow_map(struct inode *inode); +void nilfs_mdt_restore_from_shadow_map(struct inode *inode); +void nilfs_mdt_clear_shadow_map(struct inode *inode); #define nilfs_mdt_mark_buffer_dirty(bh) nilfs_mark_buffer_dirty(bh) diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index aab11db2cb0..6384ac14c0c 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -513,6 +513,31 @@ unsigned nilfs_page_count_clean_buffers(struct page *page, } return nc; } + +void nilfs_mapping_init_once(struct address_space *mapping) +{ + memset(mapping, 0, sizeof(*mapping)); + INIT_RADIX_TREE(&mapping->page_tree, GFP_ATOMIC); + spin_lock_init(&mapping->tree_lock); + INIT_LIST_HEAD(&mapping->private_list); + spin_lock_init(&mapping->private_lock); + + spin_lock_init(&mapping->i_mmap_lock); + INIT_RAW_PRIO_TREE_ROOT(&mapping->i_mmap); + INIT_LIST_HEAD(&mapping->i_mmap_nonlinear); +} + +void nilfs_mapping_init(struct address_space *mapping, + struct backing_dev_info *bdi, + const struct address_space_operations *aops) +{ + mapping->host = NULL; + mapping->flags = 0; + mapping_set_gfp_mask(mapping, GFP_NOFS); + mapping->assoc_mapping = NULL; + mapping->backing_dev_info = bdi; + mapping->a_ops = aops; +} /* * NILFS2 needs clear_page_dirty() in the following two cases: diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h index f53d8da41ed..6ec4f498fc2 100644 --- a/fs/nilfs2/page.h +++ b/fs/nilfs2/page.h @@ -59,6 +59,10 @@ void nilfs_free_private_page(struct page *); int nilfs_copy_dirty_pages(struct address_space *, struct address_space *); void nilfs_copy_back_pages(struct address_space *, struct address_space *); void nilfs_clear_dirty_pages(struct address_space *); +void nilfs_mapping_init_once(struct address_space *mapping); +void nilfs_mapping_init(struct address_space *mapping, + struct backing_dev_info *bdi, + const struct address_space_operations *aops); unsigned nilfs_page_count_clean_buffers(struct page *, unsigned, unsigned); #define NILFS_PAGE_BUG(page, m, a...) \ -- cgit v1.2.3-18-g5258 From b1f6a4f294088b3fcf9ae67915ca550a1ded2819 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Tue, 31 Aug 2010 11:40:34 +0900 Subject: nilfs2: add routines to redirect access to buffers of DAT file During garbage collection (GC), DAT file, which converts virtual block number to real block number, may return disk block number that is not yet written to the device. To avoid access to unwritten blocks, the current implementation stores changes to the caches of GCDAT during GC and atomically commit the changes into the DAT file after they are written to the device. This patch, instead, adds a function that makes a copy of specified buffer and stores it in nilfs_shadow_map, and a function to get the backup copy as needed (nilfs_mdt_freeze_buffer and nilfs_mdt_get_frozen_buffer respectively). Before DAT changes block number in an entry block, it makes a copy and redirect access to the buffer so that address conversion function (i.e. nilfs_dat_translate) refers to the old address saved in the copy. This patch gives requisites for such redirection. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/mdt.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/nilfs2/mdt.h | 3 +++ fs/nilfs2/page.c | 2 ++ fs/nilfs2/page.h | 2 ++ fs/nilfs2/segment.c | 1 + 5 files changed, 75 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 0066468609d..532f85acf27 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -622,6 +622,72 @@ int nilfs_mdt_save_to_shadow_map(struct inode *inode) return ret; } +int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh) +{ + struct nilfs_shadow_map *shadow = NILFS_MDT(inode)->mi_shadow; + struct buffer_head *bh_frozen; + struct page *page; + int blkbits = inode->i_blkbits; + int ret = -ENOMEM; + + page = grab_cache_page(&shadow->frozen_data, bh->b_page->index); + if (!page) + return ret; + + if (!page_has_buffers(page)) + create_empty_buffers(page, 1 << blkbits, 0); + + bh_frozen = nilfs_page_get_nth_block(page, bh_offset(bh) >> blkbits); + if (bh_frozen) { + if (!buffer_uptodate(bh_frozen)) + nilfs_copy_buffer(bh_frozen, bh); + if (list_empty(&bh_frozen->b_assoc_buffers)) { + list_add_tail(&bh_frozen->b_assoc_buffers, + &shadow->frozen_buffers); + set_buffer_nilfs_redirected(bh); + } else { + brelse(bh_frozen); /* already frozen */ + } + ret = 0; + } + unlock_page(page); + page_cache_release(page); + return ret; +} + +struct buffer_head * +nilfs_mdt_get_frozen_buffer(struct inode *inode, struct buffer_head *bh) +{ + struct nilfs_shadow_map *shadow = NILFS_MDT(inode)->mi_shadow; + struct buffer_head *bh_frozen = NULL; + struct page *page; + int n; + + page = find_lock_page(&shadow->frozen_data, bh->b_page->index); + if (page) { + if (page_has_buffers(page)) { + n = bh_offset(bh) >> inode->i_blkbits; + bh_frozen = nilfs_page_get_nth_block(page, n); + } + unlock_page(page); + page_cache_release(page); + } + return bh_frozen; +} + +static void nilfs_release_frozen_buffers(struct nilfs_shadow_map *shadow) +{ + struct list_head *head = &shadow->frozen_buffers; + struct buffer_head *bh; + + while (!list_empty(head)) { + bh = list_first_entry(head, struct buffer_head, + b_assoc_buffers); + list_del_init(&bh->b_assoc_buffers); + brelse(bh); /* drop ref-count to make it releasable */ + } +} + /** * nilfs_mdt_restore_from_shadow_map - restore dirty pages and bmap state * @inode: inode of the metadata file @@ -658,6 +724,7 @@ void nilfs_mdt_clear_shadow_map(struct inode *inode) struct nilfs_shadow_map *shadow = mi->mi_shadow; down_write(&mi->mi_sem); + nilfs_release_frozen_buffers(shadow); truncate_inode_pages(&shadow->frozen_data, 0); truncate_inode_pages(&shadow->frozen_btnodes, 0); up_write(&mi->mi_sem); diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h index e7f0d158c52..e60bbfe899f 100644 --- a/fs/nilfs2/mdt.h +++ b/fs/nilfs2/mdt.h @@ -100,6 +100,9 @@ int nilfs_mdt_setup_shadow_map(struct inode *inode, int nilfs_mdt_save_to_shadow_map(struct inode *inode); void nilfs_mdt_restore_from_shadow_map(struct inode *inode); void nilfs_mdt_clear_shadow_map(struct inode *inode); +int nilfs_mdt_freeze_buffer(struct inode *inode, struct buffer_head *bh); +struct buffer_head *nilfs_mdt_get_frozen_buffer(struct inode *inode, + struct buffer_head *bh); #define nilfs_mdt_mark_buffer_dirty(bh) nilfs_mark_buffer_dirty(bh) diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 6384ac14c0c..7083344ac88 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -131,6 +131,7 @@ void nilfs_forget_buffer(struct buffer_head *bh) lock_buffer(bh); clear_buffer_nilfs_volatile(bh); clear_buffer_nilfs_checked(bh); + clear_buffer_nilfs_redirected(bh); clear_buffer_dirty(bh); if (nilfs_page_buffers_clean(page)) __nilfs_clear_page_dirty(page); @@ -483,6 +484,7 @@ void nilfs_clear_dirty_pages(struct address_space *mapping) clear_buffer_dirty(bh); clear_buffer_nilfs_volatile(bh); clear_buffer_nilfs_checked(bh); + clear_buffer_nilfs_redirected(bh); clear_buffer_uptodate(bh); clear_buffer_mapped(bh); unlock_buffer(bh); diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h index 6ec4f498fc2..fb9e8a8a203 100644 --- a/fs/nilfs2/page.h +++ b/fs/nilfs2/page.h @@ -35,12 +35,14 @@ enum { BH_NILFS_Node, BH_NILFS_Volatile, BH_NILFS_Checked, + BH_NILFS_Redirected, }; BUFFER_FNS(NILFS_Allocated, nilfs_allocated) /* nilfs private buffers */ BUFFER_FNS(NILFS_Node, nilfs_node) /* nilfs node buffers */ BUFFER_FNS(NILFS_Volatile, nilfs_volatile) BUFFER_FNS(NILFS_Checked, nilfs_checked) /* buffer is verified */ +BUFFER_FNS(NILFS_Redirected, nilfs_redirected) /* redirected to a copy */ void nilfs_mark_buffer_dirty(struct buffer_head *bh); diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index b75306d642c..91dc0668ec8 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -1908,6 +1908,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) set_buffer_uptodate(bh); clear_buffer_dirty(bh); clear_buffer_nilfs_volatile(bh); + clear_buffer_nilfs_redirected(bh); if (bh == segbuf->sb_super_root) { if (bh->b_page != bd_page) { end_page_writeback(bd_page); -- cgit v1.2.3-18-g5258 From c1c1d7092072093ad960db2f6c08f06705c57fa4 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 29 Aug 2010 12:44:56 +0900 Subject: nilfs2: get rid of GCDAT inode This applies prepared rollback function and redirect function of metadata file to DAT file, and eliminates GCDAT inode. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/Makefile | 2 +- fs/nilfs2/bmap.c | 16 ---------- fs/nilfs2/bmap.h | 2 -- fs/nilfs2/dat.c | 30 +++++++++++++++++- fs/nilfs2/gcdat.c | 87 --------------------------------------------------- fs/nilfs2/mdt.c | 9 ------ fs/nilfs2/mdt.h | 1 - fs/nilfs2/nilfs.h | 8 +---- fs/nilfs2/page.c | 28 ++--------------- fs/nilfs2/segment.c | 14 ++++----- fs/nilfs2/the_nilfs.c | 13 +------- fs/nilfs2/the_nilfs.h | 2 -- 12 files changed, 41 insertions(+), 171 deletions(-) delete mode 100644 fs/nilfs2/gcdat.c (limited to 'fs') diff --git a/fs/nilfs2/Makefile b/fs/nilfs2/Makefile index df3e62c1ddc..85c98737a14 100644 --- a/fs/nilfs2/Makefile +++ b/fs/nilfs2/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_NILFS2_FS) += nilfs2.o nilfs2-y := inode.o file.o dir.o super.o namei.o page.o mdt.o \ btnode.o bmap.o btree.o direct.o dat.o recovery.o \ the_nilfs.o segbuf.o segment.o cpfile.o sufile.o \ - ifile.o alloc.o gcinode.o ioctl.o gcdat.o + ifile.o alloc.o gcinode.o ioctl.o diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c index 00244402d59..8b782b062ba 100644 --- a/fs/nilfs2/bmap.c +++ b/fs/nilfs2/bmap.c @@ -533,22 +533,6 @@ void nilfs_bmap_init_gc(struct nilfs_bmap *bmap) nilfs_btree_init_gc(bmap); } -void nilfs_bmap_init_gcdat(struct nilfs_bmap *gcbmap, struct nilfs_bmap *bmap) -{ - memcpy(gcbmap, bmap, sizeof(*bmap)); - init_rwsem(&gcbmap->b_sem); - lockdep_set_class(&bmap->b_sem, &nilfs_bmap_dat_lock_key); - gcbmap->b_inode = &NILFS_BMAP_I(gcbmap)->vfs_inode; -} - -void nilfs_bmap_commit_gcdat(struct nilfs_bmap *gcbmap, struct nilfs_bmap *bmap) -{ - memcpy(bmap, gcbmap, sizeof(*bmap)); - init_rwsem(&bmap->b_sem); - lockdep_set_class(&bmap->b_sem, &nilfs_bmap_dat_lock_key); - bmap->b_inode = &NILFS_BMAP_I(bmap)->vfs_inode; -} - void nilfs_bmap_save(const struct nilfs_bmap *bmap, struct nilfs_bmap_store *store) { diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h index 5f3339e3eac..bde1c0aa2e1 100644 --- a/fs/nilfs2/bmap.h +++ b/fs/nilfs2/bmap.h @@ -159,8 +159,6 @@ int nilfs_bmap_lookup_at_level(struct nilfs_bmap *, __u64, int, __u64 *); int nilfs_bmap_mark(struct nilfs_bmap *, __u64, int); void nilfs_bmap_init_gc(struct nilfs_bmap *); -void nilfs_bmap_init_gcdat(struct nilfs_bmap *, struct nilfs_bmap *); -void nilfs_bmap_commit_gcdat(struct nilfs_bmap *, struct nilfs_bmap *); void nilfs_bmap_save(const struct nilfs_bmap *, struct nilfs_bmap_store *); void nilfs_bmap_restore(struct nilfs_bmap *, const struct nilfs_bmap_store *); diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c index 01314675568..7091c4e0f04 100644 --- a/fs/nilfs2/dat.c +++ b/fs/nilfs2/dat.c @@ -36,6 +36,7 @@ struct nilfs_dat_info { struct nilfs_mdt_info mi; struct nilfs_palloc_cache palloc_cache; + struct nilfs_shadow_map shadow; }; static inline struct nilfs_dat_info *NILFS_DAT_I(struct inode *dat) @@ -327,6 +328,23 @@ int nilfs_dat_move(struct inode *dat, __u64 vblocknr, sector_t blocknr) ret = nilfs_palloc_get_entry_block(dat, vblocknr, 0, &entry_bh); if (ret < 0) return ret; + + /* + * The given disk block number (blocknr) is not yet written to + * the device at this point. + * + * To prevent nilfs_dat_translate() from returning the + * uncommited block number, this makes a copy of the entry + * buffer and redirects nilfs_dat_translate() to the copy. + */ + if (!buffer_nilfs_redirected(entry_bh)) { + ret = nilfs_mdt_freeze_buffer(dat, entry_bh); + if (ret) { + brelse(entry_bh); + return ret; + } + } + kaddr = kmap_atomic(entry_bh->b_page, KM_USER0); entry = nilfs_palloc_block_get_entry(dat, vblocknr, entry_bh, kaddr); if (unlikely(entry->de_blocknr == cpu_to_le64(0))) { @@ -371,7 +389,7 @@ int nilfs_dat_move(struct inode *dat, __u64 vblocknr, sector_t blocknr) */ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp) { - struct buffer_head *entry_bh; + struct buffer_head *entry_bh, *bh; struct nilfs_dat_entry *entry; sector_t blocknr; void *kaddr; @@ -381,6 +399,15 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp) if (ret < 0) return ret; + if (!nilfs_doing_gc() && buffer_nilfs_redirected(entry_bh)) { + bh = nilfs_mdt_get_frozen_buffer(dat, entry_bh); + if (bh) { + WARN_ON(!buffer_uptodate(bh)); + brelse(entry_bh); + entry_bh = bh; + } + } + kaddr = kmap_atomic(entry_bh->b_page, KM_USER0); entry = nilfs_palloc_block_get_entry(dat, vblocknr, entry_bh, kaddr); blocknr = le64_to_cpu(entry->de_blocknr); @@ -468,6 +495,7 @@ struct inode *nilfs_dat_new(struct the_nilfs *nilfs, size_t entry_size) di = NILFS_DAT_I(dat); lockdep_set_class(&di->mi.mi_sem, &dat_lock_key); nilfs_palloc_setup_cache(dat, &di->palloc_cache); + nilfs_mdt_setup_shadow_map(dat, &di->shadow); } return dat; } diff --git a/fs/nilfs2/gcdat.c b/fs/nilfs2/gcdat.c deleted file mode 100644 index 84a45d1d546..00000000000 --- a/fs/nilfs2/gcdat.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * gcdat.c - NILFS shadow DAT inode for GC - * - * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Seiji Kihara , Amagai Yoshiji , - * and Ryusuke Konishi . - * - */ - -#include -#include "nilfs.h" -#include "page.h" -#include "mdt.h" - -int nilfs_init_gcdat_inode(struct the_nilfs *nilfs) -{ - struct inode *dat = nilfs->ns_dat, *gcdat = nilfs->ns_gc_dat; - struct nilfs_inode_info *dii = NILFS_I(dat), *gii = NILFS_I(gcdat); - int err; - - gcdat->i_state = 0; - gcdat->i_blocks = dat->i_blocks; - gii->i_flags = dii->i_flags; - gii->i_state = dii->i_state | (1 << NILFS_I_GCDAT); - gii->i_cno = 0; - nilfs_bmap_init_gcdat(gii->i_bmap, dii->i_bmap); - err = nilfs_copy_dirty_pages(gcdat->i_mapping, dat->i_mapping); - if (unlikely(err)) - return err; - - return nilfs_copy_dirty_pages(&gii->i_btnode_cache, - &dii->i_btnode_cache); -} - -void nilfs_commit_gcdat_inode(struct the_nilfs *nilfs) -{ - struct inode *dat = nilfs->ns_dat, *gcdat = nilfs->ns_gc_dat; - struct nilfs_inode_info *dii = NILFS_I(dat), *gii = NILFS_I(gcdat); - struct address_space *mapping = dat->i_mapping; - struct address_space *gmapping = gcdat->i_mapping; - - down_write(&NILFS_MDT(dat)->mi_sem); - dat->i_blocks = gcdat->i_blocks; - dii->i_flags = gii->i_flags; - dii->i_state = gii->i_state & ~(1 << NILFS_I_GCDAT); - - nilfs_bmap_commit_gcdat(gii->i_bmap, dii->i_bmap); - - nilfs_palloc_clear_cache(dat); - nilfs_palloc_clear_cache(gcdat); - nilfs_clear_dirty_pages(mapping); - nilfs_copy_back_pages(mapping, gmapping); - /* note: mdt dirty flags should be cleared by segctor. */ - - nilfs_clear_dirty_pages(&dii->i_btnode_cache); - nilfs_copy_back_pages(&dii->i_btnode_cache, &gii->i_btnode_cache); - - up_write(&NILFS_MDT(dat)->mi_sem); -} - -void nilfs_clear_gcdat_inode(struct the_nilfs *nilfs) -{ - struct inode *gcdat = nilfs->ns_gc_dat; - struct nilfs_inode_info *gii = NILFS_I(gcdat); - - gcdat->i_state = I_FREEING | I_CLEAR; - gii->i_flags = 0; - - nilfs_palloc_clear_cache(gcdat); - truncate_inode_pages(gcdat->i_mapping, 0); - truncate_inode_pages(&gii->i_btnode_cache, 0); -} diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 532f85acf27..3bbd340a513 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -414,8 +414,6 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc) sb = inode->i_sb; nilfs = NILFS_MDT(inode)->mi_nilfs; - if (page->mapping->assoc_mapping) - return 0; /* Do not request flush for shadow page cache */ if (!sb) { down_read(&nilfs->ns_writer_sem); writer = nilfs->ns_writer; @@ -566,13 +564,6 @@ void nilfs_mdt_set_entry_size(struct inode *inode, unsigned entry_size, mi->mi_first_entry_offset = DIV_ROUND_UP(header_size, entry_size); } -void nilfs_mdt_set_shadow(struct inode *orig, struct inode *shadow) -{ - shadow->i_mapping->assoc_mapping = orig->i_mapping; - NILFS_I(shadow)->i_btnode_cache.assoc_mapping = - &NILFS_I(orig)->i_btnode_cache; -} - static const struct address_space_operations shadow_map_aops = { .sync_page = block_sync_page, }; diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h index e60bbfe899f..1e0901c3fd6 100644 --- a/fs/nilfs2/mdt.h +++ b/fs/nilfs2/mdt.h @@ -93,7 +93,6 @@ struct inode *nilfs_mdt_new_common(struct the_nilfs *, struct super_block *, ino_t); void nilfs_mdt_destroy(struct inode *); void nilfs_mdt_set_entry_size(struct inode *, unsigned, unsigned); -void nilfs_mdt_set_shadow(struct inode *, struct inode *); int nilfs_mdt_setup_shadow_map(struct inode *inode, struct nilfs_shadow_map *shadow); diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index cf5507a2a17..e9f457951e3 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -101,7 +101,6 @@ enum { NILFS_I_INODE_DIRTY, /* write_inode is requested */ NILFS_I_BMAP, /* has bmap and btnode_cache */ NILFS_I_GCINODE, /* inode for GC, on memory only */ - NILFS_I_GCDAT, /* shadow DAT, on memory only */ }; /* @@ -193,7 +192,7 @@ static inline int nilfs_doing_construction(void) static inline struct inode *nilfs_dat_inode(const struct the_nilfs *nilfs) { - return nilfs_doing_gc() ? nilfs->ns_gc_dat : nilfs->ns_dat; + return nilfs->ns_dat; } /* @@ -294,11 +293,6 @@ int nilfs_gccache_wait_and_mark_dirty(struct buffer_head *); int nilfs_init_gcinode(struct inode *inode); void nilfs_remove_all_gcinodes(struct the_nilfs *nilfs); -/* gcdat.c */ -int nilfs_init_gcdat_inode(struct the_nilfs *); -void nilfs_commit_gcdat_inode(struct the_nilfs *); -void nilfs_clear_gcdat_inode(struct the_nilfs *); - /* * Inodes and files operations */ diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 7083344ac88..a6c3c2e817f 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -79,8 +79,8 @@ struct buffer_head *nilfs_grab_buffer(struct inode *inode, { int blkbits = inode->i_blkbits; pgoff_t index = blkoff >> (PAGE_CACHE_SHIFT - blkbits); - struct page *page, *opage; - struct buffer_head *bh, *obh; + struct page *page; + struct buffer_head *bh; page = grab_cache_page(mapping, index); if (unlikely(!page)) @@ -92,30 +92,6 @@ struct buffer_head *nilfs_grab_buffer(struct inode *inode, page_cache_release(page); return NULL; } - if (!buffer_uptodate(bh) && mapping->assoc_mapping != NULL) { - /* - * Shadow page cache uses assoc_mapping to point its original - * page cache. The following code tries the original cache - * if the given cache is a shadow and it didn't hit. - */ - opage = find_lock_page(mapping->assoc_mapping, index); - if (!opage) - return bh; - - obh = __nilfs_get_page_block(opage, blkoff, index, blkbits, - b_state); - if (buffer_uptodate(obh)) { - nilfs_copy_buffer(bh, obh); - if (buffer_dirty(obh)) { - nilfs_mark_buffer_dirty(bh); - if (!buffer_nilfs_node(bh) && NILFS_MDT(inode)) - nilfs_mdt_mark_dirty(inode); - } - } - brelse(obh); - unlock_page(opage); - page_cache_release(opage); - } return bh; } diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 91dc0668ec8..b0c5e08d06c 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -1945,11 +1945,9 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) nilfs_drop_collected_inodes(&sci->sc_dirty_files); - if (nilfs_doing_gc()) { + if (nilfs_doing_gc()) nilfs_drop_collected_inodes(&sci->sc_gc_inodes); - if (update_sr) - nilfs_commit_gcdat_inode(nilfs); - } else + else nilfs->ns_nongc_ctime = sci->sc_seg_ctime; sci->sc_nblk_inc += sci->sc_nblk_this_inc; @@ -2472,13 +2470,15 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, nilfs_transaction_lock(sbi, &ti, 1); - err = nilfs_init_gcdat_inode(nilfs); + err = nilfs_mdt_save_to_shadow_map(nilfs->ns_dat); if (unlikely(err)) goto out_unlock; err = nilfs_ioctl_prepare_clean_segments(nilfs, argv, kbufs); - if (unlikely(err)) + if (unlikely(err)) { + nilfs_mdt_restore_from_shadow_map(nilfs->ns_dat); goto out_unlock; + } sci->sc_freesegs = kbufs[4]; sci->sc_nfreesegs = argv[4].v_nmembs; @@ -2510,7 +2510,7 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, out_unlock: sci->sc_freesegs = NULL; sci->sc_nfreesegs = 0; - nilfs_clear_gcdat_inode(nilfs); + nilfs_mdt_clear_shadow_map(nilfs->ns_dat); nilfs_transaction_unlock(sbi); return err; } diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 6eeb4f072f8..b7666bc0425 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -96,7 +96,6 @@ void destroy_nilfs(struct the_nilfs *nilfs) nilfs_mdt_destroy(nilfs->ns_sufile); nilfs_mdt_destroy(nilfs->ns_cpfile); nilfs_mdt_destroy(nilfs->ns_dat); - nilfs_mdt_destroy(nilfs->ns_gc_dat); } if (nilfs_init(nilfs)) { brelse(nilfs->ns_sbh[0]); @@ -131,20 +130,14 @@ static int nilfs_load_super_root(struct the_nilfs *nilfs, sector_t sr_block) if (unlikely(!nilfs->ns_dat)) goto failed; - nilfs->ns_gc_dat = nilfs_dat_new(nilfs, dat_entry_size); - if (unlikely(!nilfs->ns_gc_dat)) - goto failed_dat; - nilfs->ns_cpfile = nilfs_cpfile_new(nilfs, checkpoint_size); if (unlikely(!nilfs->ns_cpfile)) - goto failed_gc_dat; + goto failed_dat; nilfs->ns_sufile = nilfs_sufile_new(nilfs, segment_usage_size); if (unlikely(!nilfs->ns_sufile)) goto failed_cpfile; - nilfs_mdt_set_shadow(nilfs->ns_dat, nilfs->ns_gc_dat); - err = nilfs_dat_read(nilfs->ns_dat, (void *)bh_sr->b_data + NILFS_SR_DAT_OFFSET(inode_size)); if (unlikely(err)) @@ -173,9 +166,6 @@ static int nilfs_load_super_root(struct the_nilfs *nilfs, sector_t sr_block) failed_cpfile: nilfs_mdt_destroy(nilfs->ns_cpfile); - failed_gc_dat: - nilfs_mdt_destroy(nilfs->ns_gc_dat); - failed_dat: nilfs_mdt_destroy(nilfs->ns_dat); goto failed; @@ -371,7 +361,6 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) nilfs_mdt_destroy(nilfs->ns_cpfile); nilfs_mdt_destroy(nilfs->ns_sufile); nilfs_mdt_destroy(nilfs->ns_dat); - nilfs_mdt_destroy(nilfs->ns_gc_dat); failed: nilfs_clear_recovery_info(&ri); diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index 1908dc7bbd8..a5178dc43df 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -74,7 +74,6 @@ enum { * @ns_dat: DAT file inode * @ns_cpfile: checkpoint file inode * @ns_sufile: segusage file inode - * @ns_gc_dat: shadow inode of the DAT file inode for GC * @ns_cptree: rb-tree of all mounted checkpoints (nilfs_root) * @ns_cptree_lock: lock protecting @ns_cptree * @ns_gc_inodes: dummy inodes to keep live blocks @@ -149,7 +148,6 @@ struct the_nilfs { struct inode *ns_dat; struct inode *ns_cpfile; struct inode *ns_sufile; - struct inode *ns_gc_dat; /* Checkpoint tree */ struct rb_root ns_cptree; -- cgit v1.2.3-18-g5258 From f1e89c86fdd0f5e59f6768146c86437934202033 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 5 Sep 2010 12:20:59 +0900 Subject: nilfs2: use iget for all metadata files This makes use of iget5_locked to allocate or get inode for metadata files to stop using own inode allocator. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/cpfile.c | 49 +++++++++++++++++++++------------- fs/nilfs2/cpfile.h | 4 +-- fs/nilfs2/dat.c | 61 ++++++++++++++++++++++++------------------ fs/nilfs2/dat.h | 4 +-- fs/nilfs2/ifile.c | 51 ++++++++++++++++++++++++----------- fs/nilfs2/ifile.h | 4 ++- fs/nilfs2/inode.c | 13 ++++++--- fs/nilfs2/mdt.c | 16 +++++------ fs/nilfs2/mdt.h | 3 +-- fs/nilfs2/nilfs.h | 2 ++ fs/nilfs2/sufile.c | 73 +++++++++++++++++++++++++++++---------------------- fs/nilfs2/sufile.h | 4 +-- fs/nilfs2/super.c | 21 ++++++++++----- fs/nilfs2/the_nilfs.c | 58 ++++++++++++++-------------------------- 14 files changed, 207 insertions(+), 156 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c index 18737818db6..03de1da8795 100644 --- a/fs/nilfs2/cpfile.c +++ b/fs/nilfs2/cpfile.c @@ -933,27 +933,40 @@ int nilfs_cpfile_get_stat(struct inode *cpfile, struct nilfs_cpstat *cpstat) } /** - * nilfs_cpfile_read - read cpfile inode - * @cpfile: cpfile inode - * @raw_inode: on-disk cpfile inode - */ -int nilfs_cpfile_read(struct inode *cpfile, struct nilfs_inode *raw_inode) -{ - return nilfs_read_inode_common(cpfile, raw_inode); -} - -/** - * nilfs_cpfile_new - create cpfile - * @nilfs: nilfs object + * nilfs_cpfile_read - read or get cpfile inode + * @sb: super block instance * @cpsize: size of a checkpoint entry + * @raw_inode: on-disk cpfile inode + * @inodep: buffer to store the inode */ -struct inode *nilfs_cpfile_new(struct the_nilfs *nilfs, size_t cpsize) +int nilfs_cpfile_read(struct super_block *sb, size_t cpsize, + struct nilfs_inode *raw_inode, struct inode **inodep) { struct inode *cpfile; + int err; - cpfile = nilfs_mdt_new(nilfs, NULL, NILFS_CPFILE_INO, 0); - if (cpfile) - nilfs_mdt_set_entry_size(cpfile, cpsize, - sizeof(struct nilfs_cpfile_header)); - return cpfile; + cpfile = nilfs_iget_locked(sb, NULL, NILFS_CPFILE_INO); + if (unlikely(!cpfile)) + return -ENOMEM; + if (!(cpfile->i_state & I_NEW)) + goto out; + + err = nilfs_mdt_init(cpfile, NILFS_MDT_GFP, 0); + if (err) + goto failed; + + nilfs_mdt_set_entry_size(cpfile, cpsize, + sizeof(struct nilfs_cpfile_header)); + + err = nilfs_read_inode_common(cpfile, raw_inode); + if (err) + goto failed; + + unlock_new_inode(cpfile); + out: + *inodep = cpfile; + return 0; + failed: + iget_failed(cpfile); + return err; } diff --git a/fs/nilfs2/cpfile.h b/fs/nilfs2/cpfile.h index bc0809e0ab4..a242b9a314f 100644 --- a/fs/nilfs2/cpfile.h +++ b/fs/nilfs2/cpfile.h @@ -40,7 +40,7 @@ int nilfs_cpfile_get_stat(struct inode *, struct nilfs_cpstat *); ssize_t nilfs_cpfile_get_cpinfo(struct inode *, __u64 *, int, void *, unsigned, size_t); -int nilfs_cpfile_read(struct inode *cpfile, struct nilfs_inode *raw_inode); -struct inode *nilfs_cpfile_new(struct the_nilfs *nilfs, size_t cpsize); +int nilfs_cpfile_read(struct super_block *sb, size_t cpsize, + struct nilfs_inode *raw_inode, struct inode **inodep); #endif /* _NILFS_CPFILE_H */ diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c index 7091c4e0f04..ab04a68f425 100644 --- a/fs/nilfs2/dat.c +++ b/fs/nilfs2/dat.c @@ -463,39 +463,48 @@ ssize_t nilfs_dat_get_vinfo(struct inode *dat, void *buf, unsigned visz, } /** - * nilfs_dat_read - read dat inode - * @dat: dat inode - * @raw_inode: on-disk dat inode - */ -int nilfs_dat_read(struct inode *dat, struct nilfs_inode *raw_inode) -{ - return nilfs_read_inode_common(dat, raw_inode); -} - -/** - * nilfs_dat_new - create dat file - * @nilfs: nilfs object + * nilfs_dat_read - read or get dat inode + * @sb: super block instance * @entry_size: size of a dat entry + * @raw_inode: on-disk dat inode + * @inodep: buffer to store the inode */ -struct inode *nilfs_dat_new(struct the_nilfs *nilfs, size_t entry_size) +int nilfs_dat_read(struct super_block *sb, size_t entry_size, + struct nilfs_inode *raw_inode, struct inode **inodep) { static struct lock_class_key dat_lock_key; struct inode *dat; struct nilfs_dat_info *di; int err; - dat = nilfs_mdt_new(nilfs, NULL, NILFS_DAT_INO, sizeof(*di)); - if (dat) { - err = nilfs_palloc_init_blockgroup(dat, entry_size); - if (unlikely(err)) { - nilfs_mdt_destroy(dat); - return NULL; - } + dat = nilfs_iget_locked(sb, NULL, NILFS_DAT_INO); + if (unlikely(!dat)) + return -ENOMEM; + if (!(dat->i_state & I_NEW)) + goto out; - di = NILFS_DAT_I(dat); - lockdep_set_class(&di->mi.mi_sem, &dat_lock_key); - nilfs_palloc_setup_cache(dat, &di->palloc_cache); - nilfs_mdt_setup_shadow_map(dat, &di->shadow); - } - return dat; + err = nilfs_mdt_init(dat, NILFS_MDT_GFP, sizeof(*di)); + if (err) + goto failed; + + err = nilfs_palloc_init_blockgroup(dat, entry_size); + if (err) + goto failed; + + di = NILFS_DAT_I(dat); + lockdep_set_class(&di->mi.mi_sem, &dat_lock_key); + nilfs_palloc_setup_cache(dat, &di->palloc_cache); + nilfs_mdt_setup_shadow_map(dat, &di->shadow); + + err = nilfs_read_inode_common(dat, raw_inode); + if (err) + goto failed; + + unlock_new_inode(dat); + out: + *inodep = dat; + return 0; + failed: + iget_failed(dat); + return err; } diff --git a/fs/nilfs2/dat.h b/fs/nilfs2/dat.h index d31c3aab0ef..cbd8e973250 100644 --- a/fs/nilfs2/dat.h +++ b/fs/nilfs2/dat.h @@ -53,7 +53,7 @@ int nilfs_dat_freev(struct inode *, __u64 *, size_t); int nilfs_dat_move(struct inode *, __u64, sector_t); ssize_t nilfs_dat_get_vinfo(struct inode *, void *, unsigned, size_t); -int nilfs_dat_read(struct inode *dat, struct nilfs_inode *raw_inode); -struct inode *nilfs_dat_new(struct the_nilfs *nilfs, size_t entry_size); +int nilfs_dat_read(struct super_block *sb, size_t entry_size, + struct nilfs_inode *raw_inode, struct inode **inodep); #endif /* _NILFS_DAT_H */ diff --git a/fs/nilfs2/ifile.c b/fs/nilfs2/ifile.c index 922d9dd42c8..9f8a2da67f9 100644 --- a/fs/nilfs2/ifile.c +++ b/fs/nilfs2/ifile.c @@ -161,25 +161,46 @@ int nilfs_ifile_get_inode_block(struct inode *ifile, ino_t ino, } /** - * nilfs_ifile_new - create inode file - * @sbi: nilfs_sb_info struct + * nilfs_ifile_read - read or get ifile inode + * @sb: super block instance + * @root: root object * @inode_size: size of an inode + * @raw_inode: on-disk ifile inode + * @inodep: buffer to store the inode */ -struct inode *nilfs_ifile_new(struct nilfs_sb_info *sbi, size_t inode_size) +int nilfs_ifile_read(struct super_block *sb, struct nilfs_root *root, + size_t inode_size, struct nilfs_inode *raw_inode, + struct inode **inodep) { struct inode *ifile; int err; - ifile = nilfs_mdt_new(sbi->s_nilfs, sbi->s_super, NILFS_IFILE_INO, - sizeof(struct nilfs_ifile_info)); - if (ifile) { - err = nilfs_palloc_init_blockgroup(ifile, inode_size); - if (unlikely(err)) { - nilfs_mdt_destroy(ifile); - return NULL; - } - nilfs_palloc_setup_cache(ifile, - &NILFS_IFILE_I(ifile)->palloc_cache); - } - return ifile; + ifile = nilfs_iget_locked(sb, root, NILFS_IFILE_INO); + if (unlikely(!ifile)) + return -ENOMEM; + if (!(ifile->i_state & I_NEW)) + goto out; + + err = nilfs_mdt_init(ifile, NILFS_MDT_GFP, + sizeof(struct nilfs_ifile_info)); + if (err) + goto failed; + + err = nilfs_palloc_init_blockgroup(ifile, inode_size); + if (err) + goto failed; + + nilfs_palloc_setup_cache(ifile, &NILFS_IFILE_I(ifile)->palloc_cache); + + err = nilfs_read_inode_common(ifile, raw_inode); + if (err) + goto failed; + + unlock_new_inode(ifile); + out: + *inodep = ifile; + return 0; + failed: + iget_failed(ifile); + return err; } diff --git a/fs/nilfs2/ifile.h b/fs/nilfs2/ifile.h index cbca32e498f..59b6f2b51df 100644 --- a/fs/nilfs2/ifile.h +++ b/fs/nilfs2/ifile.h @@ -49,6 +49,8 @@ int nilfs_ifile_create_inode(struct inode *, ino_t *, struct buffer_head **); int nilfs_ifile_delete_inode(struct inode *, ino_t); int nilfs_ifile_get_inode_block(struct inode *, ino_t, struct buffer_head **); -struct inode *nilfs_ifile_new(struct nilfs_sb_info *sbi, size_t inode_size); +int nilfs_ifile_read(struct super_block *sb, struct nilfs_root *root, + size_t inode_size, struct nilfs_inode *raw_inode, + struct inode **inodep); #endif /* _NILFS_IFILE_H */ diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 5485dd12da6..5b3d43fb4e1 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -506,16 +506,23 @@ static int nilfs_iget_set(struct inode *inode, void *opaque) return 0; } -struct inode *nilfs_iget(struct super_block *sb, struct nilfs_root *root, - unsigned long ino) +struct inode *nilfs_iget_locked(struct super_block *sb, struct nilfs_root *root, + unsigned long ino) { struct nilfs_iget_args args = { .ino = ino, .root = root, .cno = 0, .for_gc = 0 }; + + return iget5_locked(sb, ino, nilfs_iget_test, nilfs_iget_set, &args); +} + +struct inode *nilfs_iget(struct super_block *sb, struct nilfs_root *root, + unsigned long ino) +{ struct inode *inode; int err; - inode = iget5_locked(sb, ino, nilfs_iget_test, nilfs_iget_set, &args); + inode = nilfs_iget_locked(sb, root, ino); if (unlikely(!inode)) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 3bbd340a513..44326cfe1fa 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -444,8 +444,7 @@ static const struct inode_operations def_mdt_iops; static const struct file_operations def_mdt_fops; -int nilfs_mdt_init(struct inode *inode, struct the_nilfs *nilfs, - gfp_t gfp_mask, size_t objsz) +int nilfs_mdt_init(struct inode *inode, gfp_t gfp_mask, size_t objsz) { struct nilfs_mdt_info *mi; @@ -453,13 +452,17 @@ int nilfs_mdt_init(struct inode *inode, struct the_nilfs *nilfs, if (!mi) return -ENOMEM; - mi->mi_nilfs = nilfs; + mi->mi_nilfs = NILFS_I_NILFS(inode); init_rwsem(&mi->mi_sem); inode->i_private = mi; inode->i_mode = S_IFREG; mapping_set_gfp_mask(inode->i_mapping, gfp_mask); - inode->i_mapping->backing_dev_info = nilfs->ns_bdi; + inode->i_mapping->backing_dev_info = inode->i_sb->s_bdi; + + inode->i_op = &def_mdt_iops; + inode->i_fop = &def_mdt_fops; + inode->i_mapping->a_ops = &def_mdt_aops; return 0; } @@ -544,13 +547,10 @@ struct inode *nilfs_mdt_new(struct the_nilfs *nilfs, struct super_block *sb, if (!inode) return NULL; - if (nilfs_mdt_init(inode, nilfs, NILFS_MDT_GFP, objsz) < 0) { + if (nilfs_mdt_init(inode, NILFS_MDT_GFP, objsz) < 0) { nilfs_destroy_inode(inode); return NULL; } - inode->i_op = &def_mdt_iops; - inode->i_fop = &def_mdt_fops; - inode->i_mapping->a_ops = &def_mdt_aops; return inode; } diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h index 1e0901c3fd6..73ff7c05571 100644 --- a/fs/nilfs2/mdt.h +++ b/fs/nilfs2/mdt.h @@ -85,8 +85,7 @@ int nilfs_mdt_forget_block(struct inode *, unsigned long); int nilfs_mdt_mark_block_dirty(struct inode *, unsigned long); int nilfs_mdt_fetch_dirty(struct inode *); -int nilfs_mdt_init(struct inode *inode, struct the_nilfs *nilfs, - gfp_t gfp_mask, size_t objsz); +int nilfs_mdt_init(struct inode *inode, gfp_t gfp_mask, size_t objsz); struct inode *nilfs_mdt_new(struct the_nilfs *, struct super_block *, ino_t, size_t); struct inode *nilfs_mdt_new_common(struct the_nilfs *, struct super_block *, diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index e9f457951e3..2ca2ca5ca84 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -244,6 +244,8 @@ extern int nilfs_get_block(struct inode *, sector_t, struct buffer_head *, int); extern void nilfs_set_inode_flags(struct inode *); extern int nilfs_read_inode_common(struct inode *, struct nilfs_inode *); extern void nilfs_write_inode_common(struct inode *, struct nilfs_inode *, int); +struct inode *nilfs_iget_locked(struct super_block *sb, struct nilfs_root *root, + unsigned long ino); struct inode *nilfs_iget(struct super_block *sb, struct nilfs_root *root, unsigned long ino); extern struct inode *nilfs_iget_for_gc(struct super_block *sb, diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index 3c6cc6005c2..599d9c27761 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c @@ -635,46 +635,55 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf, } /** - * nilfs_sufile_read - read sufile inode - * @sufile: sufile inode + * nilfs_sufile_read - read or get sufile inode + * @sb: super block instance + * @susize: size of a segment usage entry * @raw_inode: on-disk sufile inode + * @inodep: buffer to store the inode */ -int nilfs_sufile_read(struct inode *sufile, struct nilfs_inode *raw_inode) +int nilfs_sufile_read(struct super_block *sb, size_t susize, + struct nilfs_inode *raw_inode, struct inode **inodep) { - struct nilfs_sufile_info *sui = NILFS_SUI(sufile); + struct inode *sufile; + struct nilfs_sufile_info *sui; struct buffer_head *header_bh; struct nilfs_sufile_header *header; void *kaddr; - int ret; + int err; - ret = nilfs_read_inode_common(sufile, raw_inode); - if (ret < 0) - return ret; + sufile = nilfs_iget_locked(sb, NULL, NILFS_SUFILE_INO); + if (unlikely(!sufile)) + return -ENOMEM; + if (!(sufile->i_state & I_NEW)) + goto out; - ret = nilfs_sufile_get_header_block(sufile, &header_bh); - if (!ret) { - kaddr = kmap_atomic(header_bh->b_page, KM_USER0); - header = kaddr + bh_offset(header_bh); - sui->ncleansegs = le64_to_cpu(header->sh_ncleansegs); - kunmap_atomic(kaddr, KM_USER0); - brelse(header_bh); - } - return ret; -} + err = nilfs_mdt_init(sufile, NILFS_MDT_GFP, sizeof(*sui)); + if (err) + goto failed; -/** - * nilfs_sufile_new - create sufile - * @nilfs: nilfs object - * @susize: size of a segment usage entry - */ -struct inode *nilfs_sufile_new(struct the_nilfs *nilfs, size_t susize) -{ - struct inode *sufile; + nilfs_mdt_set_entry_size(sufile, susize, + sizeof(struct nilfs_sufile_header)); + + err = nilfs_read_inode_common(sufile, raw_inode); + if (err) + goto failed; + + err = nilfs_sufile_get_header_block(sufile, &header_bh); + if (err) + goto failed; - sufile = nilfs_mdt_new(nilfs, NULL, NILFS_SUFILE_INO, - sizeof(struct nilfs_sufile_info)); - if (sufile) - nilfs_mdt_set_entry_size(sufile, susize, - sizeof(struct nilfs_sufile_header)); - return sufile; + sui = NILFS_SUI(sufile); + kaddr = kmap_atomic(header_bh->b_page, KM_USER0); + header = kaddr + bh_offset(header_bh); + sui->ncleansegs = le64_to_cpu(header->sh_ncleansegs); + kunmap_atomic(kaddr, KM_USER0); + brelse(header_bh); + + unlock_new_inode(sufile); + out: + *inodep = sufile; + return 0; + failed: + iget_failed(sufile); + return err; } diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h index 15163b8aff7..203f6102a62 100644 --- a/fs/nilfs2/sufile.h +++ b/fs/nilfs2/sufile.h @@ -61,8 +61,8 @@ void nilfs_sufile_do_cancel_free(struct inode *, __u64, struct buffer_head *, void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *, struct buffer_head *); -int nilfs_sufile_read(struct inode *sufile, struct nilfs_inode *raw_inode); -struct inode *nilfs_sufile_new(struct the_nilfs *nilfs, size_t susize); +int nilfs_sufile_read(struct super_block *sb, size_t susize, + struct nilfs_inode *raw_inode, struct inode **inodep); /** * nilfs_sufile_scrap - make a segment garbage diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 5893cb27c90..39e7d7f8eda 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -356,6 +356,10 @@ static void nilfs_put_super(struct super_block *sb) up_write(&nilfs->ns_sem); } + iput(nilfs->ns_sufile); + iput(nilfs->ns_cpfile); + iput(nilfs->ns_dat); + destroy_nilfs(nilfs); sbi->s_super = NULL; sb->s_fs_info = NULL; @@ -403,10 +407,6 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, if (root->ifile) goto reuse; /* already attached checkpoint */ - root->ifile = nilfs_ifile_new(sbi, nilfs->ns_inode_size); - if (!root->ifile) - goto failed; - down_read(&nilfs->ns_segctor_sem); err = nilfs_cpfile_get_checkpoint(nilfs->ns_cpfile, cno, 0, &raw_cp, &bh_cp); @@ -421,8 +421,10 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, } goto failed; } - err = nilfs_read_inode_common(root->ifile, &raw_cp->cp_ifile_inode); - if (unlikely(err)) + + err = nilfs_ifile_read(sbi->s_super, root, nilfs->ns_inode_size, + &raw_cp->cp_ifile_inode, &root->ifile); + if (err) goto failed_bh; atomic_set(&root->inodes_count, le64_to_cpu(raw_cp->cp_inodes_count)); @@ -895,7 +897,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent) if (err) { printk(KERN_ERR "NILFS: error loading last checkpoint " "(checkpoint number=%llu).\n", (unsigned long long)cno); - goto failed_nilfs; + goto failed_unload; } if (!(sb->s_flags & MS_RDONLY)) { @@ -924,6 +926,11 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent) failed_checkpoint: nilfs_put_root(fsroot); + failed_unload: + iput(nilfs->ns_sufile); + iput(nilfs->ns_cpfile); + iput(nilfs->ns_dat); + failed_nilfs: destroy_nilfs(nilfs); diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index b7666bc0425..4d6763e28eb 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -92,11 +92,6 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev) void destroy_nilfs(struct the_nilfs *nilfs) { might_sleep(); - if (nilfs_loaded(nilfs)) { - nilfs_mdt_destroy(nilfs->ns_sufile); - nilfs_mdt_destroy(nilfs->ns_cpfile); - nilfs_mdt_destroy(nilfs->ns_dat); - } if (nilfs_init(nilfs)) { brelse(nilfs->ns_sbh[0]); brelse(nilfs->ns_sbh[1]); @@ -104,11 +99,13 @@ void destroy_nilfs(struct the_nilfs *nilfs) kfree(nilfs); } -static int nilfs_load_super_root(struct the_nilfs *nilfs, sector_t sr_block) +static int nilfs_load_super_root(struct the_nilfs *nilfs, + struct super_block *sb, sector_t sr_block) { struct buffer_head *bh_sr; struct nilfs_super_root *raw_sr; struct nilfs_super_block **sbp = nilfs->ns_sbp; + struct nilfs_inode *rawi; unsigned dat_entry_size, segment_usage_size, checkpoint_size; unsigned inode_size; int err; @@ -125,34 +122,22 @@ static int nilfs_load_super_root(struct the_nilfs *nilfs, sector_t sr_block) inode_size = nilfs->ns_inode_size; - err = -ENOMEM; - nilfs->ns_dat = nilfs_dat_new(nilfs, dat_entry_size); - if (unlikely(!nilfs->ns_dat)) + rawi = (void *)bh_sr->b_data + NILFS_SR_DAT_OFFSET(inode_size); + err = nilfs_dat_read(sb, dat_entry_size, rawi, &nilfs->ns_dat); + if (err) goto failed; - nilfs->ns_cpfile = nilfs_cpfile_new(nilfs, checkpoint_size); - if (unlikely(!nilfs->ns_cpfile)) + rawi = (void *)bh_sr->b_data + NILFS_SR_CPFILE_OFFSET(inode_size); + err = nilfs_cpfile_read(sb, checkpoint_size, rawi, &nilfs->ns_cpfile); + if (err) goto failed_dat; - nilfs->ns_sufile = nilfs_sufile_new(nilfs, segment_usage_size); - if (unlikely(!nilfs->ns_sufile)) + rawi = (void *)bh_sr->b_data + NILFS_SR_SUFILE_OFFSET(inode_size); + err = nilfs_sufile_read(sb, segment_usage_size, rawi, + &nilfs->ns_sufile); + if (err) goto failed_cpfile; - err = nilfs_dat_read(nilfs->ns_dat, (void *)bh_sr->b_data + - NILFS_SR_DAT_OFFSET(inode_size)); - if (unlikely(err)) - goto failed_sufile; - - err = nilfs_cpfile_read(nilfs->ns_cpfile, (void *)bh_sr->b_data + - NILFS_SR_CPFILE_OFFSET(inode_size)); - if (unlikely(err)) - goto failed_sufile; - - err = nilfs_sufile_read(nilfs->ns_sufile, (void *)bh_sr->b_data + - NILFS_SR_SUFILE_OFFSET(inode_size)); - if (unlikely(err)) - goto failed_sufile; - raw_sr = (struct nilfs_super_root *)bh_sr->b_data; nilfs->ns_nongc_ctime = le64_to_cpu(raw_sr->sr_nongc_ctime); @@ -160,14 +145,11 @@ static int nilfs_load_super_root(struct the_nilfs *nilfs, sector_t sr_block) brelse(bh_sr); return err; - failed_sufile: - nilfs_mdt_destroy(nilfs->ns_sufile); - failed_cpfile: - nilfs_mdt_destroy(nilfs->ns_cpfile); + iput(nilfs->ns_cpfile); failed_dat: - nilfs_mdt_destroy(nilfs->ns_dat); + iput(nilfs->ns_dat); goto failed; } @@ -290,7 +272,7 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) goto scan_error; } - err = nilfs_load_super_root(nilfs, ri.ri_super_root); + err = nilfs_load_super_root(nilfs, sbi->s_super, ri.ri_super_root); if (unlikely(err)) { printk(KERN_ERR "NILFS: error loading super root.\n"); goto failed; @@ -358,9 +340,9 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) goto failed; failed_unload: - nilfs_mdt_destroy(nilfs->ns_cpfile); - nilfs_mdt_destroy(nilfs->ns_sufile); - nilfs_mdt_destroy(nilfs->ns_dat); + iput(nilfs->ns_cpfile); + iput(nilfs->ns_sufile); + iput(nilfs->ns_dat); failed: nilfs_clear_recovery_info(&ri); @@ -782,7 +764,7 @@ void nilfs_put_root(struct nilfs_root *root) rb_erase(&root->rb_node, &nilfs->ns_cptree); spin_unlock(&nilfs->ns_cptree_lock); if (root->ifile) - nilfs_mdt_destroy(root->ifile); + iput(root->ifile); kfree(root); } -- cgit v1.2.3-18-g5258 From 032dbb3b503a30fce732ec4c05525d0abed1f1d6 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 13 Sep 2010 11:16:34 +0900 Subject: nilfs2: see state of root dentry for mount check of snapshots After applied the patch that unified sb instances, root dentry of snapshots can be left in dcache even after their trees are unmounted. The orphan root dentry/inode keeps a root object, and this causes false positive of nilfs_checkpoint_is_mounted function. This resolves the issue by having nilfs_checkpoint_is_mounted test whether the root dentry is busy or not. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/cpfile.c | 23 ++++++++--------------- fs/nilfs2/inode.c | 10 ++++++++++ fs/nilfs2/nilfs.h | 3 +++ fs/nilfs2/super.c | 32 ++++++++++++++++++++++++++++++++ fs/nilfs2/the_nilfs.c | 21 --------------------- fs/nilfs2/the_nilfs.h | 1 - 6 files changed, 53 insertions(+), 37 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c index 03de1da8795..5ff15a8a102 100644 --- a/fs/nilfs2/cpfile.c +++ b/fs/nilfs2/cpfile.c @@ -863,26 +863,19 @@ int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno) */ int nilfs_cpfile_change_cpmode(struct inode *cpfile, __u64 cno, int mode) { - struct the_nilfs *nilfs; int ret; - nilfs = NILFS_MDT(cpfile)->mi_nilfs; - switch (mode) { case NILFS_CHECKPOINT: - /* - * Check for protecting existing snapshot mounts: - * ns_mount_mutex is used to make this operation atomic and - * exclusive with a new mount job. Though it doesn't cover - * umount, it's enough for the purpose. - */ - if (nilfs_checkpoint_is_mounted(nilfs, cno, 1)) { - /* Current implementation does not have to protect - plain read-only mounts since they are exclusive - with a read/write mount and are protected from the - cleaner. */ + if (nilfs_checkpoint_is_mounted(cpfile->i_sb, cno)) + /* + * Current implementation does not have to protect + * plain read-only mounts since they are exclusive + * with a read/write mount and are protected from the + * cleaner. + */ ret = -EBUSY; - } else + else ret = nilfs_cpfile_clear_snapshot(cpfile, cno); return ret; case NILFS_SNAPSHOT: diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 5b3d43fb4e1..71d4bc8464e 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -506,6 +506,16 @@ static int nilfs_iget_set(struct inode *inode, void *opaque) return 0; } +struct inode *nilfs_ilookup(struct super_block *sb, struct nilfs_root *root, + unsigned long ino) +{ + struct nilfs_iget_args args = { + .ino = ino, .root = root, .cno = 0, .for_gc = 0 + }; + + return ilookup5(sb, ino, nilfs_iget_test, &args); +} + struct inode *nilfs_iget_locked(struct super_block *sb, struct nilfs_root *root, unsigned long ino) { diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 2ca2ca5ca84..f6e276eaaf6 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -244,6 +244,8 @@ extern int nilfs_get_block(struct inode *, sector_t, struct buffer_head *, int); extern void nilfs_set_inode_flags(struct inode *); extern int nilfs_read_inode_common(struct inode *, struct nilfs_inode *); extern void nilfs_write_inode_common(struct inode *, struct nilfs_inode *, int); +struct inode *nilfs_ilookup(struct super_block *sb, struct nilfs_root *root, + unsigned long ino); struct inode *nilfs_iget_locked(struct super_block *sb, struct nilfs_root *root, unsigned long ino); struct inode *nilfs_iget(struct super_block *sb, struct nilfs_root *root, @@ -285,6 +287,7 @@ extern int nilfs_commit_super(struct nilfs_sb_info *, int); extern int nilfs_cleanup_super(struct nilfs_sb_info *); int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, struct nilfs_root **root); +int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno); /* gcinode.c */ int nilfs_gccache_submit_read_data(struct inode *, sector_t, sector_t, __u64, diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 39e7d7f8eda..ab96d26bf7e 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -833,6 +833,38 @@ static int nilfs_try_to_shrink_tree(struct dentry *root_dentry) return nilfs_tree_was_touched(root_dentry); } +int nilfs_checkpoint_is_mounted(struct super_block *sb, __u64 cno) +{ + struct the_nilfs *nilfs = NILFS_SB(sb)->s_nilfs; + struct nilfs_root *root; + struct inode *inode; + struct dentry *dentry; + int ret; + + if (cno < 0 || cno > nilfs->ns_cno) + return false; + + if (cno >= nilfs_last_cno(nilfs)) + return true; /* protect recent checkpoints */ + + ret = false; + root = nilfs_lookup_root(NILFS_SB(sb)->s_nilfs, cno); + if (root) { + inode = nilfs_ilookup(sb, root, NILFS_ROOT_INO); + if (inode) { + dentry = d_find_alias(inode); + if (dentry) { + if (nilfs_tree_was_touched(dentry)) + ret = nilfs_try_to_shrink_tree(dentry); + dput(dentry); + } + iput(inode); + } + nilfs_put_root(root); + } + return ret; +} + /** * nilfs_fill_super() - initialize a super block instance * @sb: super_block diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 4d6763e28eb..4cc705a1d13 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -769,24 +769,3 @@ void nilfs_put_root(struct nilfs_root *root) kfree(root); } } - -int nilfs_checkpoint_is_mounted(struct the_nilfs *nilfs, __u64 cno, - int snapshot_mount) -{ - struct nilfs_root *root; - int ret; - - if (cno < 0 || cno > nilfs->ns_cno) - return false; - - if (cno >= nilfs_last_cno(nilfs)) - return true; /* protect recent checkpoints */ - - ret = false; - root = nilfs_lookup_root(nilfs, cno); - if (root) { - ret = true; - nilfs_put_root(root); - } - return ret; -} diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index a5178dc43df..cae56f338b6 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -242,7 +242,6 @@ struct nilfs_root *nilfs_find_or_create_root(struct the_nilfs *nilfs, __u64 cno); void nilfs_put_root(struct nilfs_root *root); struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *, int, __u64); -int nilfs_checkpoint_is_mounted(struct the_nilfs *, __u64, int); int nilfs_near_disk_full(struct the_nilfs *); void nilfs_fall_back_super_block(struct the_nilfs *); void nilfs_swap_super_block(struct the_nilfs *); -- cgit v1.2.3-18-g5258 From c6e071884aca360a14c21757d760e76ec34b4894 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 5 Sep 2010 00:23:50 +0900 Subject: nilfs2: get rid of mi_nilfs back pointer to nilfs object This removes a back pointer to nilfs object from nilfs_mdt_info structure that is attached to metadata files. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/mdt.c | 9 +++------ fs/nilfs2/mdt.h | 8 ++------ fs/nilfs2/sufile.c | 4 ++-- fs/nilfs2/sufile.h | 2 +- 4 files changed, 8 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 44326cfe1fa..32695f3c4b9 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -78,7 +78,7 @@ static int nilfs_mdt_create_block(struct inode *inode, unsigned long block, struct buffer_head *, void *)) { - struct the_nilfs *nilfs = NILFS_MDT(inode)->mi_nilfs; + struct the_nilfs *nilfs = NILFS_I_NILFS(inode); struct super_block *sb = inode->i_sb; struct nilfs_transaction_info ti; struct buffer_head *bh; @@ -167,9 +167,7 @@ nilfs_mdt_submit_block(struct inode *inode, unsigned long blkoff, unlock_buffer(bh); goto failed_bh; } - bh->b_bdev = NILFS_MDT(inode)->mi_nilfs->ns_bdev; - bh->b_blocknr = (sector_t)blknum; - set_buffer_mapped(bh); + map_bh(bh, inode->i_sb, (sector_t)blknum); bh->b_end_io = end_buffer_read_sync; get_bh(bh); @@ -412,7 +410,7 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc) return 0; sb = inode->i_sb; - nilfs = NILFS_MDT(inode)->mi_nilfs; + nilfs = NILFS_SB(sb)->s_nilfs; if (!sb) { down_read(&nilfs->ns_writer_sem); @@ -452,7 +450,6 @@ int nilfs_mdt_init(struct inode *inode, gfp_t gfp_mask, size_t objsz) if (!mi) return -ENOMEM; - mi->mi_nilfs = NILFS_I_NILFS(inode); init_rwsem(&mi->mi_sem); inode->i_private = mi; diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h index 73ff7c05571..76356d2899e 100644 --- a/fs/nilfs2/mdt.h +++ b/fs/nilfs2/mdt.h @@ -37,7 +37,6 @@ struct nilfs_shadow_map { /** * struct nilfs_mdt_info - on-memory private data of meta data files - * @mi_nilfs: back pointer to the_nilfs struct * @mi_sem: reader/writer semaphore for meta data operations * @mi_bgl: per-blockgroup locking * @mi_entry_size: size of an entry @@ -49,7 +48,6 @@ struct nilfs_shadow_map { * @mi_blocks_per_desc_block: number of blocks per descriptor block */ struct nilfs_mdt_info { - struct the_nilfs *mi_nilfs; struct rw_semaphore mi_sem; struct blockgroup_lock *mi_bgl; unsigned mi_entry_size; @@ -68,9 +66,7 @@ static inline struct nilfs_mdt_info *NILFS_MDT(const struct inode *inode) static inline struct the_nilfs *NILFS_I_NILFS(struct inode *inode) { - struct super_block *sb = inode->i_sb; - - return sb ? NILFS_SB(sb)->s_nilfs : NILFS_MDT(inode)->mi_nilfs; + return NILFS_SB(inode->i_sb)->s_nilfs; } /* Default GFP flags using highmem */ @@ -117,7 +113,7 @@ static inline void nilfs_mdt_clear_dirty(struct inode *inode) static inline __u64 nilfs_mdt_cno(struct inode *inode) { - return NILFS_MDT(inode)->mi_nilfs->ns_cno; + return NILFS_I_NILFS(inode)->ns_cno; } #define nilfs_mdt_bgl_lock(inode, bg) \ diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index 599d9c27761..1d6f488ccae 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c @@ -505,7 +505,7 @@ int nilfs_sufile_get_stat(struct inode *sufile, struct nilfs_sustat *sustat) { struct buffer_head *header_bh; struct nilfs_sufile_header *header; - struct the_nilfs *nilfs = NILFS_MDT(sufile)->mi_nilfs; + struct the_nilfs *nilfs = NILFS_I_NILFS(sufile); void *kaddr; int ret; @@ -583,7 +583,7 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf, struct nilfs_segment_usage *su; struct nilfs_suinfo *si = buf; size_t susz = NILFS_MDT(sufile)->mi_entry_size; - struct the_nilfs *nilfs = NILFS_MDT(sufile)->mi_nilfs; + struct the_nilfs *nilfs = NILFS_I_NILFS(sufile); void *kaddr; unsigned long nsegs, segusages_per_block; ssize_t n; diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h index 203f6102a62..a943fbacb45 100644 --- a/fs/nilfs2/sufile.h +++ b/fs/nilfs2/sufile.h @@ -31,7 +31,7 @@ static inline unsigned long nilfs_sufile_get_nsegments(struct inode *sufile) { - return NILFS_MDT(sufile)->mi_nilfs->ns_nsegments; + return NILFS_I_NILFS(sufile)->ns_nsegments; } unsigned long nilfs_sufile_get_ncleansegs(struct inode *sufile); -- cgit v1.2.3-18-g5258 From 090fd5b10165033d7c30afde0a7e59141d820602 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 5 Sep 2010 16:17:35 +0900 Subject: nilfs2: get rid of back pointer to writable sb instance Nilfs object holds a back pointer to a writable super block instance in nilfs->ns_writer, and this became eliminable since sb is now made per device and all inodes have a valid pointer to it. This deletes the ns_writer pointer and a reader/writer semaphore protecting it. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/mdt.c | 33 ++------------------------------- fs/nilfs2/recovery.c | 4 ---- fs/nilfs2/segment.c | 4 ---- fs/nilfs2/the_nilfs.c | 1 - fs/nilfs2/the_nilfs.h | 21 --------------------- 5 files changed, 2 insertions(+), 61 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 32695f3c4b9..0a2ccfc0d6f 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -78,25 +78,11 @@ static int nilfs_mdt_create_block(struct inode *inode, unsigned long block, struct buffer_head *, void *)) { - struct the_nilfs *nilfs = NILFS_I_NILFS(inode); struct super_block *sb = inode->i_sb; struct nilfs_transaction_info ti; struct buffer_head *bh; int err; - if (!sb) { - /* - * Make sure this function is not called from any - * read-only context. - */ - if (!nilfs->ns_writer) { - WARN_ON(1); - err = -EROFS; - goto out; - } - sb = nilfs->ns_writer->s_super; - } - nilfs_transaction_begin(sb, &ti, 0); err = -ENOMEM; @@ -112,7 +98,7 @@ static int nilfs_mdt_create_block(struct inode *inode, unsigned long block, if (buffer_uptodate(bh)) goto failed_bh; - bh->b_bdev = nilfs->ns_bdev; + bh->b_bdev = sb->s_bdev; err = nilfs_mdt_insert_new_block(inode, block, bh, init_block); if (likely(!err)) { get_bh(bh); @@ -129,7 +115,7 @@ static int nilfs_mdt_create_block(struct inode *inode, unsigned long block, err = nilfs_transaction_commit(sb); else nilfs_transaction_abort(sb); - out: + return err; } @@ -398,8 +384,6 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc) { struct inode *inode; struct super_block *sb; - struct the_nilfs *nilfs; - struct nilfs_sb_info *writer = NULL; int err = 0; redirty_page_for_writepage(wbc, page); @@ -410,25 +394,12 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc) return 0; sb = inode->i_sb; - nilfs = NILFS_SB(sb)->s_nilfs; - - if (!sb) { - down_read(&nilfs->ns_writer_sem); - writer = nilfs->ns_writer; - if (!writer) { - up_read(&nilfs->ns_writer_sem); - return -EROFS; - } - sb = writer->s_super; - } if (wbc->sync_mode == WB_SYNC_ALL) err = nilfs_construct_segment(sb); else if (wbc->for_reclaim) nilfs_flush_segment(sb, inode->i_ino); - if (writer) - up_read(&nilfs->ns_writer_sem); return err; } diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index dcb5a9812c6..5d2711c28da 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -440,7 +440,6 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, segnum[2] = ri->ri_segnum; segnum[3] = ri->ri_nextnum; - nilfs_attach_writer(nilfs, sbi); /* * Releasing the next segment of the latest super root. * The next segment is invalidated by this recovery. @@ -480,7 +479,6 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, failed: /* No need to recover sufile because it will be destroyed on error */ - nilfs_detach_writer(nilfs, sbi); return err; } @@ -599,7 +597,6 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, }; int state = RF_INIT_ST; - nilfs_attach_writer(nilfs, sbi); pseg_start = ri->ri_lsegs_start; seg_seq = ri->ri_lsegs_start_seq; segnum = nilfs_get_segnum_of_block(nilfs, pseg_start); @@ -690,7 +687,6 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, out: brelse(bh_sum); dispose_recovery_list(&dsync_blocks); - nilfs_detach_writer(nilfs, sbi); return err; confused: diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index b0c5e08d06c..56350bf1f79 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2799,7 +2799,6 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci) int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi, struct nilfs_root *root) { - struct the_nilfs *nilfs = sbi->s_nilfs; int err; if (NILFS_SC(sbi)) { @@ -2815,10 +2814,8 @@ int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi, if (!sbi->s_sc_info) return -ENOMEM; - nilfs_attach_writer(nilfs, sbi); err = nilfs_segctor_start_thread(NILFS_SC(sbi)); if (err) { - nilfs_detach_writer(nilfs, sbi); kfree(sbi->s_sc_info); sbi->s_sc_info = NULL; } @@ -2855,5 +2852,4 @@ void nilfs_detach_segment_constructor(struct nilfs_sb_info *sbi) up_write(&nilfs->ns_segctor_sem); nilfs_dispose_list(sbi, &garbage_list, 1); - nilfs_detach_writer(nilfs, sbi); } diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 4cc705a1d13..a94aa57c4bd 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -75,7 +75,6 @@ struct the_nilfs *alloc_nilfs(struct block_device *bdev) nilfs->ns_bdev = bdev; atomic_set(&nilfs->ns_ndirtyblks, 0); init_rwsem(&nilfs->ns_sem); - init_rwsem(&nilfs->ns_writer_sem); INIT_LIST_HEAD(&nilfs->ns_gc_inodes); spin_lock_init(&nilfs->ns_last_segment_lock); nilfs->ns_cptree = RB_ROOT; diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index cae56f338b6..bbbc1c748aa 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -48,9 +48,7 @@ enum { * @ns_flags: flags * @ns_bdev: block device * @ns_bdi: backing dev info - * @ns_writer: back pointer to writable nilfs_sb_info * @ns_sem: semaphore for shared states - * @ns_writer_sem: semaphore protecting ns_writer attach/detach * @ns_sbh: buffer heads of on-disk super blocks * @ns_sbp: pointers to super block data * @ns_sbwtime: previous write time of super block @@ -93,9 +91,7 @@ struct the_nilfs { struct block_device *ns_bdev; struct backing_dev_info *ns_bdi; - struct nilfs_sb_info *ns_writer; struct rw_semaphore ns_sem; - struct rw_semaphore ns_writer_sem; /* * used for @@ -252,23 +248,6 @@ static inline void nilfs_get_root(struct nilfs_root *root) atomic_inc(&root->count); } -static inline void -nilfs_attach_writer(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) -{ - down_write(&nilfs->ns_writer_sem); - nilfs->ns_writer = sbi; - up_write(&nilfs->ns_writer_sem); -} - -static inline void -nilfs_detach_writer(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) -{ - down_write(&nilfs->ns_writer_sem); - if (sbi == nilfs->ns_writer) - nilfs->ns_writer = NULL; - up_write(&nilfs->ns_writer_sem); -} - static inline int nilfs_valid_fs(struct the_nilfs *nilfs) { unsigned valid_fs; -- cgit v1.2.3-18-g5258 From 2879ed66e4c6da1dfc6bb0bd04566b61824f9256 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 5 Sep 2010 13:35:53 +0900 Subject: nilfs2: remove own inode allocator and destructor for metadata files This finally removes own inode allocator and destructor functions for metadata files. Several routines, nilfs_mdt_new(), nilfs_mdt_new_common(), nilfs_mdt_clear(), nilfs_mdt_destroy(), and nilfs_alloc_inode_common() will be gone. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/mdt.c | 111 ------------------------------------------------------ fs/nilfs2/mdt.h | 5 --- fs/nilfs2/nilfs.h | 1 - fs/nilfs2/super.c | 9 +---- 4 files changed, 2 insertions(+), 124 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 0a2ccfc0d6f..d60fdb097d5 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -36,7 +36,6 @@ #define NILFS_MDT_MAX_RA_BLOCKS (16 - 1) -#define INIT_UNUSED_INODE_FIELDS static int nilfs_mdt_insert_new_block(struct inode *inode, unsigned long block, @@ -435,93 +434,6 @@ int nilfs_mdt_init(struct inode *inode, gfp_t gfp_mask, size_t objsz) return 0; } -/* - * NILFS2 uses pseudo inodes for meta data files such as DAT, cpfile, sufile, - * ifile, or gcinodes. This allows the B-tree code and segment constructor - * to treat them like regular files, and this helps to simplify the - * implementation. - * On the other hand, some of the pseudo inodes have an irregular point: - * They don't have valid inode->i_sb pointer because their lifetimes are - * longer than those of the super block structs; they may continue for - * several consecutive mounts/umounts. This would need discussions. - */ -/** - * nilfs_mdt_new_common - allocate a pseudo inode for metadata file - * @nilfs: nilfs object - * @sb: super block instance the metadata file belongs to - * @ino: inode number - */ -struct inode * -nilfs_mdt_new_common(struct the_nilfs *nilfs, struct super_block *sb, - ino_t ino) -{ - struct inode *inode = nilfs_alloc_inode_common(nilfs); - - if (!inode) - return NULL; - else { - struct address_space * const mapping = &inode->i_data; - - inode->i_sb = sb; /* sb may be NULL for some meta data files */ - inode->i_blkbits = nilfs->ns_blocksize_bits; - inode->i_flags = 0; - atomic_set(&inode->i_count, 1); - inode->i_nlink = 1; - inode->i_ino = ino; - -#ifdef INIT_UNUSED_INODE_FIELDS - atomic_set(&inode->i_writecount, 0); - inode->i_size = 0; - inode->i_blocks = 0; - inode->i_bytes = 0; - inode->i_generation = 0; -#ifdef CONFIG_QUOTA - memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); -#endif - inode->i_pipe = NULL; - inode->i_bdev = NULL; - inode->i_cdev = NULL; - inode->i_rdev = 0; -#ifdef CONFIG_SECURITY - inode->i_security = NULL; -#endif - inode->dirtied_when = 0; - - INIT_LIST_HEAD(&inode->i_list); - INIT_LIST_HEAD(&inode->i_sb_list); - inode->i_state = 0; -#endif - - spin_lock_init(&inode->i_lock); - mutex_init(&inode->i_mutex); - init_rwsem(&inode->i_alloc_sem); - - mapping->host = NULL; /* instead of inode */ - mapping->flags = 0; - mapping->assoc_mapping = NULL; - - inode->i_mapping = mapping; - } - - return inode; -} - -struct inode *nilfs_mdt_new(struct the_nilfs *nilfs, struct super_block *sb, - ino_t ino, size_t objsz) -{ - struct inode *inode; - - inode = nilfs_mdt_new_common(nilfs, sb, ino); - if (!inode) - return NULL; - - if (nilfs_mdt_init(inode, NILFS_MDT_GFP, objsz) < 0) { - nilfs_destroy_inode(inode); - return NULL; - } - return inode; -} - void nilfs_mdt_set_entry_size(struct inode *inode, unsigned entry_size, unsigned header_size) { @@ -688,26 +600,3 @@ void nilfs_mdt_clear_shadow_map(struct inode *inode) truncate_inode_pages(&shadow->frozen_btnodes, 0); up_write(&mi->mi_sem); } - -static void nilfs_mdt_clear(struct inode *inode) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - - invalidate_mapping_pages(inode->i_mapping, 0, -1); - truncate_inode_pages(inode->i_mapping, 0); - - if (test_bit(NILFS_I_BMAP, &ii->i_state)) - nilfs_bmap_clear(ii->i_bmap); - nilfs_btnode_cache_clear(&ii->i_btnode_cache); -} - -void nilfs_mdt_destroy(struct inode *inode) -{ - struct nilfs_mdt_info *mdi = NILFS_MDT(inode); - - if (mdi->mi_palloc_cache) - nilfs_palloc_destroy_cache(inode); - nilfs_mdt_clear(inode); - - nilfs_destroy_inode(inode); -} diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h index 76356d2899e..b13734bf352 100644 --- a/fs/nilfs2/mdt.h +++ b/fs/nilfs2/mdt.h @@ -82,11 +82,6 @@ int nilfs_mdt_mark_block_dirty(struct inode *, unsigned long); int nilfs_mdt_fetch_dirty(struct inode *); int nilfs_mdt_init(struct inode *inode, gfp_t gfp_mask, size_t objsz); -struct inode *nilfs_mdt_new(struct the_nilfs *, struct super_block *, ino_t, - size_t); -struct inode *nilfs_mdt_new_common(struct the_nilfs *, struct super_block *, - ino_t); -void nilfs_mdt_destroy(struct inode *); void nilfs_mdt_set_entry_size(struct inode *, unsigned, unsigned); int nilfs_mdt_setup_shadow_map(struct inode *inode, diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index f6e276eaaf6..f7560da5a56 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -266,7 +266,6 @@ extern int nilfs_mark_inode_dirty(struct inode *); extern void nilfs_dirty_inode(struct inode *); /* super.c */ -extern struct inode *nilfs_alloc_inode_common(struct the_nilfs *); extern struct inode *nilfs_alloc_inode(struct super_block *); extern void nilfs_destroy_inode(struct inode *); extern void nilfs_error(struct super_block *, const char *, const char *, ...) diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index ab96d26bf7e..d92ebd5d60d 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -146,7 +146,7 @@ void nilfs_warning(struct super_block *sb, const char *function, } -struct inode *nilfs_alloc_inode_common(struct the_nilfs *nilfs) +struct inode *nilfs_alloc_inode(struct super_block *sb) { struct nilfs_inode_info *ii; @@ -157,15 +157,10 @@ struct inode *nilfs_alloc_inode_common(struct the_nilfs *nilfs) ii->i_state = 0; ii->i_cno = 0; ii->vfs_inode.i_version = 1; - nilfs_btnode_cache_init(&ii->i_btnode_cache, nilfs->ns_bdi); + nilfs_btnode_cache_init(&ii->i_btnode_cache, sb->s_bdi); return &ii->vfs_inode; } -struct inode *nilfs_alloc_inode(struct super_block *sb) -{ - return nilfs_alloc_inode_common(NILFS_SB(sb)->s_nilfs); -} - void nilfs_destroy_inode(struct inode *inode) { struct nilfs_mdt_info *mdi = NILFS_MDT(inode); -- cgit v1.2.3-18-g5258 From c05dbfc2609993ccc067879579e2a7726e12b3f1 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 16 Sep 2010 00:36:24 +0900 Subject: nilfs2: accept 64-bit checkpoint numbers in cp mount option The current implementation doesn't mount snapshots with checkpoint numbers larger than INT_MAX since it uses match_int() for parsing "cp=" mount option. This uses simple_strtoull() for the conversion to resolve the issue. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/super.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index d92ebd5d60d..a1cd444103f 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -556,7 +556,6 @@ static int parse_options(char *options, struct super_block *sb, int is_remount) struct nilfs_sb_info *sbi = NILFS_SB(sb); char *p; substring_t args[MAX_OPT_ARGS]; - int option; if (!options) return 1; @@ -594,8 +593,6 @@ static int parse_options(char *options, struct super_block *sb, int is_remount) nilfs_write_opt(sbi, ERROR_MODE, ERRORS_CONT); break; case Opt_snapshot: - if (match_int(&args[0], &option) || option <= 0) - return 0; if (is_remount) { printk(KERN_ERR "NILFS: \"%s\" option is invalid " @@ -1065,7 +1062,7 @@ static int nilfs_identify(char *data, struct nilfs_super_data *sd) { char *p, *options = data; substring_t args[MAX_OPT_ARGS]; - int option, token; + int token; int ret = 0; do { @@ -1073,16 +1070,18 @@ static int nilfs_identify(char *data, struct nilfs_super_data *sd) if (p != NULL && *p) { token = match_token(p, tokens, args); if (token == Opt_snapshot) { - if (!(sd->flags & MS_RDONLY)) + if (!(sd->flags & MS_RDONLY)) { ret++; - else { - ret = match_int(&args[0], &option); - if (!ret) { - if (option > 0) - sd->cno = option; - else - ret++; - } + } else { + sd->cno = simple_strtoull(args[0].from, + NULL, 0); + /* + * No need to see the end pointer; + * match_token() has done syntax + * checking. + */ + if (sd->cno == 0) + ret++; } } if (ret) -- cgit v1.2.3-18-g5258 From 5beb6e0b2008386571fd342d0a4a14f5c8c0baf8 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 20 Sep 2010 18:19:06 +0900 Subject: nilfs2: add bdev freeze/thaw support Nilfs hasn't supported the freeze/thaw feature because it didn't work due to the peculiar design that multiple super block instances could be allocated for a device. This limitation was removed by the patch "nilfs2: do not allocate multiple super block instances for a device". So now this adds the freeze/thaw support to nilfs. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/ioctl.c | 2 ++ fs/nilfs2/segment.c | 2 ++ fs/nilfs2/super.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 58 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index 338858fc907..3e90f86d5bf 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c @@ -580,6 +580,8 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, goto out_free; } + vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); + ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]); if (ret < 0) printk(KERN_ERR "NILFS: GC failed during preparation: " diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 56350bf1f79..172ad425749 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -191,6 +191,8 @@ int nilfs_transaction_begin(struct super_block *sb, if (ret > 0) return 0; + vfs_check_frozen(sb, SB_FREEZE_WRITE); + sbi = NILFS_SB(sb); nilfs = sbi->s_nilfs; down_read(&nilfs->ns_segctor_sem); diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index a1cd444103f..92e8c769584 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -73,6 +73,7 @@ struct kmem_cache *nilfs_transaction_cachep; struct kmem_cache *nilfs_segbuf_cachep; struct kmem_cache *nilfs_btree_path_cache; +static int nilfs_setup_super(struct nilfs_sb_info *sbi, int is_mount); static int nilfs_remount(struct super_block *sb, int *flags, char *data); static void nilfs_set_error(struct nilfs_sb_info *sbi) @@ -439,6 +440,36 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno, int curr_mnt, return err; } +static int nilfs_freeze(struct super_block *sb) +{ + struct nilfs_sb_info *sbi = NILFS_SB(sb); + struct the_nilfs *nilfs = sbi->s_nilfs; + int err; + + if (sb->s_flags & MS_RDONLY) + return 0; + + /* Mark super block clean */ + down_write(&nilfs->ns_sem); + err = nilfs_cleanup_super(sbi); + up_write(&nilfs->ns_sem); + return err; +} + +static int nilfs_unfreeze(struct super_block *sb) +{ + struct nilfs_sb_info *sbi = NILFS_SB(sb); + struct the_nilfs *nilfs = sbi->s_nilfs; + + if (sb->s_flags & MS_RDONLY) + return 0; + + down_write(&nilfs->ns_sem); + nilfs_setup_super(sbi, false); + up_write(&nilfs->ns_sem); + return 0; +} + static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; @@ -523,6 +554,8 @@ static const struct super_operations nilfs_sops = { .put_super = nilfs_put_super, /* .write_super = nilfs_write_super, */ .sync_fs = nilfs_sync_fs, + .freeze_fs = nilfs_freeze, + .unfreeze_fs = nilfs_unfreeze, /* .write_super_lockfs */ /* .unlockfs */ .statfs = nilfs_statfs, @@ -626,7 +659,7 @@ nilfs_set_default_options(struct nilfs_sb_info *sbi, NILFS_MOUNT_ERRORS_RO | NILFS_MOUNT_BARRIER; } -static int nilfs_setup_super(struct nilfs_sb_info *sbi) +static int nilfs_setup_super(struct nilfs_sb_info *sbi, int is_mount) { struct the_nilfs *nilfs = sbi->s_nilfs; struct nilfs_super_block **sbp; @@ -638,6 +671,9 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi) if (!sbp) return -EIO; + if (!is_mount) + goto skip_mount_setup; + max_mnt_count = le16_to_cpu(sbp[0]->s_max_mnt_count); mnt_count = le16_to_cpu(sbp[0]->s_mnt_count); @@ -654,9 +690,11 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi) sbp[0]->s_max_mnt_count = cpu_to_le16(NILFS_DFL_MAX_MNT_COUNT); sbp[0]->s_mnt_count = cpu_to_le16(mnt_count + 1); + sbp[0]->s_mtime = cpu_to_le64(get_seconds()); + +skip_mount_setup: sbp[0]->s_state = cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & ~NILFS_VALID_FS); - sbp[0]->s_mtime = cpu_to_le64(get_seconds()); /* synchronize sbp[1] with sbp[0] */ memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); return nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL); @@ -938,7 +976,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent) if (!(sb->s_flags & MS_RDONLY)) { down_write(&nilfs->ns_sem); - nilfs_setup_super(sbi); + nilfs_setup_super(sbi, true); up_write(&nilfs->ns_sem); } @@ -1034,7 +1072,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) goto restore_opts; down_write(&nilfs->ns_sem); - nilfs_setup_super(sbi); + nilfs_setup_super(sbi, true); up_write(&nilfs->ns_sem); } out: @@ -1132,7 +1170,19 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, goto failed; } + /* + * once the super is inserted into the list by sget, s_umount + * will protect the lockfs code from trying to start a snapshot + * while we are mounting + */ + mutex_lock(&sd.bdev->bd_fsfreeze_mutex); + if (sd.bdev->bd_fsfreeze_count > 0) { + mutex_unlock(&sd.bdev->bd_fsfreeze_mutex); + err = -EBUSY; + goto failed; + } s = sget(fs_type, nilfs_test_bdev_super, nilfs_set_bdev_super, sd.bdev); + mutex_unlock(&sd.bdev->bd_fsfreeze_mutex); if (IS_ERR(s)) { err = PTR_ERR(s); goto failed; -- cgit v1.2.3-18-g5258 From 026a7d63d55ba8656ed8c8a0733265cc7d47bb8c Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 7 Oct 2010 14:19:48 +0900 Subject: nilfs2: get rid of bdi from nilfs object Nilfs now can use sb->s_bdi to get backing_dev_info, so we use it instead of ns_bdi on the nilfs object and remove ns_bdi. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/mdt.c | 2 +- fs/nilfs2/segbuf.c | 3 ++- fs/nilfs2/super.c | 5 ++++- fs/nilfs2/the_nilfs.c | 4 ---- fs/nilfs2/the_nilfs.h | 2 -- 5 files changed, 7 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index d60fdb097d5..39a5b84e2c9 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -457,7 +457,7 @@ int nilfs_mdt_setup_shadow_map(struct inode *inode, struct nilfs_shadow_map *shadow) { struct nilfs_mdt_info *mi = NILFS_MDT(inode); - struct backing_dev_info *bdi = NILFS_I_NILFS(inode)->ns_bdi; + struct backing_dev_info *bdi = inode->i_sb->s_bdi; INIT_LIST_HEAD(&shadow->frozen_buffers); nilfs_mapping_init_once(&shadow->frozen_data); diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index 4588fb9e93d..0f83e93935b 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -371,7 +371,8 @@ static int nilfs_segbuf_submit_bio(struct nilfs_segment_buffer *segbuf, struct bio *bio = wi->bio; int err; - if (segbuf->sb_nbio > 0 && bdi_write_congested(wi->nilfs->ns_bdi)) { + if (segbuf->sb_nbio > 0 && + bdi_write_congested(segbuf->sb_super->s_bdi)) { wait_for_completion(&segbuf->sb_bio_event); segbuf->sb_nbio--; if (unlikely(atomic_read(&segbuf->sb_err))) { diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 92e8c769584..8e77016bafa 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -910,6 +910,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent) struct the_nilfs *nilfs; struct nilfs_sb_info *sbi; struct nilfs_root *fsroot; + struct backing_dev_info *bdi; __u64 cno; int err; @@ -948,7 +949,9 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_export_op = &nilfs_export_ops; sb->s_root = NULL; sb->s_time_gran = 1; - sb->s_bdi = nilfs->ns_bdi; + + bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info; + sb->s_bdi = bdi ? : &default_backing_dev_info; err = load_nilfs(nilfs, sbi); if (err) diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index a94aa57c4bd..bd02b6127d3 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -535,7 +535,6 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) { struct super_block *sb = sbi->s_super; struct nilfs_super_block *sbp; - struct backing_dev_info *bdi; int blocksize; int err; @@ -598,9 +597,6 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) nilfs->ns_mount_state = le16_to_cpu(sbp->s_state); - bdi = nilfs->ns_bdev->bd_inode->i_mapping->backing_dev_info; - nilfs->ns_bdi = bdi ? : &default_backing_dev_info; - err = nilfs_store_log_cursor(nilfs, sbp); if (err) goto failed_sbh; diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index bbbc1c748aa..69226e14b74 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -47,7 +47,6 @@ enum { * struct the_nilfs - struct to supervise multiple nilfs mount points * @ns_flags: flags * @ns_bdev: block device - * @ns_bdi: backing dev info * @ns_sem: semaphore for shared states * @ns_sbh: buffer heads of on-disk super blocks * @ns_sbp: pointers to super block data @@ -90,7 +89,6 @@ struct the_nilfs { unsigned long ns_flags; struct block_device *ns_bdev; - struct backing_dev_info *ns_bdi; struct rw_semaphore ns_sem; /* -- cgit v1.2.3-18-g5258 From abc0b50b6b9a9de8ae210f059598265a5438f2c4 Mon Sep 17 00:00:00 2001 From: Jiro SEKIBA Date: Fri, 8 Oct 2010 22:37:27 +0900 Subject: nilfs2: eliminate sparse warnings - "symbol not declared" change nilfs_dat_commit_free and nilfs_inode_cachep static to fix following warnings fs/nilfs2/super.c:72:19: warning: symbol 'nilfs_inode_cachep' was not declared. Should it be static? fs/nilfs2/dat.c:106:6: warning: symbol 'nilfs_dat_commit_free' was not declared. Should it be static? Signed-off-by: Jiro SEKIBA Signed-off-by: Ryusuke Konishi --- fs/nilfs2/dat.c | 3 ++- fs/nilfs2/super.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c index ab04a68f425..49c844dab33 100644 --- a/fs/nilfs2/dat.c +++ b/fs/nilfs2/dat.c @@ -103,7 +103,8 @@ void nilfs_dat_abort_alloc(struct inode *dat, struct nilfs_palloc_req *req) nilfs_palloc_abort_alloc_entry(dat, req); } -void nilfs_dat_commit_free(struct inode *dat, struct nilfs_palloc_req *req) +static void nilfs_dat_commit_free(struct inode *dat, + struct nilfs_palloc_req *req) { struct nilfs_dat_entry *entry; void *kaddr; diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 8e77016bafa..ab629078f4e 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -68,7 +68,7 @@ MODULE_DESCRIPTION("A New Implementation of the Log-structured Filesystem " "(NILFS)"); MODULE_LICENSE("GPL"); -struct kmem_cache *nilfs_inode_cachep; +static struct kmem_cache *nilfs_inode_cachep; struct kmem_cache *nilfs_transaction_cachep; struct kmem_cache *nilfs_segbuf_cachep; struct kmem_cache *nilfs_btree_path_cache; -- cgit v1.2.3-18-g5258 From 6b81e14e645016597c81e71cd27ee5c57c3a3c36 Mon Sep 17 00:00:00 2001 From: Jiro SEKIBA Date: Thu, 14 Oct 2010 13:52:00 +0900 Subject: nilfs2: eliminate sparse warning - "context imbalance" insert sparse annotations to fix following sparse warning. fs/nilfs2/segment.c:2681:3: warning: context imbalance in 'nilfs_segctor_kill_thread' - unexpected unlock nilfs_segctor_kill_thread is only called inside sc_state_lock lock. sparse doesn't detect the context and warn "unexpected unlock". __acquires/__releases pretend to lock/unlock the sc_state_lock for sparse. Signed-off-by: Jiro SEKIBA Signed-off-by: Ryusuke Konishi --- fs/nilfs2/segment.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 172ad425749..d926af62617 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2674,6 +2674,8 @@ static int nilfs_segctor_start_thread(struct nilfs_sc_info *sci) } static void nilfs_segctor_kill_thread(struct nilfs_sc_info *sci) + __acquires(&sci->sc_state_lock) + __releases(&sci->sc_state_lock) { sci->sc_state |= NILFS_SEGCTOR_QUIT; -- cgit v1.2.3-18-g5258 From f8cae0f03f75adb54b1d48ddbc90f84a1f5de186 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 22 Oct 2010 19:30:38 -0700 Subject: ocfs2: drop the BLKDEV_IFL_WAIT flag Commit dd3932eddf42 ("block: remove BLKDEV_IFL_WAIT") had removed the flag argument to blkdev_issue_flush(), but the ocfs2 merge brought in a new one. It didn't cause a merge conflict, so the merges silently worked out fine, but the result didn't actually compile. Signed-off-by: Linus Torvalds --- fs/ocfs2/file.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 9e8cc4346b7..1ca6867935b 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -187,8 +187,7 @@ static int ocfs2_sync_file(struct file *file, int datasync) * platter */ if (osb->s_mount_opt & OCFS2_MOUNT_BARRIER) - blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, - NULL, BLKDEV_IFL_WAIT); + blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); goto bail; } -- cgit v1.2.3-18-g5258 From 6c2754c28f2388a276fe21edde826f2113c8f60e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 23 Oct 2010 08:14:12 -0700 Subject: Revert "tty: Add a new file /proc/tty/consoles" This reverts commit f4a3e0bceb57466c31757f25e4e0ed108d1299ec. Jiri Sladby points out that the tty structure we're using may already be gone, and Al Viro doesn't hold back in complaining about the random loading of 'filp->private_data' which doesn't have to be a pointer at all, nor does checking the magic field for TTY_MAGIC prove anything. Belated review by Al: "a) global variable depending on stdin of the last opener? Affecting output of read(2)? Really? b) iterator is broken; list should be locked in ->start(), unlocked in ->stop() and *NOT* unlocked/relocked in ->next() c) ->show() ought to do nothing in case of ->device == NULL, instead of skipping those in ->next()/->start() d) regardless of the merits of the bright idea about asterisk at that line in output *and* regardless of (a), the implementation is not only atrociously ugly, it's actually very likely to be a roothole. Verifying that Cthulhu knows what number happens to be address of a tty_struct by blindly dereferencing memory at that address... Ouch. Please revert that crap." And Christoph pipes in and NAK's the approach of walking fd tables etc too. So it's pretty unanimous. Noticed-by: Jri Slaby Requested-by: Al Viro Cc: Greg Kroah-Hartman Cc: Werner Fink Cc: Alan Cox Cc: Christoph Hellwig Signed-off-by: Linus Torvalds --- fs/proc/proc_tty.c | 158 ----------------------------------------------------- 1 file changed, 158 deletions(-) (limited to 'fs') diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index dc44f94022f..83adcc86943 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -12,10 +12,7 @@ #include #include #include -#include -#include #include -#include #include /* @@ -139,160 +136,6 @@ static const struct file_operations proc_tty_drivers_operations = { .release = seq_release, }; -/* - * The device ID of file descriptor 0 of the current reading - * task if a character device... - */ -static dev_t current_dev; - -/* - * This is the handler for /proc/tty/consoles - */ -static int show_console_dev(struct seq_file *m, void *v) -{ - const struct tty_driver *driver; - struct console *con; - int index, len; - char flags[10]; - dev_t dev; - - if (v == SEQ_START_TOKEN) - return 0; - con = (struct console *)v; - if (!con) - return 0; - driver = con->device(con, &index); - if (!driver) - return 0; - dev = MKDEV(driver->major, driver->minor_start) + index; - - index = 0; - if (con->flags & CON_ENABLED) - flags[index++] = 'E'; - if (con->flags & CON_CONSDEV) - flags[index++] = 'C'; - if (con->flags & CON_BOOT) - flags[index++] = 'B'; - if (con->flags & CON_PRINTBUFFER) - flags[index++] = 'p'; - if (con->flags & CON_BRL) - flags[index++] = 'b'; - if (con->flags & CON_ANYTIME) - flags[index++] = 'a'; - if (current_dev == dev) - flags[index++] = '*'; - flags[index] = 0; - - seq_printf(m, "%s%d%n", con->name, con->index, &len); - len = 21 - len; - if (len < 1) - len = 1; - seq_printf(m, "%*c", len, ' '); - seq_printf(m, "%c%c%c (%s)%n", con->read ? 'R' : '-', - con->write ? 'W' : '-', con->unblank ? 'U' : '-', - flags, &len); - len = 13 - len; - if (len < 1) - len = 1; - seq_printf(m, "%*c%4d:%d\n", len, ' ', MAJOR(dev), MINOR(dev)); - - return 0; -} - -/* iterator for consoles */ -static void *c_start(struct seq_file *m, loff_t *pos) -{ - struct console *con; - loff_t off = 0; - - if (*pos == 0) - return SEQ_START_TOKEN; - - acquire_console_sem(); - for (con = console_drivers; con; con = con->next) { - if (!con->device) - continue; - if (++off == *pos) - break; - } - release_console_sem(); - - return con; -} - -static void *c_next(struct seq_file *m, void *v, loff_t *pos) -{ - struct console *con; - - acquire_console_sem(); - if (v == SEQ_START_TOKEN) - con = console_drivers; - else - con = ((struct console *)v)->next; - for (; con; con = con->next) { - if (!con->device) - continue; - ++*pos; - break; - } - release_console_sem(); - - return con; -} - -static void c_stop(struct seq_file *m, void *v) -{ -} - -static const struct seq_operations tty_consoles_op = { - .start = c_start, - .next = c_next, - .stop = c_stop, - .show = show_console_dev -}; - -/* - * Used for open /proc/tty/consoles. Before this detect - * the device ID of file descriptor 0 of the current - * reading task if a character device... - */ -static int tty_consoles_open(struct inode *inode, struct file *file) -{ - struct files_struct *curfiles; - - current_dev = 0; - curfiles = get_files_struct(current); - if (curfiles) { - const struct file *curfp; - spin_lock(&curfiles->file_lock); - curfp = fcheck_files(curfiles, 0); - if (curfp && curfp->private_data) { - const struct inode *inode; - dget(curfp->f_dentry); - inode = curfp->f_dentry->d_inode; - if (S_ISCHR(inode->i_mode)) { - struct tty_struct *tty; - tty = (struct tty_struct *)curfp->private_data; - if (tty && tty->magic == TTY_MAGIC) { - tty = tty_pair_get_tty(tty); - current_dev = tty_devnum(tty); - } - } - dput(curfp->f_dentry); - } - spin_unlock(&curfiles->file_lock); - put_files_struct(curfiles); - } - return seq_open(file, &tty_consoles_op); -} - -static const struct file_operations proc_tty_consoles_operations = { - .open = tty_consoles_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - /* * This function is called by tty_register_driver() to handle * registering the driver's /proc handler into /proc/tty/driver/ @@ -343,5 +186,4 @@ void __init proc_tty_init(void) proc_tty_driver = proc_mkdir_mode("tty/driver", S_IRUSR|S_IXUSR, NULL); proc_create("tty/ldiscs", 0, NULL, &tty_ldiscs_proc_fops); proc_create("tty/drivers", 0, NULL, &proc_tty_drivers_operations); - proc_create("tty/consoles", 0, NULL, &proc_tty_consoles_operations); } -- cgit v1.2.3-18-g5258