Skip to contentSkip to conversation

xfs_repair: fix prefetch queue waiting

PATCH In review Eric Sandeen 3 participants 4 messages Apr 8, 2014

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 xfs_repair: fix prefetch queue waiting

This fixes a regression caused by:

97b1fcf xfs_repair: fix array overrun in do_inode_prefetch

The thread creation loop has 2 ways to exit; either via the loop counter based on thread_count, or the break statement if we've started enough workers to cover all AGs.

Whether or not the loop counter "i" reflects the number of threads started depends on whether or not we exited via the break.

The above commit prevented us from indexing off the end of the queues[] array if we actually advanced "i" all the way to thread_count, but in the case where we break, "i" is one *less* than the nr of threads started, so we don't wait for completion of all threads, and all hell breaks loose in phase 5.

Just stop with the cleverness of re-using the loop counter - instead, explicitly count threads that we start, and then use that counter to wait for each worker to complete.

---

I have one fs which demonstrates the problem, and have verified the regression & tested the fix against that.

I'll run this over xfstests overnight, but it seems obvious from here (OTOH the other fix seemed obvious too) :(

repair/prefetch.c +3 -1
index e47a48e..4c32395 100644
@@ -944,6 +944,7 @@ do_inode_prefetch(
 	int			i;
 	struct work_queue	queue;
 	struct work_queue	*queues;
+	int			queues_started = 0;
 
 	/*
 	 * If the previous phases of repair have not overflowed the buffer
@@ -987,6 +988,7 @@ do_inode_prefetch(
 
 		create_work_queue(&queues[i], mp, 1);
 		queue_work(&queues[i], prefetch_ag_range_work, 0, wargs);
+		queues_started++;
 
 		if (wargs->end_ag >= mp->m_sb.sb_agcount)
 			break;
@@ -995,7 +997,7 @@ do_inode_prefetch(
 	/*
 	 * wait for workers to complete
 	 */
-	while (i--)
+	for (i = 0; i < queues_started; i++)
 		destroy_work_queue(&queues[i]);
 	free(queues);
 }

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#1 lore raw
Quoted 53 lines · Eric Sandeen

This fixes a regression caused by:

97b1fcf xfs_repair: fix array overrun in do_inode_prefetch

The thread creation loop has 2 ways to exit; either via the loop counter based on thread_count, or the break statement if we've started enough workers to cover all AGs.

Whether or not the loop counter "i" reflects the number of threads started depends on whether or not we exited via the break.

The above commit prevented us from indexing off the end of the queues[] array if we actually advanced "i" all the way to thread_count, but in the case where we break, "i" is one *less* than the nr of threads started, so we don't wait for completion of all threads, and all hell breaks loose in phase 5.

Just stop with the cleverness of re-using the loop counter - instead, explicitly count threads that we start, and then use that counter to wait for each worker to complete.

---

I have one fs which demonstrates the problem, and have verified the regression & tested the fix against that.

I'll run this over xfstests overnight, but it seems obvious from here (OTOH the other fix seemed obvious too) :(

repair/prefetch.c +2 -0
index e47a48e..4c32395 100644
@@ -944,6 +944,7 @@ do_inode_prefetch(
 	int			i;
 	struct work_queue	queue;
 	struct work_queue	*queues;
+	int			queues_started = 0;
 
 	/*
 	 * If the previous phases of repair have not overflowed the buffer
@@ -987,6 +988,7 @@ do_inode_prefetch(
 
 		create_work_queue(&queues[i], mp, 1);
 		queue_work(&queues[i], prefetch_ag_range_work, 0, wargs);
+		queues_started++;
 
 		if (wargs->end_ag >= mp->m_sb.sb_agcount)
 			break;

repair/prefetch.c do_inode_prefetch( · line 1001

Quoted 6 lines
repair/prefetch.c +1 -1
@@ -995,7 +997,7 @@ do_inode_prefetch(
 	/*
 	 * wait for workers to complete
 	 */
-	while (i--)
+	for (i = 0; i < queues_started; i++)
 		destroy_work_queue(&queues[i]);

Fix looks good, but any reason to reverse the order of the destroy loop?

Brian

Quoted 7 lines
repair/prefetch.c +0 -0
 	free(queues);
 }

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

#2 lore raw
Quoted 51 lines · Brian Foster
Quoted 50 lines · Eric Sandeen

This fixes a regression caused by:

