diff options
author | Peng Tao <bergwolf@gmail.com> | 2012-08-23 12:27:52 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-10-01 18:38:29 -0400 |
commit | f742dc4a32587bff50b13dde9d8894b96851951a (patch) | |
tree | beedd26b6424ab5bc8dd63fcc04644c5023a4803 | |
parent | fe6e1e8d9fad86873eb74a26e80a8f91f9e870b5 (diff) |
pnfsblock: fix non-aligned DIO read
For DIO read, if it is not sector aligned, we should reject it
and resend via MDS. Otherwise there might be data corruption.
Also teach bl_read_pagelist to handle partial page reads for DIO.
Cc: stable <stable@vger.kernel.org> [since v3.4]
Signed-off-by: Peng Tao <tao.peng@emc.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/blocklayout/blocklayout.c | 64 |
1 files changed, 56 insertions, 8 deletions
diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index a9fe644a12d1..61e04fb7c4b8 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c | |||
@@ -252,8 +252,11 @@ bl_read_pagelist(struct nfs_read_data *rdata) | |||
252 | sector_t isect, extent_length = 0; | 252 | sector_t isect, extent_length = 0; |
253 | struct parallel_io *par; | 253 | struct parallel_io *par; |
254 | loff_t f_offset = rdata->args.offset; | 254 | loff_t f_offset = rdata->args.offset; |
255 | size_t bytes_left = rdata->args.count; | ||
256 | unsigned int pg_offset, pg_len; | ||
255 | struct page **pages = rdata->args.pages; | 257 | struct page **pages = rdata->args.pages; |
256 | int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT; | 258 | int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT; |
259 | const bool is_dio = (header->dreq != NULL); | ||
257 | 260 | ||
258 | dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__, | 261 | dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__, |
259 | rdata->pages.npages, f_offset, (unsigned int)rdata->args.count); | 262 | rdata->pages.npages, f_offset, (unsigned int)rdata->args.count); |
@@ -287,36 +290,53 @@ bl_read_pagelist(struct nfs_read_data *rdata) | |||
287 | extent_length = min(extent_length, cow_length); | 290 | extent_length = min(extent_length, cow_length); |
288 | } | 291 | } |
289 | } | 292 | } |
293 | |||
294 | if (is_dio) { | ||
295 | pg_offset = f_offset & ~PAGE_CACHE_MASK; | ||
296 | if (pg_offset + bytes_left > PAGE_CACHE_SIZE) | ||
297 | pg_len = PAGE_CACHE_SIZE - pg_offset; | ||
298 | else | ||
299 | pg_len = bytes_left; | ||
300 | |||
301 | f_offset += pg_len; | ||
302 | bytes_left -= pg_len; | ||
303 | isect += (pg_offset >> SECTOR_SHIFT); | ||
304 | } else { | ||
305 | pg_offset = 0; | ||
306 | pg_len = PAGE_CACHE_SIZE; | ||
307 | } | ||
308 | |||
290 | hole = is_hole(be, isect); | 309 | hole = is_hole(be, isect); |
291 | if (hole && !cow_read) { | 310 | if (hole && !cow_read) { |
292 | bio = bl_submit_bio(READ, bio); | 311 | bio = bl_submit_bio(READ, bio); |
293 | /* Fill hole w/ zeroes w/o accessing device */ | 312 | /* Fill hole w/ zeroes w/o accessing device */ |
294 | dprintk("%s Zeroing page for hole\n", __func__); | 313 | dprintk("%s Zeroing page for hole\n", __func__); |
295 | zero_user_segment(pages[i], 0, PAGE_CACHE_SIZE); | 314 | zero_user_segment(pages[i], pg_offset, pg_len); |
296 | print_page(pages[i]); | 315 | print_page(pages[i]); |
297 | SetPageUptodate(pages[i]); | 316 | SetPageUptodate(pages[i]); |
298 | } else { | 317 | } else { |
299 | struct pnfs_block_extent *be_read; | 318 | struct pnfs_block_extent *be_read; |
300 | 319 | ||
301 | be_read = (hole && cow_read) ? cow_read : be; | 320 | be_read = (hole && cow_read) ? cow_read : be; |
302 | bio = bl_add_page_to_bio(bio, rdata->pages.npages - i, | 321 | bio = do_add_page_to_bio(bio, rdata->pages.npages - i, |
303 | READ, | 322 | READ, |
304 | isect, pages[i], be_read, | 323 | isect, pages[i], be_read, |
305 | bl_end_io_read, par); | 324 | bl_end_io_read, par, |
325 | pg_offset, pg_len); | ||
306 | if (IS_ERR(bio)) { | 326 | if (IS_ERR(bio)) { |
307 | header->pnfs_error = PTR_ERR(bio); | 327 | header->pnfs_error = PTR_ERR(bio); |
308 | bio = NULL; | 328 | bio = NULL; |
309 | goto out; | 329 | goto out; |
310 | } | 330 | } |
311 | } | 331 | } |
312 | isect += PAGE_CACHE_SECTORS; | 332 | isect += (pg_len >> SECTOR_SHIFT); |
313 | extent_length -= PAGE_CACHE_SECTORS; | 333 | extent_length -= PAGE_CACHE_SECTORS; |
314 | } | 334 | } |
315 | if ((isect << SECTOR_SHIFT) >= header->inode->i_size) { | 335 | if ((isect << SECTOR_SHIFT) >= header->inode->i_size) { |
316 | rdata->res.eof = 1; | 336 | rdata->res.eof = 1; |
317 | rdata->res.count = header->inode->i_size - f_offset; | 337 | rdata->res.count = header->inode->i_size - rdata->args.offset; |
318 | } else { | 338 | } else { |
319 | rdata->res.count = (isect << SECTOR_SHIFT) - f_offset; | 339 | rdata->res.count = (isect << SECTOR_SHIFT) - rdata->args.offset; |
320 | } | 340 | } |
321 | out: | 341 | out: |
322 | bl_put_extent(be); | 342 | bl_put_extent(be); |
@@ -1149,9 +1169,37 @@ bl_clear_layoutdriver(struct nfs_server *server) | |||
1149 | return 0; | 1169 | return 0; |
1150 | } | 1170 | } |
1151 | 1171 | ||
1172 | static bool | ||
1173 | is_aligned_req(struct nfs_page *req, unsigned int alignment) | ||
1174 | { | ||
1175 | return IS_ALIGNED(req->wb_offset, alignment) && | ||
1176 | IS_ALIGNED(req->wb_bytes, alignment); | ||
1177 | } | ||
1178 | |||
1179 | static void | ||
1180 | bl_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req) | ||
1181 | { | ||
1182 | if (pgio->pg_dreq != NULL && | ||
1183 | !is_aligned_req(req, SECTOR_SIZE)) | ||
1184 | nfs_pageio_reset_read_mds(pgio); | ||
1185 | else | ||
1186 | pnfs_generic_pg_init_read(pgio, req); | ||
1187 | } | ||
1188 | |||
1189 | static bool | ||
1190 | bl_pg_test_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev, | ||
1191 | struct nfs_page *req) | ||
1192 | { | ||
1193 | if (pgio->pg_dreq != NULL && | ||
1194 | !is_aligned_req(req, SECTOR_SIZE)) | ||
1195 | return false; | ||
1196 | |||
1197 | return pnfs_generic_pg_test(pgio, prev, req); | ||
1198 | } | ||
1199 | |||
1152 | static const struct nfs_pageio_ops bl_pg_read_ops = { | 1200 | static const struct nfs_pageio_ops bl_pg_read_ops = { |
1153 | .pg_init = pnfs_generic_pg_init_read, | 1201 | .pg_init = bl_pg_init_read, |
1154 | .pg_test = pnfs_generic_pg_test, | 1202 | .pg_test = bl_pg_test_read, |
1155 | .pg_doio = pnfs_generic_pg_readpages, | 1203 | .pg_doio = pnfs_generic_pg_readpages, |
1156 | }; | 1204 | }; |
1157 | 1205 | ||