diff options
author | Peng Tao <bergwolf@gmail.com> | 2012-05-29 01:57:58 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-07-30 18:52:06 -0400 |
commit | 159e0561e322dd8008fff59e36efff8d2bdd0b0e (patch) | |
tree | 19a15ff455d33cb25f1747027751e46b2e79efb0 /fs/nfs/blocklayout | |
parent | f44106e2173f08ccb1c9195d85a6c22388b461c1 (diff) |
pnfsblock: bail out partial page IO
Current block layout driver read/write code assumes page
aligned IO in many places. Add a checker to validate the assumption.
Otherwise there would be data corruption like when application does
open(O_WRONLY) and page unaliged write.
Signed-off-by: Peng Tao <tao.peng@emc.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/blocklayout')
-rw-r--r-- | fs/nfs/blocklayout/blocklayout.c | 39 |
1 files changed, 36 insertions, 3 deletions
diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 7ae8a608956f..dd392ed5f2e2 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c | |||
@@ -228,6 +228,14 @@ bl_end_par_io_read(void *data, int unused) | |||
228 | schedule_work(&rdata->task.u.tk_work); | 228 | schedule_work(&rdata->task.u.tk_work); |
229 | } | 229 | } |
230 | 230 | ||
231 | static bool | ||
232 | bl_check_alignment(u64 offset, u32 len, unsigned long blkmask) | ||
233 | { | ||
234 | if ((offset & blkmask) || (len & blkmask)) | ||
235 | return false; | ||
236 | return true; | ||
237 | } | ||
238 | |||
231 | static enum pnfs_try_status | 239 | static enum pnfs_try_status |
232 | bl_read_pagelist(struct nfs_read_data *rdata) | 240 | bl_read_pagelist(struct nfs_read_data *rdata) |
233 | { | 241 | { |
@@ -244,6 +252,9 @@ bl_read_pagelist(struct nfs_read_data *rdata) | |||
244 | dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__, | 252 | dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__, |
245 | rdata->pages.npages, f_offset, (unsigned int)rdata->args.count); | 253 | rdata->pages.npages, f_offset, (unsigned int)rdata->args.count); |
246 | 254 | ||
255 | if (!bl_check_alignment(f_offset, rdata->args.count, PAGE_CACHE_MASK)) | ||
256 | goto use_mds; | ||
257 | |||
247 | par = alloc_parallel(rdata); | 258 | par = alloc_parallel(rdata); |
248 | if (!par) | 259 | if (!par) |
249 | goto use_mds; | 260 | goto use_mds; |
@@ -552,7 +563,7 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync) | |||
552 | struct bio *bio = NULL; | 563 | struct bio *bio = NULL; |
553 | struct pnfs_block_extent *be = NULL, *cow_read = NULL; | 564 | struct pnfs_block_extent *be = NULL, *cow_read = NULL; |
554 | sector_t isect, last_isect = 0, extent_length = 0; | 565 | sector_t isect, last_isect = 0, extent_length = 0; |
555 | struct parallel_io *par; | 566 | struct parallel_io *par = NULL; |
556 | loff_t offset = wdata->args.offset; | 567 | loff_t offset = wdata->args.offset; |
557 | size_t count = wdata->args.count; | 568 | size_t count = wdata->args.count; |
558 | struct page **pages = wdata->args.pages; | 569 | struct page **pages = wdata->args.pages; |
@@ -563,6 +574,10 @@ bl_write_pagelist(struct nfs_write_data *wdata, int sync) | |||
563 | NFS_SERVER(header->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT; | 574 | NFS_SERVER(header->inode)->pnfs_blksize >> PAGE_CACHE_SHIFT; |
564 | 575 | ||
565 | dprintk("%s enter, %Zu@%lld\n", __func__, count, offset); | 576 | dprintk("%s enter, %Zu@%lld\n", __func__, count, offset); |
577 | /* Check for alignment first */ | ||
578 | if (!bl_check_alignment(offset, count, PAGE_CACHE_MASK)) | ||
579 | goto out_mds; | ||
580 | |||
566 | /* At this point, wdata->pages is a (sequential) list of nfs_pages. | 581 | /* At this point, wdata->pages is a (sequential) list of nfs_pages. |
567 | * We want to write each, and if there is an error set pnfs_error | 582 | * We want to write each, and if there is an error set pnfs_error |
568 | * to have it redone using nfs. | 583 | * to have it redone using nfs. |
@@ -996,14 +1011,32 @@ bl_clear_layoutdriver(struct nfs_server *server) | |||
996 | return 0; | 1011 | return 0; |
997 | } | 1012 | } |
998 | 1013 | ||
1014 | static void | ||
1015 | bl_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) | ||
1016 | { | ||
1017 | if (!bl_check_alignment(req->wb_offset, req->wb_bytes, PAGE_CACHE_MASK)) | ||
1018 | nfs_pageio_reset_read_mds(pgio); | ||
1019 | else | ||
1020 | pnfs_generic_pg_init_read(pgio, req); | ||
1021 | } | ||
1022 | |||
1023 | static void | ||
1024 | bl_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) | ||
1025 | { | ||
1026 | if (!bl_check_alignment(req->wb_offset, req->wb_bytes, PAGE_CACHE_MASK)) | ||
1027 | nfs_pageio_reset_write_mds(pgio); | ||
1028 | else | ||
1029 | pnfs_generic_pg_init_write(pgio, req); | ||
1030 | } | ||
1031 | |||
999 | static const struct nfs_pageio_ops bl_pg_read_ops = { | 1032 | static const struct nfs_pageio_ops bl_pg_read_ops = { |
1000 | .pg_init = pnfs_generic_pg_init_read, | 1033 | .pg_init = bl_pg_init_read, |
1001 | .pg_test = pnfs_generic_pg_test, | 1034 | .pg_test = pnfs_generic_pg_test, |
1002 | .pg_doio = pnfs_generic_pg_readpages, | 1035 | .pg_doio = pnfs_generic_pg_readpages, |
1003 | }; | 1036 | }; |
1004 | 1037 | ||
1005 | static const struct nfs_pageio_ops bl_pg_write_ops = { | 1038 | static const struct nfs_pageio_ops bl_pg_write_ops = { |
1006 | .pg_init = pnfs_generic_pg_init_write, | 1039 | .pg_init = bl_pg_init_write, |
1007 | .pg_test = pnfs_generic_pg_test, | 1040 | .pg_test = pnfs_generic_pg_test, |
1008 | .pg_doio = pnfs_generic_pg_writepages, | 1041 | .pg_doio = pnfs_generic_pg_writepages, |
1009 | }; | 1042 | }; |