aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2014-11-06 16:30:30 -0500
committerDave Chinner <david@fromorbit.com>2014-11-06 16:30:30 -0500
commitbf4a5af20d25ecc8876978ad34b8db83b4235f3c (patch)
tree0eb16d3c6176c769c2f8802422fa94bdf72fb209 /fs/xfs
parentafa947cb52a8e73fe71915a0b0af6fcf98dfbe1a (diff)
xfs: bulkstat chunk formatting cursor is broken
The xfs_bulkstat_agichunk formatting cursor takes buffer values from the main loop and passes them via the structure to the chunk formatter, and the writes the changed values back into the main loop local variables. Unfortunately, this complex dance is full of corner cases that aren't handled correctly. The biggest problem is that it is double handling the information in both the main loop and the chunk formatting function, leading to inconsistent updates and endless loops where progress is not made. To fix this, push the struct xfs_bulkstat_agichunk outwards to be the primary holder of user buffer information. this removes the double handling in the main loop. Also, pass the last inode processed by the chunk formatter as a separate parameter as it purely an output variable and is not related to the user buffer consumption cursor. Finally, the chunk formatting code is not shared by anyone, so make it local to xfs_itable.c. cc: <stable@vger.kernel.org> # 3.17 Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
Diffstat (limited to 'fs/xfs')
-rw-r--r--fs/xfs/xfs_itable.c59
-rw-r--r--fs/xfs/xfs_itable.h16
2 files changed, 28 insertions, 47 deletions
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index 16737cbbee17..50a3e5995dd9 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -262,20 +262,26 @@ xfs_bulkstat_grab_ichunk(
262 262
263#define XFS_BULKSTAT_UBLEFT(ubleft) ((ubleft) >= statstruct_size) 263#define XFS_BULKSTAT_UBLEFT(ubleft) ((ubleft) >= statstruct_size)
264 264
265struct xfs_bulkstat_agichunk {
266 char __user **ac_ubuffer;/* pointer into user's buffer */
267 int ac_ubleft; /* bytes left in user's buffer */
268 int ac_ubelem; /* spaces used in user's buffer */
269};
270
265/* 271/*
266 * Process inodes in chunk with a pointer to a formatter function 272 * Process inodes in chunk with a pointer to a formatter function
267 * that will iget the inode and fill in the appropriate structure. 273 * that will iget the inode and fill in the appropriate structure.
268 */ 274 */
269int 275static int
270xfs_bulkstat_ag_ichunk( 276xfs_bulkstat_ag_ichunk(
271 struct xfs_mount *mp, 277 struct xfs_mount *mp,
272 xfs_agnumber_t agno, 278 xfs_agnumber_t agno,
273 struct xfs_inobt_rec_incore *irbp, 279 struct xfs_inobt_rec_incore *irbp,
274 bulkstat_one_pf formatter, 280 bulkstat_one_pf formatter,
275 size_t statstruct_size, 281 size_t statstruct_size,
276 struct xfs_bulkstat_agichunk *acp) 282 struct xfs_bulkstat_agichunk *acp,
283 xfs_ino_t *lastino)
277{ 284{
278 xfs_ino_t lastino = acp->ac_lastino;
279 char __user **ubufp = acp->ac_ubuffer; 285 char __user **ubufp = acp->ac_ubuffer;
280 int ubleft = acp->ac_ubleft; 286 int ubleft = acp->ac_ubleft;
281 int ubelem = acp->ac_ubelem; 287 int ubelem = acp->ac_ubelem;
@@ -295,7 +301,7 @@ xfs_bulkstat_ag_ichunk(
295 301
296 /* Skip if this inode is free */ 302 /* Skip if this inode is free */
297 if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free) { 303 if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free) {
298 lastino = ino; 304 *lastino = ino;
299 continue; 305 continue;
300 } 306 }
301 307
@@ -313,7 +319,7 @@ xfs_bulkstat_ag_ichunk(
313 ubleft = 0; 319 ubleft = 0;
314 break; 320 break;
315 } 321 }
316 lastino = ino; 322 *lastino = ino;
317 continue; 323 continue;
318 } 324 }
319 if (fmterror == BULKSTAT_RV_GIVEUP) { 325 if (fmterror == BULKSTAT_RV_GIVEUP) {
@@ -325,10 +331,9 @@ xfs_bulkstat_ag_ichunk(
325 *ubufp += ubused; 331 *ubufp += ubused;
326 ubleft -= ubused; 332 ubleft -= ubused;
327 ubelem++; 333 ubelem++;
328 lastino = ino; 334 *lastino = ino;
329 } 335 }
330 336
331 acp->ac_lastino = lastino;
332 acp->ac_ubleft = ubleft; 337 acp->ac_ubleft = ubleft;
333 acp->ac_ubelem = ubelem; 338 acp->ac_ubelem = ubelem;
334 339
@@ -355,7 +360,6 @@ xfs_bulkstat(
355 xfs_btree_cur_t *cur; /* btree cursor for ialloc btree */ 360 xfs_btree_cur_t *cur; /* btree cursor for ialloc btree */
356 int end_of_ag; /* set if we've seen the ag end */ 361 int end_of_ag; /* set if we've seen the ag end */
357 int error; /* error code */ 362 int error; /* error code */
358 int fmterror;/* bulkstat formatter result */
359 int icount; /* count of inodes good in irbuf */ 363 int icount; /* count of inodes good in irbuf */
360 size_t irbsize; /* size of irec buffer in bytes */ 364 size_t irbsize; /* size of irec buffer in bytes */
361 xfs_ino_t ino; /* inode number (filesystem) */ 365 xfs_ino_t ino; /* inode number (filesystem) */
@@ -366,10 +370,8 @@ xfs_bulkstat(
366 int nirbuf; /* size of irbuf */ 370 int nirbuf; /* size of irbuf */
367 int rval; /* return value error code */ 371 int rval; /* return value error code */
368 int ubcount; /* size of user's buffer */ 372 int ubcount; /* size of user's buffer */
369 int ubleft; /* bytes left in user's buffer */
370 char __user *ubufp; /* pointer into user's buffer */
371 int ubelem; /* spaces used in user's buffer */
372 int stat; 373 int stat;
374 struct xfs_bulkstat_agichunk ac;
373 375
374 /* 376 /*
375 * Get the last inode value, see if there's nothing to do. 377 * Get the last inode value, see if there's nothing to do.
@@ -386,11 +388,13 @@ xfs_bulkstat(
386 } 388 }
387 389
388 ubcount = *ubcountp; /* statstruct's */ 390 ubcount = *ubcountp; /* statstruct's */
389 ubleft = ubcount * statstruct_size; /* bytes */ 391 ac.ac_ubuffer = &ubuffer;
390 *ubcountp = ubelem = 0; 392 ac.ac_ubleft = ubcount * statstruct_size; /* bytes */;
393 ac.ac_ubelem = 0;
394
395 *ubcountp = 0;
391 *done = 0; 396 *done = 0;
392 fmterror = 0; 397
393 ubufp = ubuffer;
394 irbuf = kmem_zalloc_greedy(&irbsize, PAGE_SIZE, PAGE_SIZE * 4); 398 irbuf = kmem_zalloc_greedy(&irbsize, PAGE_SIZE, PAGE_SIZE * 4);
395 if (!irbuf) 399 if (!irbuf)
396 return -ENOMEM; 400 return -ENOMEM;
@@ -402,7 +406,7 @@ xfs_bulkstat(
402 * inode returned; 0 means start of the allocation group. 406 * inode returned; 0 means start of the allocation group.
403 */ 407 */
404 rval = 0; 408 rval = 0;
405 while (XFS_BULKSTAT_UBLEFT(ubleft) && agno < mp->m_sb.sb_agcount) { 409 while (XFS_BULKSTAT_UBLEFT(ac.ac_ubleft) && agno < mp->m_sb.sb_agcount) {
406 cond_resched(); 410 cond_resched();
407 error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp); 411 error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
408 if (error) 412 if (error)
@@ -497,28 +501,21 @@ del_cursor:
497 */ 501 */
498 irbufend = irbp; 502 irbufend = irbp;
499 for (irbp = irbuf; 503 for (irbp = irbuf;
500 irbp < irbufend && XFS_BULKSTAT_UBLEFT(ubleft); irbp++) { 504 irbp < irbufend && XFS_BULKSTAT_UBLEFT(ac.ac_ubleft);
501 struct xfs_bulkstat_agichunk ac; 505 irbp++) {
502
503 ac.ac_lastino = lastino;
504 ac.ac_ubuffer = &ubuffer;
505 ac.ac_ubleft = ubleft;
506 ac.ac_ubelem = ubelem;
507 error = xfs_bulkstat_ag_ichunk(mp, agno, irbp, 506 error = xfs_bulkstat_ag_ichunk(mp, agno, irbp,
508 formatter, statstruct_size, &ac); 507 formatter, statstruct_size, &ac,
508 &lastino);
509 if (error) 509 if (error)
510 rval = error; 510 rval = error;
511 511
512 lastino = ac.ac_lastino;
513 ubleft = ac.ac_ubleft;
514 ubelem = ac.ac_ubelem;
515
516 cond_resched(); 512 cond_resched();
517 } 513 }
514
518 /* 515 /*
519 * Set up for the next loop iteration. 516 * Set up for the next loop iteration.
520 */ 517 */
521 if (XFS_BULKSTAT_UBLEFT(ubleft)) { 518 if (XFS_BULKSTAT_UBLEFT(ac.ac_ubleft)) {
522 if (end_of_ag) { 519 if (end_of_ag) {
523 agno++; 520 agno++;
524 agino = 0; 521 agino = 0;
@@ -531,11 +528,11 @@ del_cursor:
531 * Done, we're either out of filesystem or space to put the data. 528 * Done, we're either out of filesystem or space to put the data.
532 */ 529 */
533 kmem_free(irbuf); 530 kmem_free(irbuf);
534 *ubcountp = ubelem; 531 *ubcountp = ac.ac_ubelem;
535 /* 532 /*
536 * Found some inodes, return them now and return the error next time. 533 * Found some inodes, return them now and return the error next time.
537 */ 534 */
538 if (ubelem) 535 if (ac.ac_ubelem)
539 rval = 0; 536 rval = 0;
540 if (agno >= mp->m_sb.sb_agcount) { 537 if (agno >= mp->m_sb.sb_agcount) {
541 /* 538 /*
diff --git a/fs/xfs/xfs_itable.h b/fs/xfs/xfs_itable.h
index aaed08022eb9..6ea8b3912fa4 100644
--- a/fs/xfs/xfs_itable.h
+++ b/fs/xfs/xfs_itable.h
@@ -30,22 +30,6 @@ typedef int (*bulkstat_one_pf)(struct xfs_mount *mp,
30 int *ubused, 30 int *ubused,
31 int *stat); 31 int *stat);
32 32
33struct xfs_bulkstat_agichunk {
34 xfs_ino_t ac_lastino; /* last inode returned */
35 char __user **ac_ubuffer;/* pointer into user's buffer */
36 int ac_ubleft; /* bytes left in user's buffer */
37 int ac_ubelem; /* spaces used in user's buffer */
38};
39
40int
41xfs_bulkstat_ag_ichunk(
42 struct xfs_mount *mp,
43 xfs_agnumber_t agno,
44 struct xfs_inobt_rec_incore *irbp,
45 bulkstat_one_pf formatter,
46 size_t statstruct_size,
47 struct xfs_bulkstat_agichunk *acp);
48
49/* 33/*
50 * Values for stat return value. 34 * Values for stat return value.
51 */ 35 */