Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dm thin: stay in out-of-data-space mode once no_space_timeout expires

This fixes an issue where running out of data space would cause the
thin-pool's metadata to become read-only. There was no reason to make
metadata read-only -- calling set_pool_mode() with PM_READ_ONLY was a
misguided way to error all queued and future write IOs. We can
accomplish the same by degrading from PM_OUT_OF_DATA_SPACE to
PM_OUT_OF_DATA_SPACE with error_if_no_space enabled.

Otherwise, the use of PM_READ_ONLY could cause a race where commit() was
started before the PM_READ_ONLY transition but dm_pool_commit_metadata()
would go on to fail because the block manager had transitioned to
read-only. The return of -EPERM from dm_pool_commit_metadata(), due to
attempting to commit while in read-only mode, caused the thin-pool to
set 'needs_check' because a metadata_operation_failed(). This needless
cascade of failures makes life for users more difficult than needed.

Reported-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>

+17 -4
+17 -4
drivers/md/dm-thin.c
··· 2282 2282 queue_delayed_work(pool->wq, &pool->waker, COMMIT_PERIOD); 2283 2283 } 2284 2284 2285 + static void notify_of_pool_mode_change_to_oods(struct pool *pool); 2286 + 2285 2287 /* 2286 2288 * We're holding onto IO to allow userland time to react. After the 2287 2289 * timeout either the pool will have been resized (and thus back in 2288 - * PM_WRITE mode), or we degrade to PM_READ_ONLY and start erroring IO. 2290 + * PM_WRITE mode), or we degrade to PM_OUT_OF_DATA_SPACE w/ error_if_no_space. 2289 2291 */ 2290 2292 static void do_no_space_timeout(struct work_struct *ws) 2291 2293 { 2292 2294 struct pool *pool = container_of(to_delayed_work(ws), struct pool, 2293 2295 no_space_timeout); 2294 2296 2295 - if (get_pool_mode(pool) == PM_OUT_OF_DATA_SPACE && !pool->pf.error_if_no_space) 2296 - set_pool_mode(pool, PM_READ_ONLY); 2297 + if (get_pool_mode(pool) == PM_OUT_OF_DATA_SPACE && !pool->pf.error_if_no_space) { 2298 + pool->pf.error_if_no_space = true; 2299 + notify_of_pool_mode_change_to_oods(pool); 2300 + error_retry_list(pool); 2301 + } 2297 2302 } 2298 2303 2299 2304 /*----------------------------------------------------------------*/ ··· 2374 2369 dm_table_event(pool->ti->table); 2375 2370 DMINFO("%s: switching pool to %s mode", 2376 2371 dm_device_name(pool->pool_md), new_mode); 2372 + } 2373 + 2374 + static void notify_of_pool_mode_change_to_oods(struct pool *pool) 2375 + { 2376 + if (!pool->pf.error_if_no_space) 2377 + notify_of_pool_mode_change(pool, "out-of-data-space (queue IO)"); 2378 + else 2379 + notify_of_pool_mode_change(pool, "out-of-data-space (error IO)"); 2377 2380 } 2378 2381 2379 2382 static bool passdown_enabled(struct pool_c *pt) ··· 2468 2455 * frequently seeing this mode. 2469 2456 */ 2470 2457 if (old_mode != new_mode) 2471 - notify_of_pool_mode_change(pool, "out-of-data-space"); 2458 + notify_of_pool_mode_change_to_oods(pool); 2472 2459 pool->process_bio = process_bio_read_only; 2473 2460 pool->process_discard = process_discard_bio; 2474 2461 pool->process_cell = process_cell_read_only;