diff options
author | Jeremy Allison <jra@samba.org> | 2010-06-04 21:00:24 -0700 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2010-06-04 21:00:24 -0700 |
commit | 62e0a744e23ced873241b8fd531276e8f0c870b7 (patch) | |
tree | 81a03c00a964058fd8fbdb4b0384e53c58912757 /source3 | |
parent | 79ec886aa4e8398cd45d9821030a1b87a17950f8 (diff) | |
download | samba-62e0a744e23ced873241b8fd531276e8f0c870b7.tar.gz samba-62e0a744e23ced873241b8fd531276e8f0c870b7.tar.bz2 samba-62e0a744e23ced873241b8fd531276e8f0c870b7.zip |
Fix a long-standing bug with async io that would only be triggered by SMB2.
On normal or shutdown close, ensure we wait for any pending IO to
complete before returning. Implement a blocking aio_suspend inside
vfs_aio_fork.c. These changes pass make test when the aio_fork module
is used by default on the test shares.
Jeremy.
Diffstat (limited to 'source3')
-rw-r--r-- | source3/modules/vfs_aio_fork.c | 124 | ||||
-rw-r--r-- | source3/smbd/close.c | 10 |
2 files changed, 129 insertions, 5 deletions
diff --git a/source3/modules/vfs_aio_fork.c b/source3/modules/vfs_aio_fork.c index b0262542c3..b43aad2df4 100644 --- a/source3/modules/vfs_aio_fork.c +++ b/source3/modules/vfs_aio_fork.c @@ -2,6 +2,7 @@ * Simulate the Posix AIO using mmap/fork * * Copyright (C) Volker Lendecke 2008 + * Copyright (C) Jeremy Allison 2010 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -343,9 +344,12 @@ static void aio_child_loop(int sockfd, struct mmap_area *map) ret_struct.size = sys_pread( fd, (void *)map->ptr, cmd_struct.n, cmd_struct.offset); +#if 0 +/* This breaks "make test" when run with aio_fork module. */ #ifdef ENABLE_BUILD_FARM_HACKS ret_struct.size = MAX(1, ret_struct.size * 0.9); #endif +#endif } else { ret_struct.size = sys_pwrite( @@ -723,12 +727,132 @@ static int aio_fork_error_fn(struct vfs_handle_struct *handle, return child->retval.ret_errno; } +static void aio_fork_suspend_timed_out(struct tevent_context *event_ctx, + struct tevent_timer *te, + struct timeval now, + void *private_data) +{ + bool *timed_out = (bool *)private_data; + /* Remove this timed event handler. */ + TALLOC_FREE(te); + *timed_out = true; +} + +static int aio_fork_suspend(struct vfs_handle_struct *handle, + struct files_struct *fsp, + const SMB_STRUCT_AIOCB * const aiocb_array[], + int n, + const struct timespec *timeout) +{ + struct aio_child_list *children = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev = NULL; + int i; + int ret = -1; + bool timed_out = false; + + children = init_aio_children(handle); + if (children == NULL) { + errno = EINVAL; + goto out; + } + + /* This is a blocking call, and has to use a sub-event loop. */ + ev = event_context_init(frame); + if (ev == NULL) { + errno = ENOMEM; + goto out; + } + + if (timeout) { + struct timeval tv; + struct tevent_timer *te = tevent_add_timer(ev, + frame, + timeval_current_ofs(tv.tv_sec,0), + aio_fork_suspend_timed_out, + &timed_out); + if (!te) { + errno = ENOMEM; + goto out; + } + } + + for (i = 0; i < n; i++) { + struct aio_child *child = NULL; + const SMB_STRUCT_AIOCB *aiocb = aiocb_array[i]; + + if (!aiocb) { + continue; + } + + /* + * We're going to cheat here. We know that smbd/aio.c + * only calls this when it's waiting for every single + * outstanding call to finish on a close, so just wait + * individually for each IO to complete. We don't care + * what order they finish - only that they all do. JRA. + */ + + for (child = children->children; child != NULL; child = child->next) { + if (child->aiocb == NULL) { + continue; + } + if (child->aiocb->aio_fildes != fsp->fh->fd) { + continue; + } + if (child->aiocb != aiocb) { + continue; + } + + if (child->aiocb->aio_sigevent.sigev_value.sival_ptr == NULL) { + continue; + } + + /* We're never using this event on the + * main event context again... */ + TALLOC_FREE(child->sock_event); + + child->sock_event = event_add_fd(ev, + child, + child->sockfd, + EVENT_FD_READ, + handle_aio_completion, + child); + + while (1) { + if (tevent_loop_once(ev) == -1) { + goto out; + } + + if (timed_out) { + errno = EAGAIN; + goto out; + } + + /* We set child->aiocb to NULL in our hooked + * AIO_RETURN(). */ + if (child->aiocb == NULL) { + break; + } + } + } + } + + ret = 0; + + out: + + TALLOC_FREE(frame); + return ret; +} + static struct vfs_fn_pointers vfs_aio_fork_fns = { .aio_read = aio_fork_read, .aio_write = aio_fork_write, .aio_return_fn = aio_fork_return_fn, .aio_cancel = aio_fork_cancel, .aio_error_fn = aio_fork_error_fn, + .aio_suspend = aio_fork_suspend, }; NTSTATUS vfs_aio_fork_init(void); diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 20fe9e46f1..b35d544630 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -580,9 +580,11 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp, NTSTATUS tmp; connection_struct *conn = fsp->conn; - if (fsp->aio_write_behind) { + if (close_type == ERROR_CLOSE) { + cancel_aio_by_fsp(fsp); + } else { /* - * If we're finishing write behind on a close we can get a write + * If we're finishing async io on a close we can get a write * error here, we must remember this. */ int ret = wait_for_aio_completion(fsp); @@ -590,10 +592,8 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp, status = ntstatus_keeperror( status, map_nt_error_from_unix(ret)); } - } else { - cancel_aio_by_fsp(fsp); } - + /* * If we're flushing on a close we can get a write * error here, we must remember this. |