97b1fcf xfs_repair: fix array overrun in do_inode_prefetch

The thread creation loop has 2 ways to exit; either via the loop counter based on thread_count, or the break statement if we've started enough workers to cover all AGs.

Whether or not the loop counter "i" reflects the number of threads started depends on whether or not we exited via the break.

The above commit prevented us from indexing off the end of the queues[] array if we actually advanced "i" all the way to thread_count, but in the case where we break, "i" is one *less* than the nr of threads started, so we don't wait for completion of all threads, and all hell breaks loose in phase 5.

Just stop with the cleverness of re-using the loop counter - instead, explicitly count threads that we start, and then use that counter to wait for each worker to complete.

---

I have one fs which demonstrates the problem, and have verified the regression & tested the fix against that.

I'll run this over xfstests overnight, but it seems obvious from here (OTOH the other fix seemed obvious too) :(

repair/prefetch.c +2 -0
index e47a48e..4c32395 100644
@@ -944,6 +944,7 @@ do_inode_prefetch(
 	int			i;
 	struct work_queue	queue;
 	struct work_queue	*queues;
+	int			queues_started = 0;
 
 	/*
 	 * If the previous phases of repair have not overflowed the buffer
@@ -987,6 +988,7 @@ do_inode_prefetch(
 
 		create_work_queue(&queues[i], mp, 1);
 		queue_work(&queues[i], prefetch_ag_range_work, 0, wargs);
+		queues_started++;
 
Quoted 11 lines

repair/prefetch.c do_inode_prefetch( · line 1001

Quoted 9 lines
repair/prefetch.c +1 -1
 		if (wargs->end_ag >= mp->m_sb.sb_agcount)
 			break;
@@ -995,7 +997,7 @@ do_inode_prefetch(
 	/*
 	 * wait for workers to complete
 	 */
-	while (i--)
+	for (i = 0; i < queues_started; i++)
 		destroy_work_queue(&queues[i]);

Fix looks good, but any reason to reverse the order of the destroy loop?

simplicity? :)

I don't think it matters operationally...

-Eric

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

Reviewed-by
#3 lore raw
Quoted 64 lines · Eric Sandeen
Quoted 51 lines · Brian Foster
Quoted 50 lines · Eric Sandeen

This fixes a regression caused by:

97b1fcf xfs_repair: fix array overrun in do_inode_prefetch

The thread creation loop has 2 ways to exit; either via the loop counter based on thread_count, or the break statement if we've started enough workers to cover all AGs.

Whether or not the loop counter "i" reflects the number of threads started depends on whether or not we exited via the break.

The above commit prevented us from indexing off the end of the queues[] array if we actually advanced "i" all the way to thread_count, but in the case where we break, "i" is one *less* than the nr of threads started, so we don't wait for completion of all threads, and all hell breaks loose in phase 5.

Just stop with the cleverness of re-using the loop counter - instead, explicitly count threads that we start, and then use that counter to wait for each worker to complete.

---

I have one fs which demonstrates the problem, and have verified the regression & tested the fix against that.

I'll run this over xfstests overnight, but it seems obvious from here (OTOH the other fix seemed obvious too) :(

repair/prefetch.c +2 -0
index e47a48e..4c32395 100644
@@ -944,6 +944,7 @@ do_inode_prefetch(
 	int			i;
 	struct work_queue	queue;
 	struct work_queue	*queues;
+	int			queues_started = 0;
 
 	/*
 	 * If the previous phases of repair have not overflowed the buffer
@@ -987,6 +988,7 @@ do_inode_prefetch(
 
 		create_work_queue(&queues[i], mp, 1);
 		queue_work(&queues[i], prefetch_ag_range_work, 0, wargs);
+		queues_started++;
 
Quoted 11 lines

repair/prefetch.c do_inode_prefetch( · line 1001

Quoted 9 lines
repair/prefetch.c +1 -1
 		if (wargs->end_ag >= mp->m_sb.sb_agcount)
 			break;
@@ -995,7 +997,7 @@ do_inode_prefetch(
 	/*
 	 * wait for workers to complete
 	 */
-	while (i--)
+	for (i = 0; i < queues_started; i++)
 		destroy_work_queue(&queues[i]);

Fix looks good, but any reason to reverse the order of the destroy loop?

Quoted 2 lines

simplicity? :)

Fine by me. :)

Quoted 9 lines

I don't think it matters operationally...

-Eric

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs