BFS: Keep references to vnodes during asynchronous I/O.

IORequest notifications begin by notifying the "finished" condition,
then invoking the request callback, then invoking the parent callback.

vfs_{read|write}_pages wait on the "finished" condition and then
return at once, potentially releasing their references to the vnode
in question, before the request callback is even invoked. The request
callback is what (eventually) invokes iterative_io_finished_hook,
which meant that we were accessing the Inode object after our reference
to it had already been released.

We can't really change IORequest notification order, as any one of the
notifications could delete the request, and indeed the first one does here.
So the solution is just to acquire another reference to the vnode and
release it in the finished hook.

Fixes #19122 and #8405.
This commit is contained in:
Augustin Cavalier 2024-11-20 18:03:09 -05:00
parent 138d92635d
commit 551a9b9a01

View File

@ -113,6 +113,7 @@ iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
{
Inode* inode = (Inode*)cookie;
rw_lock_read_unlock(&inode->Lock());
put_vnode(inode->GetVolume()->FSVolume(), inode->ID());
return B_OK;
}
@ -522,6 +523,12 @@ bfs_io(fs_volume* _volume, fs_vnode* _node, void* _cookie, io_request* request)
// We lock the node here and will unlock it in the "finished" hook.
rw_lock_read_lock(&inode->Lock());
// Due to how I/O request notifications work, it is possible that
// some other thread could be notified that the request completed
// before we have a chance to release the read lock. We thus need
// our own reference to the vnode.
acquire_vnode(_volume, inode->ID());
return do_iterative_fd_io(volume->Device(), request,
iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
}