build: Remove special case for the build farm
[samba.git] / source3 / modules / vfs_aio_fork.c
index 266e3645960ac8f62484ad64e96496eaa6c52649..811d44e6bbe11059e15c3174fb1e8512997a5013 100644 (file)
 #include "system/shmem.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
-#include <aio.h>
+#include "lib/async_req/async_sock.h"
+#include "lib/util/tevent_unix.h"
+
+#undef recvmsg
 
 #ifndef MAP_FILE
 #define MAP_FILE 0
@@ -78,10 +81,37 @@ fail:
        return NULL;
 }
 
+enum cmd_type {
+       READ_CMD,
+       WRITE_CMD,
+       FSYNC_CMD
+};
+
+static const char *cmd_type_str(enum cmd_type cmd)
+{
+       const char *result;
+
+       switch (cmd) {
+       case READ_CMD:
+               result = "READ";
+               break;
+       case WRITE_CMD:
+               result = "WRITE";
+               break;
+       case FSYNC_CMD:
+               result = "FSYNC";
+               break;
+       default:
+               result = "<UNKNOWN>";
+               break;
+       }
+       return result;
+}
+
 struct rw_cmd {
        size_t n;
        off_t offset;
-       bool read_cmd;
+       enum cmd_type cmd;
 };
 
 struct rw_ret {
@@ -94,17 +124,11 @@ struct aio_child_list;
 struct aio_child {
        struct aio_child *prev, *next;
        struct aio_child_list *list;
-       SMB_STRUCT_AIOCB *aiocb;
        pid_t pid;
        int sockfd;
-       struct fd_event *sock_event;
-       struct rw_ret retval;
-       struct mmap_area *map;  /* ==NULL means write request */
+       struct mmap_area *map;
        bool dont_delete;       /* Marked as in use since last cleanup */
-       bool cancelled;
-       bool read_cmd;
-       bool called_from_suspend;
-       bool completion_done;
+       bool busy;
 };
 
 struct aio_child_list {
@@ -241,7 +265,7 @@ static void aio_child_cleanup(struct event_context *event_ctx,
        for (child = list->children; child != NULL; child = next) {
                next = child->next;
 
-               if (child->aiocb != NULL) {
+               if (child->busy) {
                        DEBUG(10, ("child %d currently active\n",
                                   (int)child->pid));
                        continue;
@@ -328,13 +352,13 @@ static void aio_child_loop(int sockfd, struct mmap_area *map)
                }
 
                DEBUG(10, ("aio_child_loop: %s %d bytes at %d from fd %d\n",
-                          cmd_struct.read_cmd ? "read" : "write",
+                          cmd_type_str(cmd_struct.cmd),
                           (int)cmd_struct.n, (int)cmd_struct.offset, fd));
 
-#ifdef ENABLE_BUILD_FARM_HACKS
+#ifdef DEVELOPER
                {
                        /*
-                        * In the build farm, we want erratic behaviour for
+                        * For developer testing, we want erratic behaviour for
                         * async I/O times
                         */
                        uint8_t randval;
@@ -353,21 +377,29 @@ static void aio_child_loop(int sockfd, struct mmap_area *map)
 
                ZERO_STRUCT(ret_struct);
 
-               if (cmd_struct.read_cmd) {
+               switch (cmd_struct.cmd) {
+               case READ_CMD:
                        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
+#ifdef DEVELOPER
                        ret_struct.size = MAX(1, ret_struct.size * 0.9);
 #endif
 #endif
-               }
-               else {
+                       break;
+               case WRITE_CMD:
                        ret_struct.size = sys_pwrite(
                                fd, (void *)map->ptr, cmd_struct.n,
                                cmd_struct.offset);
+                       break;
+               case FSYNC_CMD:
+                       ret_struct.size = fsync(fd);
+                       break;
+               default:
+                       ret_struct.size = -1;
+                       errno = EINVAL;
                }
 
                DEBUG(10, ("aio_child_loop: syscall returned %d\n",
@@ -395,62 +427,11 @@ static void aio_child_loop(int sockfd, struct mmap_area *map)
        }
 }
 
-static void handle_aio_completion(struct event_context *event_ctx,
-                                 struct fd_event *event, uint16 flags,
-                                 void *p)
-{
-       struct aio_extra *aio_ex = NULL;
-       struct aio_child *child = (struct aio_child *)p;
-       NTSTATUS status;
-
-       DEBUG(10, ("handle_aio_completion called with flags=%d\n", flags));
-
-       if ((flags & EVENT_FD_READ) == 0) {
-               return;
-       }
-
-       status = read_data(child->sockfd, (char *)&child->retval,
-                          sizeof(child->retval));
-
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("aio child %d died: %s\n", (int)child->pid,
-                         nt_errstr(status)));
-               child->retval.size = -1;
-               child->retval.ret_errno = EIO;
-       }
-
-       if (child->aiocb == NULL) {
-               DEBUG(1, ("Inactive child died\n"));
-               TALLOC_FREE(child);
-               return;
-       }
-
-       if (child->cancelled) {
-               child->aiocb = NULL;
-               child->cancelled = false;
-               return;
-       }
-
-       if (child->read_cmd && (child->retval.size > 0)) {
-               SMB_ASSERT(child->retval.size <= child->aiocb->aio_nbytes);
-               memcpy((void *)child->aiocb->aio_buf, (void *)child->map->ptr,
-                      child->retval.size);
-       }
-
-       if (child->called_from_suspend) {
-               child->completion_done = true;
-               return;
-       }
-       aio_ex = (struct aio_extra *)child->aiocb->aio_sigevent.sigev_value.sival_ptr;
-       smbd_aio_complete_aio_ex(aio_ex);
-       TALLOC_FREE(aio_ex);
-}
-
 static int aio_child_destructor(struct aio_child *child)
 {
        char c=0;
 
-       SMB_ASSERT((child->aiocb == NULL) || child->cancelled);
+       SMB_ASSERT(!child->busy);
 
        DEBUG(10, ("aio_child_destructor: removing child %d on fd %d\n",
                        child->pid, child->sockfd));
@@ -531,16 +512,6 @@ static int create_aio_child(struct smbd_server_connection *sconn,
        result->sockfd = fdpair[0];
        close(fdpair[1]);
 
-       result->sock_event = event_add_fd(server_event_context(), result,
-                                         result->sockfd, EVENT_FD_READ,
-                                         handle_aio_completion,
-                                         result);
-       if (result->sock_event == NULL) {
-               ret = ENOMEM;
-               DEBUG(0, ("event_add_fd failed\n"));
-               goto fail;
-       }
-
        result->list = children;
        DLIST_ADD(children->children, result);
 
@@ -558,21 +529,19 @@ static int create_aio_child(struct smbd_server_connection *sconn,
        return ret;
 }
 
-static NTSTATUS get_idle_child(struct vfs_handle_struct *handle,
-                              struct aio_child **pchild)
+static int get_idle_child(struct vfs_handle_struct *handle,
+                         struct aio_child **pchild)
 {
        struct aio_child_list *children;
        struct aio_child *child;
-       NTSTATUS status;
 
        children = init_aio_children(handle);
        if (children == NULL) {
-               return NT_STATUS_NO_MEMORY;
+               return ENOMEM;
        }
 
        for (child = children->children; child != NULL; child = child->next) {
-               if (child->aiocb == NULL) {
-                       /* idle */
+               if (!child->busy) {
                        break;
                }
        }
@@ -587,315 +556,341 @@ static NTSTATUS get_idle_child(struct vfs_handle_struct *handle,
                if (ret != 0) {
                        DEBUG(10, ("create_aio_child failed: %s\n",
                                   strerror(errno)));
-                       return map_nt_error_from_unix(errno);
+                       return ret;
                }
        }
 
        child->dont_delete = true;
+       child->busy = true;
 
        *pchild = child;
-       return NT_STATUS_OK;
+       return 0;
 }
 
-static int aio_fork_read(struct vfs_handle_struct *handle,
-                        struct files_struct *fsp, SMB_STRUCT_AIOCB *aiocb)
-{
+struct aio_fork_pread_state {
        struct aio_child *child;
-       struct rw_cmd cmd;
        ssize_t ret;
-       NTSTATUS status;
+       int err;
+};
 
-       if (aiocb->aio_nbytes > 128*1024) {
-               /* TODO: support variable buffers */
-               errno = EINVAL;
-               return -1;
+static void aio_fork_pread_done(struct tevent_req *subreq);
+
+static struct tevent_req *aio_fork_pread_send(struct vfs_handle_struct *handle,
+                                             TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct files_struct *fsp,
+                                             void *data,
+                                             size_t n, off_t offset)
+{
+       struct tevent_req *req, *subreq;
+       struct aio_fork_pread_state *state;
+       struct rw_cmd cmd;
+       ssize_t written;
+       int err;
+
+       req = tevent_req_create(mem_ctx, &state, struct aio_fork_pread_state);
+       if (req == NULL) {
+               return NULL;
        }
 
-       status = get_idle_child(handle, &child);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("Could not get an idle child\n"));
-               return -1;
+       if (n > 128*1024) {
+               /* TODO: support variable buffers */
+               tevent_req_error(req, EINVAL);
+               return tevent_req_post(req, ev);
        }
 
-       child->read_cmd = true;
-       child->aiocb = aiocb;
-       child->retval.ret_errno = EINPROGRESS;
+       err = get_idle_child(handle, &state->child);
+       if (err != 0) {
+               tevent_req_error(req, err);
+               return tevent_req_post(req, ev);
+       }
 
        ZERO_STRUCT(cmd);
-       cmd.n = aiocb->aio_nbytes;
-       cmd.offset = aiocb->aio_offset;
-       cmd.read_cmd = child->read_cmd;
+       cmd.n = n;
+       cmd.offset = offset;
+       cmd.cmd = READ_CMD;
 
        DEBUG(10, ("sending fd %d to child %d\n", fsp->fh->fd,
-                  (int)child->pid));
+                  (int)state->child->pid));
 
-       ret = write_fd(child->sockfd, &cmd, sizeof(cmd), fsp->fh->fd);
-       if (ret == -1) {
-               DEBUG(10, ("write_fd failed: %s\n", strerror(errno)));
-               return -1;
+       /*
+        * Not making this async. We're writing into an empty unix
+        * domain socket. This should never block.
+        */
+       written = write_fd(state->child->sockfd, &cmd, sizeof(cmd),
+                          fsp->fh->fd);
+       if (written == -1) {
+               err = errno;
+
+               TALLOC_FREE(state->child);
+
+               DEBUG(10, ("write_fd failed: %s\n", strerror(err)));
+               tevent_req_error(req, err);
+               return tevent_req_post(req, ev);
        }
 
-       return 0;
+       subreq = read_packet_send(state, ev, state->child->sockfd,
+                                 sizeof(struct rw_ret), NULL, NULL);
+       if (tevent_req_nomem(subreq, req)) {
+               TALLOC_FREE(state->child); /* we sent sth down */
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, aio_fork_pread_done, req);
+       return req;
 }
 
-static int aio_fork_write(struct vfs_handle_struct *handle,
-                         struct files_struct *fsp, SMB_STRUCT_AIOCB *aiocb)
+static void aio_fork_pread_done(struct tevent_req *subreq)
 {
-       struct aio_child *child;
-       struct rw_cmd cmd;
-       ssize_t ret;
-       NTSTATUS status;
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct aio_fork_pread_state *state = tevent_req_data(
+               req, struct aio_fork_pread_state);
+       ssize_t nread;
+       uint8_t *buf;
+       int err;
+       struct rw_ret *retbuf;
 
-       if (aiocb->aio_nbytes > 128*1024) {
-               /* TODO: support variable buffers */
-               errno = EINVAL;
-               return -1;
+       nread = read_packet_recv(subreq, talloc_tos(), &buf, &err);
+       TALLOC_FREE(subreq);
+       if (nread == -1) {
+               TALLOC_FREE(state->child);
+               tevent_req_error(req, err);
+               return;
        }
 
-       status = get_idle_child(handle, &child);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("Could not get an idle child\n"));
+       state->child->busy = false;
+
+       retbuf = (struct rw_ret *)buf;
+       state->ret = retbuf->size;
+       state->err = retbuf->ret_errno;
+       tevent_req_done(req);
+}
+
+static ssize_t aio_fork_pread_recv(struct tevent_req *req, int *err)
+{
+       struct aio_fork_pread_state *state = tevent_req_data(
+               req, struct aio_fork_pread_state);
+
+       if (tevent_req_is_unix_error(req, err)) {
                return -1;
        }
+       if (state->ret == -1) {
+               *err = state->err;
+       }
+       return state->ret;
+}
+
+struct aio_fork_pwrite_state {
+       struct aio_child *child;
+       ssize_t ret;
+       int err;
+};
 
-       child->read_cmd = false;
-       child->aiocb = aiocb;
-       child->retval.ret_errno = EINPROGRESS;
+static void aio_fork_pwrite_done(struct tevent_req *subreq);
 
-       memcpy((void *)child->map->ptr, (void *)aiocb->aio_buf,
-              aiocb->aio_nbytes);
+static struct tevent_req *aio_fork_pwrite_send(
+       struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev, struct files_struct *fsp,
+       const void *data, size_t n, off_t offset)
+{
+       struct tevent_req *req, *subreq;
+       struct aio_fork_pwrite_state *state;
+       struct rw_cmd cmd;
+       ssize_t written;
+       int err;
+
+       req = tevent_req_create(mem_ctx, &state, struct aio_fork_pwrite_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (n > 128*1024) {
+               /* TODO: support variable buffers */
+               tevent_req_error(req, EINVAL);
+               return tevent_req_post(req, ev);
+       }
+
+       err = get_idle_child(handle, &state->child);
+       if (err != 0) {
+               tevent_req_error(req, err);
+               return tevent_req_post(req, ev);
+       }
 
        ZERO_STRUCT(cmd);
-       cmd.n = aiocb->aio_nbytes;
-       cmd.offset = aiocb->aio_offset;
-       cmd.read_cmd = child->read_cmd;
+       cmd.n = n;
+       cmd.offset = offset;
+       cmd.cmd = WRITE_CMD;
 
        DEBUG(10, ("sending fd %d to child %d\n", fsp->fh->fd,
-                  (int)child->pid));
+                  (int)state->child->pid));
 
-       ret = write_fd(child->sockfd, &cmd, sizeof(cmd), fsp->fh->fd);
-       if (ret == -1) {
-               DEBUG(10, ("write_fd failed: %s\n", strerror(errno)));
-               return -1;
+       /*
+        * Not making this async. We're writing into an empty unix
+        * domain socket. This should never block.
+        */
+       written = write_fd(state->child->sockfd, &cmd, sizeof(cmd),
+                          fsp->fh->fd);
+       if (written == -1) {
+               err = errno;
+
+               TALLOC_FREE(state->child);
+
+               DEBUG(10, ("write_fd failed: %s\n", strerror(err)));
+               tevent_req_error(req, err);
+               return tevent_req_post(req, ev);
        }
 
-       return 0;
+       subreq = read_packet_send(state, ev, state->child->sockfd,
+                                 sizeof(struct rw_ret), NULL, NULL);
+       if (tevent_req_nomem(subreq, req)) {
+               TALLOC_FREE(state->child); /* we sent sth down */
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, aio_fork_pwrite_done, req);
+       return req;
 }
 
-static struct aio_child *aio_fork_find_child(struct vfs_handle_struct *handle,
-                                            SMB_STRUCT_AIOCB *aiocb)
+static void aio_fork_pwrite_done(struct tevent_req *subreq)
 {
-       struct aio_child_list *children;
-       struct aio_child *child;
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct aio_fork_pwrite_state *state = tevent_req_data(
+               req, struct aio_fork_pwrite_state);
+       ssize_t nread;
+       uint8_t *buf;
+       int err;
+       struct rw_ret *retbuf;
 
-       children = init_aio_children(handle);
-       if (children == NULL) {
-               return NULL;
+       nread = read_packet_recv(subreq, talloc_tos(), &buf, &err);
+       TALLOC_FREE(subreq);
+       if (nread == -1) {
+               TALLOC_FREE(state->child);
+               tevent_req_error(req, err);
+               return;
        }
 
-       for (child = children->children; child != NULL; child = child->next) {
-               if (child->aiocb == aiocb) {
-                       return child;
-               }
-       }
+       state->child->busy = false;
 
-       return NULL;
+       retbuf = (struct rw_ret *)buf;
+       state->ret = retbuf->size;
+       state->err = retbuf->ret_errno;
+       tevent_req_done(req);
 }
 
-static ssize_t aio_fork_return_fn(struct vfs_handle_struct *handle,
-                                 struct files_struct *fsp,
-                                 SMB_STRUCT_AIOCB *aiocb)
+static ssize_t aio_fork_pwrite_recv(struct tevent_req *req, int *err)
 {
-       struct aio_child *child = aio_fork_find_child(handle, aiocb);
+       struct aio_fork_pwrite_state *state = tevent_req_data(
+               req, struct aio_fork_pwrite_state);
 
-       if (child == NULL) {
-               errno = EINVAL;
-               DEBUG(0, ("returning EINVAL\n"));
+       if (tevent_req_is_unix_error(req, err)) {
                return -1;
        }
-
-       child->aiocb = NULL;
-
-       if (child->cancelled) {
-               errno = ECANCELED;
-               return -1;
+       if (state->ret == -1) {
+               *err = state->err;
        }
+       return state->ret;
+}
 
-       if (child->retval.size == -1) {
-               errno = child->retval.ret_errno;
-       }
+struct aio_fork_fsync_state {
+       struct aio_child *child;
+       ssize_t ret;
+       int err;
+};
 
-       return child->retval.size;
-}
+static void aio_fork_fsync_done(struct tevent_req *subreq);
 
-static int aio_fork_cancel(struct vfs_handle_struct *handle,
-                          struct files_struct *fsp,
-                          SMB_STRUCT_AIOCB *aiocb)
+static struct tevent_req *aio_fork_fsync_send(
+       struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev, struct files_struct *fsp)
 {
-       struct aio_child_list *children;
-       struct aio_child *child;
+       struct tevent_req *req, *subreq;
+       struct aio_fork_fsync_state *state;
+       struct rw_cmd cmd;
+       ssize_t written;
+       int err;
 
-       children = init_aio_children(handle);
-       if (children == NULL) {
-               errno = EINVAL;
-               return -1;
+       req = tevent_req_create(mem_ctx, &state, struct aio_fork_fsync_state);
+       if (req == NULL) {
+               return NULL;
        }
 
-       for (child = children->children; child != NULL; child = child->next) {
-               if (child->aiocb == NULL) {
-                       continue;
-               }
-               if (child->aiocb->aio_fildes != fsp->fh->fd) {
-                       continue;
-               }
-               if ((aiocb != NULL) && (child->aiocb != aiocb)) {
-                       continue;
-               }
+       err = get_idle_child(handle, &state->child);
+       if (err != 0) {
+               tevent_req_error(req, err);
+               return tevent_req_post(req, ev);
+       }
 
-               /*
-                * We let the child do its job, but we discard the result when
-                * it's finished.
-                */
+       ZERO_STRUCT(cmd);
+       cmd.cmd = FSYNC_CMD;
 
-               child->cancelled = true;
-       }
+       DEBUG(10, ("sending fd %d to child %d\n", fsp->fh->fd,
+                  (int)state->child->pid));
 
-       return AIO_CANCELED;
-}
+       /*
+        * Not making this async. We're writing into an empty unix
+        * domain socket. This should never block.
+        */
+       written = write_fd(state->child->sockfd, &cmd, sizeof(cmd),
+                          fsp->fh->fd);
+       if (written == -1) {
+               err = errno;
 
-static int aio_fork_error_fn(struct vfs_handle_struct *handle,
-                            struct files_struct *fsp,
-                            SMB_STRUCT_AIOCB *aiocb)
-{
-       struct aio_child *child = aio_fork_find_child(handle, aiocb);
+               TALLOC_FREE(state->child);
 
-       if (child == NULL) {
-               errno = EINVAL;
-               return -1;
+               DEBUG(10, ("write_fd failed: %s\n", strerror(err)));
+               tevent_req_error(req, err);
+               return tevent_req_post(req, ev);
        }
 
-       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;
+       subreq = read_packet_send(state, ev, state->child->sockfd,
+                                 sizeof(struct rw_ret), NULL, NULL);
+       if (tevent_req_nomem(subreq, req)) {
+               TALLOC_FREE(state->child); /* we sent sth down */
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, aio_fork_fsync_done, req);
+       return req;
 }
 
-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)
+static void aio_fork_fsync_done(struct tevent_req *subreq)
 {
-       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;
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct aio_fork_fsync_state *state = tevent_req_data(
+               req, struct aio_fork_fsync_state);
+       ssize_t nread;
+       uint8_t *buf;
        int err;
+       struct rw_ret *retbuf;
 
-       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 = convert_timespec_to_timeval(*timeout);
-               struct tevent_timer *te = tevent_add_timer(ev,
-                                               frame,
-                                               timeval_current_ofs(tv.tv_sec,
-                                                                   tv.tv_usec),
-                                               aio_fork_suspend_timed_out,
-                                               &timed_out);
-               if (!te) {
-                       errno = ENOMEM;
-                       goto out;
-               }
+       nread = read_packet_recv(subreq, talloc_tos(), &buf, &err);
+       TALLOC_FREE(subreq);
+       if (nread == -1) {
+               TALLOC_FREE(state->child);
+               tevent_req_error(req, err);
+               return;
        }
 
-       for (i = 0; i < n; i++) {
-               struct aio_child *child = NULL;
-               const SMB_STRUCT_AIOCB *aiocb = aiocb_array[i];
+       state->child->busy = false;
 
-               if (!aiocb) {
-                       continue;
-               }
+       retbuf = (struct rw_ret *)buf;
+       state->ret = retbuf->size;
+       state->err = retbuf->ret_errno;
+       tevent_req_done(req);
+}
 
-               /*
-                * 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.
-                */
+static int aio_fork_fsync_recv(struct tevent_req *req, int *err)
+{
+       struct aio_fork_fsync_state *state = tevent_req_data(
+               req, struct aio_fork_fsync_state);
 
-               for (child = children->children; child != NULL; child = child->next) {
-                       struct tevent_fd *event;
-
-                       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;
-                       }
-
-                       event = event_add_fd(ev,
-                                            frame,
-                                            child->sockfd,
-                                            EVENT_FD_READ,
-                                            handle_aio_completion,
-                                            child);
-                       if (event == NULL) {
-                               errno = ENOMEM;
-                               goto out;
-                       }
-
-                       child->called_from_suspend = true;
-
-                       while (!child->completion_done) {
-                               if (tevent_loop_once(ev) == -1) {
-                                       goto out;
-                               }
-
-                               if (timed_out) {
-                                       errno = EAGAIN;
-                                       goto out;
-                               }
-                       }
-               }
+       if (tevent_req_is_unix_error(req, err)) {
+               return -1;
        }
-
-       ret = 0;
-
-  out:
-
-       err = errno;
-       TALLOC_FREE(frame);
-       errno = err;
-       return ret;
+       if (state->ret == -1) {
+               *err = state->err;
+       }
+       return state->ret;
 }
 
 static int aio_fork_connect(vfs_handle_struct *handle, const char *service,
@@ -917,12 +912,12 @@ static int aio_fork_connect(vfs_handle_struct *handle, const char *service,
 
 static struct vfs_fn_pointers vfs_aio_fork_fns = {
        .connect_fn = aio_fork_connect,
-       .aio_read_fn = aio_fork_read,
-       .aio_write_fn = aio_fork_write,
-       .aio_return_fn = aio_fork_return_fn,
-       .aio_cancel_fn = aio_fork_cancel,
-       .aio_error_fn = aio_fork_error_fn,
-       .aio_suspend_fn = aio_fork_suspend,
+       .pread_send_fn = aio_fork_pread_send,
+       .pread_recv_fn = aio_fork_pread_recv,
+       .pwrite_send_fn = aio_fork_pwrite_send,
+       .pwrite_recv_fn = aio_fork_pwrite_recv,
+       .fsync_send_fn = aio_fork_fsync_send,
+       .fsync_recv_fn = aio_fork_fsync_recv,
 };
 
 NTSTATUS vfs_aio_fork_init(void);