We can't call the error handler while holding the CACHE_MUTEX (see previous commit, "libext2fs: unix_io: fix_potential error path deadlock in reuse_cache()" for details), so first try to write out all of the dirty blocks in the cache, and then for those where we had errors, then call the error handler. Signed-off-by: Theodore Ts'o <tytso@xxxxxxx> --- lib/ext2fs/unix_io.c | 61 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c index 2e108a2f..353d85af 100644 --- a/lib/ext2fs/unix_io.c +++ b/lib/ext2fs/unix_io.c @@ -614,31 +614,66 @@ static errcode_t flush_cached_blocks(io_channel channel, int flags) { struct unix_cache *cache; - errcode_t retval, retval2; + errcode_t retval, retval2 = 0; int i; + int errors_found = 0; - retval2 = 0; if ((flags & FLUSH_NOLOCK) == 0) mutex_lock(data, CACHE_MTX); for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { - if (!cache->in_use) + if (!cache->in_use || !cache->dirty) continue; - - if (flags & FLUSH_INVALIDATE) - cache->in_use = 0; - - if (!cache->dirty) - continue; - retval = raw_write_blk(channel, data, - cache->block, 1, cache->buf, 0); - if (retval) + cache->block, 1, cache->buf, + RAW_WRITE_NO_HANDLER); + if (retval) { + cache->write_err = 1; + errors_found = 1; retval2 = retval; - else + } else { cache->dirty = 0; + cache->write_err = 0; + if (flags & FLUSH_INVALIDATE) + cache->in_use = 0; + } } if ((flags & FLUSH_NOLOCK) == 0) mutex_unlock(data, CACHE_MTX); +retry: + while (errors_found) { + if ((flags & FLUSH_NOLOCK) == 0) + mutex_lock(data, CACHE_MTX); + errors_found = 0; + for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { + if (!cache->in_use || !cache->write_err) + continue; + errors_found = 1; + if (cache->write_err && channel->write_error) { + char *err_buf = NULL; + unsigned long long err_block = cache->block; + + cache->dirty = 0; + cache->in_use = 0; + cache->write_err = 0; + if (io_channel_alloc_buf(channel, 0, + &err_buf)) + err_buf = NULL; + else + memcpy(err_buf, cache->buf, + channel->block_size); + mutex_unlock(data, CACHE_MTX); + (channel->write_error)(channel, err_block, + 1, err_buf, channel->block_size, -1, + retval2); + if (err_buf) + ext2fs_free_mem(&err_buf); + goto retry; + } else + cache->write_err = 0; + } + if ((flags & FLUSH_NOLOCK) == 0) + mutex_unlock(data, CACHE_MTX); + } return retval2; } #endif /* NO_IO_CACHE */ -- 2.31.0