diff options
Diffstat (limited to 'source3/smbd/aio.c')
-rw-r--r-- | source3/smbd/aio.c | 1124 |
1 files changed, 512 insertions, 612 deletions
diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c index b0b90c0dfe..3f553ebcfb 100644 --- a/source3/smbd/aio.c +++ b/source3/smbd/aio.c @@ -22,89 +22,35 @@ #include "smbd/smbd.h" #include "smbd/globals.h" #include "../lib/util/tevent_ntstatus.h" - -#if defined(WITH_AIO) - -/* The signal we'll use to signify aio done. */ -#ifndef RT_SIGNAL_AIO -#define RT_SIGNAL_AIO (SIGRTMIN+3) -#endif - -#ifndef HAVE_STRUCT_SIGEVENT_SIGEV_VALUE_SIVAL_PTR -#ifdef HAVE_STRUCT_SIGEVENT_SIGEV_VALUE_SIGVAL_PTR -#define sival_int sigval_int -#define sival_ptr sigval_ptr -#endif -#endif +#include "../lib/util/tevent_unix.h" +#include "lib/tevent_wait.h" /**************************************************************************** The buffer we keep around whilst an aio request is in process. *****************************************************************************/ struct aio_extra { - struct aio_extra *next, *prev; - SMB_STRUCT_AIOCB acb; files_struct *fsp; struct smb_request *smbreq; DATA_BLOB outbuf; struct lock_struct lock; + size_t nbyte; + off_t offset; bool write_through; - int (*handle_completion)(struct aio_extra *ex, int errcode); }; /**************************************************************************** - Initialize the signal handler for aio read/write. + Accessor function to return write_through state. *****************************************************************************/ -static void smbd_aio_signal_handler(struct tevent_context *ev_ctx, - struct tevent_signal *se, - int signum, int count, - void *_info, void *private_data) -{ - siginfo_t *info = (siginfo_t *)_info; - struct aio_extra *aio_ex = (struct aio_extra *) - info->si_value.sival_ptr; - - smbd_aio_complete_aio_ex(aio_ex); - TALLOC_FREE(aio_ex); -} - - -static bool initialize_async_io_handler(void) +bool aio_write_through_requested(struct aio_extra *aio_ex) { - static bool tried_signal_setup = false; - - if (aio_signal_event) { - return true; - } - if (tried_signal_setup) { - return false; - } - tried_signal_setup = true; - - aio_signal_event = tevent_add_signal(smbd_event_context(), - smbd_event_context(), - RT_SIGNAL_AIO, SA_SIGINFO, - smbd_aio_signal_handler, - NULL); - if (!aio_signal_event) { - DEBUG(10, ("Failed to setup RT_SIGNAL_AIO handler\n")); - return false; - } - - /* tevent supports 100 signal with SA_SIGINFO */ - aio_pending_size = 100; - return true; + return aio_ex->write_through; } -static int handle_aio_read_complete(struct aio_extra *aio_ex, int errcode); -static int handle_aio_write_complete(struct aio_extra *aio_ex, int errcode); -static int handle_aio_smb2_read_complete(struct aio_extra *aio_ex, int errcode); -static int handle_aio_smb2_write_complete(struct aio_extra *aio_ex, int errcode); - static int aio_extra_destructor(struct aio_extra *aio_ex) { - DLIST_REMOVE(aio_list_head, aio_ex); + outstanding_aio_calls--; return 0; } @@ -117,7 +63,7 @@ static struct aio_extra *create_aio_extra(TALLOC_CTX *mem_ctx, files_struct *fsp, size_t buflen) { - struct aio_extra *aio_ex = TALLOC_ZERO_P(mem_ctx, struct aio_extra); + struct aio_extra *aio_ex = talloc_zero(mem_ctx, struct aio_extra); if (!aio_ex) { return NULL; @@ -134,31 +80,89 @@ static struct aio_extra *create_aio_extra(TALLOC_CTX *mem_ctx, return NULL; } } - DLIST_ADD(aio_list_head, aio_ex); talloc_set_destructor(aio_ex, aio_extra_destructor); aio_ex->fsp = fsp; + outstanding_aio_calls++; return aio_ex; } +struct aio_req_fsp_link { + files_struct *fsp; + struct tevent_req *req; +}; + +static int aio_del_req_from_fsp(struct aio_req_fsp_link *lnk) +{ + unsigned i; + files_struct *fsp = lnk->fsp; + struct tevent_req *req = lnk->req; + + for (i=0; i<fsp->num_aio_requests; i++) { + if (fsp->aio_requests[i] == req) { + break; + } + } + if (i == fsp->num_aio_requests) { + DEBUG(1, ("req %p not found in fsp %p\n", req, fsp)); + return 0; + } + fsp->num_aio_requests -= 1; + fsp->aio_requests[i] = fsp->aio_requests[fsp->num_aio_requests]; + + if (fsp->num_aio_requests == 0) { + tevent_wait_done(fsp->deferred_close); + } + return 0; +} + +static bool aio_add_req_to_fsp(files_struct *fsp, struct tevent_req *req) +{ + size_t array_len; + struct aio_req_fsp_link *lnk; + + lnk = talloc(req, struct aio_req_fsp_link); + if (lnk == NULL) { + return false; + } + + array_len = talloc_array_length(fsp->aio_requests); + if (array_len <= fsp->num_aio_requests) { + struct tevent_req **tmp; + + tmp = talloc_realloc( + fsp, fsp->aio_requests, struct tevent_req *, + fsp->num_aio_requests+1); + if (tmp == NULL) { + TALLOC_FREE(lnk); + return false; + } + fsp->aio_requests = tmp; + } + fsp->aio_requests[fsp->num_aio_requests] = req; + fsp->num_aio_requests += 1; + + lnk->fsp = fsp; + lnk->req = req; + talloc_set_destructor(lnk, aio_del_req_from_fsp); + + return true; +} + +static void aio_pread_smb1_done(struct tevent_req *req); + /**************************************************************************** Set up an aio request from a SMBreadX call. *****************************************************************************/ NTSTATUS schedule_aio_read_and_X(connection_struct *conn, struct smb_request *smbreq, - files_struct *fsp, SMB_OFF_T startpos, + files_struct *fsp, off_t startpos, size_t smb_maxcnt) { struct aio_extra *aio_ex; - SMB_STRUCT_AIOCB *a; size_t bufsize; size_t min_aio_read_size = lp_aio_read_size(SNUM(conn)); - int ret; - - /* Ensure aio is initialized. */ - if (!initialize_async_io_handler()) { - return NT_STATUS_RETRY; - } + struct tevent_req *req; if (fsp->base_fsp != NULL) { /* No AIO on streams yet */ @@ -198,7 +202,6 @@ NTSTATUS schedule_aio_read_and_X(connection_struct *conn, DEBUG(10,("schedule_aio_read_and_X: malloc fail.\n")); return NT_STATUS_NO_MEMORY; } - aio_ex->handle_completion = handle_aio_read_complete; construct_reply_common_req(smbreq, (char *)aio_ex->outbuf.data); srv_set_message((char *)aio_ex->outbuf.data, 12, 0, True); @@ -214,28 +217,28 @@ NTSTATUS schedule_aio_read_and_X(connection_struct *conn, return NT_STATUS_FILE_LOCK_CONFLICT; } - a = &aio_ex->acb; - - /* Now set up the aio record for the read call. */ + aio_ex->nbyte = smb_maxcnt; + aio_ex->offset = startpos; - a->aio_fildes = fsp->fh->fd; - a->aio_buf = smb_buf(aio_ex->outbuf.data); - a->aio_nbytes = smb_maxcnt; - a->aio_offset = startpos; - a->aio_sigevent.sigev_notify = SIGEV_SIGNAL; - a->aio_sigevent.sigev_signo = RT_SIGNAL_AIO; - a->aio_sigevent.sigev_value.sival_ptr = aio_ex; - - ret = SMB_VFS_AIO_READ(fsp, a); - if (ret == -1) { + req = SMB_VFS_PREAD_SEND(aio_ex, fsp->conn->sconn->ev_ctx, + fsp, smb_buf(aio_ex->outbuf.data), + smb_maxcnt, startpos); + if (req == NULL) { DEBUG(0,("schedule_aio_read_and_X: aio_read failed. " "Error %s\n", strerror(errno) )); SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock); TALLOC_FREE(aio_ex); return NT_STATUS_RETRY; } + tevent_req_set_callback(req, aio_pread_smb1_done, aio_ex); + + if (!aio_add_req_to_fsp(fsp, req)) { + DEBUG(1, ("Could not add req to fsp\n")); + SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock); + TALLOC_FREE(aio_ex); + return NT_STATUS_RETRY; + } - outstanding_aio_calls++; aio_ex->smbreq = talloc_move(aio_ex, &smbreq); DEBUG(10,("schedule_aio_read_and_X: scheduled aio_read for file %s, " @@ -246,26 +249,183 @@ NTSTATUS schedule_aio_read_and_X(connection_struct *conn, return NT_STATUS_OK; } +static void aio_pread_smb1_done(struct tevent_req *req) +{ + struct aio_extra *aio_ex = tevent_req_callback_data( + req, struct aio_extra); + files_struct *fsp = aio_ex->fsp; + int outsize; + char *outbuf = (char *)aio_ex->outbuf.data; + char *data = smb_buf(outbuf); + ssize_t nread; + int err; + + nread = SMB_VFS_PREAD_RECV(req, &err); + TALLOC_FREE(req); + + DEBUG(10, ("pread_recv returned %d, err = %s\n", (int)nread, + (nread == -1) ? strerror(err) : "no error")); + + if (fsp == NULL) { + DEBUG( 3, ("aio_pread_smb1_done: file closed whilst " + "aio outstanding (mid[%llu]).\n", + (unsigned long long)aio_ex->smbreq->mid)); + TALLOC_FREE(aio_ex); + return; + } + + /* Unlock now we're done. */ + SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &aio_ex->lock); + + if (nread < 0) { + DEBUG( 3, ("handle_aio_read_complete: file %s nread == %d. " + "Error = %s\n", fsp_str_dbg(fsp), (int)nread, + strerror(err))); + + ERROR_NT(map_nt_error_from_unix(err)); + outsize = srv_set_message(outbuf,0,0,true); + } else { + outsize = srv_set_message(outbuf, 12, nread, False); + SSVAL(outbuf,smb_vwv2, 0xFFFF); /* Remaining - must be * -1. */ + SSVAL(outbuf,smb_vwv5, nread); + SSVAL(outbuf,smb_vwv6, smb_offset(data,outbuf)); + SSVAL(outbuf,smb_vwv7, ((nread >> 16) & 1)); + SSVAL(smb_buf(outbuf), -2, nread); + + aio_ex->fsp->fh->pos = aio_ex->offset + nread; + aio_ex->fsp->fh->position_information = aio_ex->fsp->fh->pos; + + DEBUG( 3, ("handle_aio_read_complete file %s max=%d " + "nread=%d\n", fsp_str_dbg(fsp), + (int)aio_ex->nbyte, (int)nread ) ); + + } + smb_setlen(outbuf, outsize - 4); + show_msg(outbuf); + if (!srv_send_smb(aio_ex->smbreq->sconn, outbuf, + true, aio_ex->smbreq->seqnum+1, + IS_CONN_ENCRYPTED(fsp->conn), NULL)) { + exit_server_cleanly("handle_aio_read_complete: srv_send_smb " + "failed."); + } + + DEBUG(10, ("handle_aio_read_complete: scheduled aio_read completed " + "for file %s, offset %.0f, len = %u\n", + fsp_str_dbg(fsp), (double)aio_ex->offset, + (unsigned int)nread)); + + TALLOC_FREE(aio_ex); +} + +struct pwrite_fsync_state { + struct tevent_context *ev; + files_struct *fsp; + bool write_through; + ssize_t nwritten; +}; + +static void pwrite_fsync_write_done(struct tevent_req *subreq); +static void pwrite_fsync_sync_done(struct tevent_req *subreq); + +static struct tevent_req *pwrite_fsync_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct files_struct *fsp, + const void *data, + size_t n, off_t offset, + bool write_through) +{ + struct tevent_req *req, *subreq; + struct pwrite_fsync_state *state; + + req = tevent_req_create(mem_ctx, &state, struct pwrite_fsync_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->fsp = fsp; + state->write_through = write_through; + + subreq = SMB_VFS_PWRITE_SEND(state, ev, fsp, data, n, offset); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, pwrite_fsync_write_done, req); + return req; +} + +static void pwrite_fsync_write_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct pwrite_fsync_state *state = tevent_req_data( + req, struct pwrite_fsync_state); + connection_struct *conn = state->fsp->conn; + int err; + bool do_sync; + + state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &err); + TALLOC_FREE(subreq); + if (state->nwritten == -1) { + tevent_req_error(req, err); + return; + } + + do_sync = (lp_strict_sync(SNUM(conn)) && + (lp_syncalways(SNUM(conn)) || state->write_through)); + if (!do_sync) { + tevent_req_done(req); + return; + } + + subreq = SMB_VFS_FSYNC_SEND(state, state->ev, state->fsp); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, pwrite_fsync_sync_done, req); +} + +static void pwrite_fsync_sync_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + int ret, err; + + ret = SMB_VFS_FSYNC_RECV(subreq, &err); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_error(req, err); + return; + } + tevent_req_done(req); +} + +static ssize_t pwrite_fsync_recv(struct tevent_req *req, int *perr) +{ + struct pwrite_fsync_state *state = tevent_req_data( + req, struct pwrite_fsync_state); + + if (tevent_req_is_unix_error(req, perr)) { + return -1; + } + return state->nwritten; +} + +static void aio_pwrite_smb1_done(struct tevent_req *req); + /**************************************************************************** Set up an aio request from a SMBwriteX call. *****************************************************************************/ NTSTATUS schedule_aio_write_and_X(connection_struct *conn, struct smb_request *smbreq, - files_struct *fsp, char *data, - SMB_OFF_T startpos, + files_struct *fsp, const char *data, + off_t startpos, size_t numtowrite) { struct aio_extra *aio_ex; - SMB_STRUCT_AIOCB *a; size_t bufsize; size_t min_aio_write_size = lp_aio_write_size(SNUM(conn)); - int ret; - - /* Ensure aio is initialized. */ - if (!initialize_async_io_handler()) { - return NT_STATUS_RETRY; - } + struct tevent_req *req; if (fsp->base_fsp != NULL) { /* No AIO on streams yet */ @@ -308,7 +468,6 @@ NTSTATUS schedule_aio_write_and_X(connection_struct *conn, DEBUG(0,("schedule_aio_write_and_X: malloc fail.\n")); return NT_STATUS_NO_MEMORY; } - aio_ex->handle_completion = handle_aio_write_complete; aio_ex->write_through = BITSETW(smbreq->vwv+7,0); construct_reply_common_req(smbreq, (char *)aio_ex->outbuf.data); @@ -325,28 +484,28 @@ NTSTATUS schedule_aio_write_and_X(connection_struct *conn, return NT_STATUS_FILE_LOCK_CONFLICT; } - a = &aio_ex->acb; - - /* Now set up the aio record for the write call. */ + aio_ex->nbyte = numtowrite; + aio_ex->offset = startpos; - a->aio_fildes = fsp->fh->fd; - a->aio_buf = data; - a->aio_nbytes = numtowrite; - a->aio_offset = startpos; - a->aio_sigevent.sigev_notify = SIGEV_SIGNAL; - a->aio_sigevent.sigev_signo = RT_SIGNAL_AIO; - a->aio_sigevent.sigev_value.sival_ptr = aio_ex; - - ret = SMB_VFS_AIO_WRITE(fsp, a); - if (ret == -1) { + req = pwrite_fsync_send(aio_ex, fsp->conn->sconn->ev_ctx, fsp, + data, numtowrite, startpos, + aio_ex->write_through); + if (req == NULL) { DEBUG(3,("schedule_aio_wrote_and_X: aio_write failed. " "Error %s\n", strerror(errno) )); SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock); TALLOC_FREE(aio_ex); return NT_STATUS_RETRY; } + tevent_req_set_callback(req, aio_pwrite_smb1_done, aio_ex); + + if (!aio_add_req_to_fsp(fsp, req)) { + DEBUG(1, ("Could not add req to fsp\n")); + SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock); + TALLOC_FREE(aio_ex); + return NT_STATUS_RETRY; + } - outstanding_aio_calls++; aio_ex->smbreq = talloc_move(aio_ex, &smbreq); /* This should actually be improved to span the write. */ @@ -381,6 +540,132 @@ NTSTATUS schedule_aio_write_and_X(connection_struct *conn, return NT_STATUS_OK; } +static void aio_pwrite_smb1_done(struct tevent_req *req) +{ + struct aio_extra *aio_ex = tevent_req_callback_data( + req, struct aio_extra); + files_struct *fsp = aio_ex->fsp; + char *outbuf = (char *)aio_ex->outbuf.data; + ssize_t numtowrite = aio_ex->nbyte; + ssize_t nwritten; + int err; + + nwritten = pwrite_fsync_recv(req, &err); + TALLOC_FREE(req); + + DEBUG(10, ("pwrite_recv returned %d, err = %s\n", (int)nwritten, + (nwritten == -1) ? strerror(err) : "no error")); + + if (fsp == NULL) { + DEBUG( 3, ("aio_pwrite_smb1_done: file closed whilst " + "aio outstanding (mid[%llu]).\n", + (unsigned long long)aio_ex->smbreq->mid)); + TALLOC_FREE(aio_ex); + return; + } + + /* Unlock now we're done. */ + SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &aio_ex->lock); + + mark_file_modified(fsp); + + if (fsp->aio_write_behind) { + + if (nwritten != numtowrite) { + if (nwritten == -1) { + DEBUG(5,("handle_aio_write_complete: " + "aio_write_behind failed ! File %s " + "is corrupt ! Error %s\n", + fsp_str_dbg(fsp), strerror(err))); + } else { + DEBUG(0,("handle_aio_write_complete: " + "aio_write_behind failed ! File %s " + "is corrupt ! Wanted %u bytes but " + "only wrote %d\n", fsp_str_dbg(fsp), + (unsigned int)numtowrite, + (int)nwritten )); + } + } else { + DEBUG(10,("handle_aio_write_complete: " + "aio_write_behind completed for file %s\n", + fsp_str_dbg(fsp))); + } + /* TODO: should no return success in case of an error !!! */ + TALLOC_FREE(aio_ex); + return; + } + + /* We don't need outsize or set_message here as we've already set the + fixed size length when we set up the aio call. */ + + if (nwritten == -1) { + DEBUG(3, ("handle_aio_write: file %s wanted %u bytes. " + "nwritten == %d. Error = %s\n", + fsp_str_dbg(fsp), (unsigned int)numtowrite, + (int)nwritten, strerror(err))); + + ERROR_NT(map_nt_error_from_unix(err)); + srv_set_message(outbuf,0,0,true); + } else { + SSVAL(outbuf,smb_vwv2,nwritten); + SSVAL(outbuf,smb_vwv4,(nwritten>>16)&1); + if (nwritten < (ssize_t)numtowrite) { + SCVAL(outbuf,smb_rcls,ERRHRD); + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + DEBUG(3,("handle_aio_write: %s, num=%d wrote=%d\n", + fsp_fnum_dbg(fsp), (int)numtowrite, (int)nwritten)); + + aio_ex->fsp->fh->pos = aio_ex->offset + nwritten; + } + + show_msg(outbuf); + if (!srv_send_smb(aio_ex->smbreq->sconn, outbuf, + true, aio_ex->smbreq->seqnum+1, + IS_CONN_ENCRYPTED(fsp->conn), + NULL)) { + exit_server_cleanly("handle_aio_write_complete: " + "srv_send_smb failed."); + } + + DEBUG(10, ("handle_aio_write_complete: scheduled aio_write completed " + "for file %s, offset %.0f, requested %u, written = %u\n", + fsp_str_dbg(fsp), (double)aio_ex->offset, + (unsigned int)numtowrite, (unsigned int)nwritten)); + + TALLOC_FREE(aio_ex); +} + +bool cancel_smb2_aio(struct smb_request *smbreq) +{ + struct smbd_smb2_request *smb2req = smbreq->smb2req; + struct aio_extra *aio_ex = NULL; + + if (smb2req) { + aio_ex = talloc_get_type(smbreq->async_priv, + struct aio_extra); + } + + if (aio_ex == NULL) { + return false; + } + + if (aio_ex->fsp == NULL) { + return false; + } + + /* + * We let the aio request run. Setting fsp to NULL has the + * effect that the _done routines don't send anything out. + */ + + aio_ex->fsp = NULL; + return true; +} + +static void aio_pread_smb2_done(struct tevent_req *req); + /**************************************************************************** Set up an aio request from a SMB2 read call. *****************************************************************************/ @@ -390,18 +675,12 @@ NTSTATUS schedule_smb2_aio_read(connection_struct *conn, files_struct *fsp, TALLOC_CTX *ctx, DATA_BLOB *preadbuf, - SMB_OFF_T startpos, + off_t startpos, size_t smb_maxcnt) { struct aio_extra *aio_ex; - SMB_STRUCT_AIOCB *a; size_t min_aio_read_size = lp_aio_read_size(SNUM(conn)); - int ret; - - /* Ensure aio is initialized. */ - if (!initialize_async_io_handler()) { - return NT_STATUS_RETRY; - } + struct tevent_req *req; if (fsp->base_fsp != NULL) { /* No AIO on streams yet */ @@ -440,7 +719,6 @@ NTSTATUS schedule_smb2_aio_read(connection_struct *conn, if (!(aio_ex = create_aio_extra(smbreq->smb2req, fsp, 0))) { return NT_STATUS_NO_MEMORY; } - aio_ex->handle_completion = handle_aio_smb2_read_complete; init_strict_lock_struct(fsp, (uint64_t)smbreq->smbpid, (uint64_t)startpos, (uint64_t)smb_maxcnt, READ_LOCK, @@ -452,31 +730,31 @@ NTSTATUS schedule_smb2_aio_read(connection_struct *conn, return NT_STATUS_FILE_LOCK_CONFLICT; } - a = &aio_ex->acb; - - /* Now set up the aio record for the read call. */ + aio_ex->nbyte = smb_maxcnt; + aio_ex->offset = startpos; - a->aio_fildes = fsp->fh->fd; - a->aio_buf = preadbuf->data; - a->aio_nbytes = smb_maxcnt; - a->aio_offset = startpos; - a->aio_sigevent.sigev_notify = SIGEV_SIGNAL; - a->aio_sigevent.sigev_signo = RT_SIGNAL_AIO; - a->aio_sigevent.sigev_value.sival_ptr = aio_ex; + req = SMB_VFS_PREAD_SEND(aio_ex, fsp->conn->sconn->ev_ctx, fsp, + preadbuf->data, smb_maxcnt, startpos); + if (req == NULL) { + DEBUG(0, ("smb2: SMB_VFS_PREAD_SEND failed. " + "Error %s\n", strerror(errno))); + SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock); + TALLOC_FREE(aio_ex); + return NT_STATUS_RETRY; + } + tevent_req_set_callback(req, aio_pread_smb2_done, aio_ex); - ret = SMB_VFS_AIO_READ(fsp, a); - if (ret == -1) { - DEBUG(0,("smb2: aio_read failed. " - "Error %s\n", strerror(errno) )); + if (!aio_add_req_to_fsp(fsp, req)) { + DEBUG(1, ("Could not add req to fsp\n")); SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock); TALLOC_FREE(aio_ex); return NT_STATUS_RETRY; } - outstanding_aio_calls++; /* We don't need talloc_move here as both aio_ex and * smbreq are children of smbreq->smb2req. */ aio_ex->smbreq = smbreq; + smbreq->async_priv = aio_ex; DEBUG(10,("smb2: scheduled aio_read for file %s, " "offset %.0f, len = %u (mid = %u)\n", @@ -486,6 +764,60 @@ NTSTATUS schedule_smb2_aio_read(connection_struct *conn, return NT_STATUS_OK; } +static void aio_pread_smb2_done(struct tevent_req *req) +{ + struct aio_extra *aio_ex = tevent_req_callback_data( + req, struct aio_extra); + struct tevent_req *subreq = aio_ex->smbreq->smb2req->subreq; + files_struct *fsp = aio_ex->fsp; + NTSTATUS status; + ssize_t nread; + int err = 0; + + nread = SMB_VFS_PREAD_RECV(req, &err); + TALLOC_FREE(req); + + DEBUG(10, ("pread_recv returned %d, err = %s\n", (int)nread, + (nread == -1) ? strerror(err) : "no error")); + + if (fsp == NULL) { + DEBUG( 3, ("aio_pread_smb2_done: file closed whilst " + "aio outstanding (mid[%llu]).\n", + (unsigned long long)aio_ex->smbreq->mid)); + TALLOC_FREE(aio_ex); + return; + } + + /* Unlock now we're done. */ + SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &aio_ex->lock); + + /* Common error or success code processing for async or sync + read returns. */ + + status = smb2_read_complete(subreq, nread, err); + + if (nread > 0) { + fsp->fh->pos = aio_ex->offset + nread; + fsp->fh->position_information = fsp->fh->pos; + } + + DEBUG(10, ("smb2: scheduled aio_read completed " + "for file %s, offset %.0f, len = %u " + "(errcode = %d, NTSTATUS = %s)\n", + fsp_str_dbg(aio_ex->fsp), + (double)aio_ex->offset, + (unsigned int)nread, + err, nt_errstr(status))); + + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(subreq, status); + return; + } + tevent_req_done(subreq); +} + +static void aio_pwrite_smb2_done(struct tevent_req *req); + /**************************************************************************** Set up an aio request from a SMB2write call. *****************************************************************************/ @@ -498,14 +830,8 @@ NTSTATUS schedule_aio_smb2_write(connection_struct *conn, bool write_through) { struct aio_extra *aio_ex = NULL; - SMB_STRUCT_AIOCB *a = NULL; size_t min_aio_write_size = lp_aio_write_size(SNUM(conn)); - int ret; - - /* Ensure aio is initialized. */ - if (!initialize_async_io_handler()) { - return NT_STATUS_RETRY; - } + struct tevent_req *req; if (fsp->base_fsp != NULL) { /* No AIO on streams yet */ @@ -539,7 +865,6 @@ NTSTATUS schedule_aio_smb2_write(connection_struct *conn, return NT_STATUS_NO_MEMORY; } - aio_ex->handle_completion = handle_aio_smb2_write_complete; aio_ex->write_through = write_through; init_strict_lock_struct(fsp, (uint64_t)smbreq->smbpid, @@ -552,31 +877,32 @@ NTSTATUS schedule_aio_smb2_write(connection_struct *conn, return NT_STATUS_FILE_LOCK_CONFLICT; } - a = &aio_ex->acb; - - /* Now set up the aio record for the write call. */ + aio_ex->nbyte = in_data.length; + aio_ex->offset = in_offset; - a->aio_fildes = fsp->fh->fd; - a->aio_buf = in_data.data; - a->aio_nbytes = in_data.length; - a->aio_offset = in_offset; - a->aio_sigevent.sigev_notify = SIGEV_SIGNAL; - a->aio_sigevent.sigev_signo = RT_SIGNAL_AIO; - a->aio_sigevent.sigev_value.sival_ptr = aio_ex; + req = pwrite_fsync_send(aio_ex, fsp->conn->sconn->ev_ctx, fsp, + in_data.data, in_data.length, in_offset, + write_through); + if (req == NULL) { + DEBUG(3, ("smb2: SMB_VFS_PWRITE_SEND failed. " + "Error %s\n", strerror(errno))); + SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock); + TALLOC_FREE(aio_ex); + return NT_STATUS_RETRY; + } + tevent_req_set_callback(req, aio_pwrite_smb2_done, aio_ex); - ret = SMB_VFS_AIO_WRITE(fsp, a); - if (ret == -1) { - DEBUG(3,("smb2: aio_write failed. " - "Error %s\n", strerror(errno) )); + if (!aio_add_req_to_fsp(fsp, req)) { + DEBUG(1, ("Could not add req to fsp\n")); SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock); TALLOC_FREE(aio_ex); return NT_STATUS_RETRY; } - outstanding_aio_calls++; /* We don't need talloc_move here as both aio_ex and * smbreq are children of smbreq->smb2req. */ aio_ex->smbreq = smbreq; + smbreq->async_priv = aio_ex; /* This should actually be improved to span the write. */ contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_WRITE); @@ -600,474 +926,48 @@ NTSTATUS schedule_aio_smb2_write(connection_struct *conn, return NT_STATUS_OK; } -/**************************************************************************** - Complete the read and return the data or error back to the client. - Returns errno or zero if all ok. -*****************************************************************************/ - -static int handle_aio_read_complete(struct aio_extra *aio_ex, int errcode) -{ - int outsize; - char *outbuf = (char *)aio_ex->outbuf.data; - char *data = smb_buf(outbuf); - ssize_t nread = SMB_VFS_AIO_RETURN(aio_ex->fsp,&aio_ex->acb); - - if (nread < 0) { - /* We're relying here on the fact that if the fd is - closed then the aio will complete and aio_return - will return an error. Hopefully this is - true.... JRA. */ - - DEBUG( 3,( "handle_aio_read_complete: file %s nread == %d. " - "Error = %s\n", - fsp_str_dbg(aio_ex->fsp), (int)nread, strerror(errcode))); - - ERROR_NT(map_nt_error_from_unix(errcode)); - outsize = srv_set_message(outbuf,0,0,true); - } else { - outsize = srv_set_message(outbuf,12,nread,False); - SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be * -1. */ - SSVAL(outbuf,smb_vwv5,nread); - SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); - SSVAL(outbuf,smb_vwv7,((nread >> 16) & 1)); - SSVAL(smb_buf(outbuf),-2,nread); - - aio_ex->fsp->fh->pos = aio_ex->acb.aio_offset + nread; - aio_ex->fsp->fh->position_information = aio_ex->fsp->fh->pos; - - DEBUG( 3, ( "handle_aio_read_complete file %s max=%d " - "nread=%d\n", - fsp_str_dbg(aio_ex->fsp), - (int)aio_ex->acb.aio_nbytes, (int)nread ) ); - - } - smb_setlen(outbuf,outsize - 4); - show_msg(outbuf); - if (!srv_send_smb(aio_ex->smbreq->sconn, outbuf, - true, aio_ex->smbreq->seqnum+1, - IS_CONN_ENCRYPTED(aio_ex->fsp->conn), NULL)) { - exit_server_cleanly("handle_aio_read_complete: srv_send_smb " - "failed."); - } - - DEBUG(10,("handle_aio_read_complete: scheduled aio_read completed " - "for file %s, offset %.0f, len = %u\n", - fsp_str_dbg(aio_ex->fsp), (double)aio_ex->acb.aio_offset, - (unsigned int)nread )); - - return errcode; -} - -/**************************************************************************** - Complete the write and return the data or error back to the client. - Returns error code or zero if all ok. -*****************************************************************************/ - -static int handle_aio_write_complete(struct aio_extra *aio_ex, int errcode) +static void aio_pwrite_smb2_done(struct tevent_req *req) { - files_struct *fsp = aio_ex->fsp; - char *outbuf = (char *)aio_ex->outbuf.data; - ssize_t numtowrite = aio_ex->acb.aio_nbytes; - ssize_t nwritten = SMB_VFS_AIO_RETURN(fsp,&aio_ex->acb); - - if (fsp->aio_write_behind) { - if (nwritten != numtowrite) { - if (nwritten == -1) { - DEBUG(5,("handle_aio_write_complete: " - "aio_write_behind failed ! File %s " - "is corrupt ! Error %s\n", - fsp_str_dbg(fsp), strerror(errcode))); - } else { - DEBUG(0,("handle_aio_write_complete: " - "aio_write_behind failed ! File %s " - "is corrupt ! Wanted %u bytes but " - "only wrote %d\n", fsp_str_dbg(fsp), - (unsigned int)numtowrite, - (int)nwritten )); - errcode = EIO; - } - } else { - DEBUG(10,("handle_aio_write_complete: " - "aio_write_behind completed for file %s\n", - fsp_str_dbg(fsp))); - } - /* TODO: should no return 0 in case of an error !!! */ - return 0; - } - - /* We don't need outsize or set_message here as we've already set the - fixed size length when we set up the aio call. */ - - if(nwritten == -1) { - DEBUG( 3,( "handle_aio_write: file %s wanted %u bytes. " - "nwritten == %d. Error = %s\n", - fsp_str_dbg(fsp), (unsigned int)numtowrite, - (int)nwritten, strerror(errcode) )); - - ERROR_NT(map_nt_error_from_unix(errcode)); - srv_set_message(outbuf,0,0,true); - } else { - NTSTATUS status; - - SSVAL(outbuf,smb_vwv2,nwritten); - SSVAL(outbuf,smb_vwv4,(nwritten>>16)&1); - if (nwritten < (ssize_t)numtowrite) { - SCVAL(outbuf,smb_rcls,ERRHRD); - SSVAL(outbuf,smb_err,ERRdiskfull); - } - - DEBUG(3,("handle_aio_write: fnum=%d num=%d wrote=%d\n", - fsp->fnum, (int)numtowrite, (int)nwritten)); - status = sync_file(fsp->conn,fsp, aio_ex->write_through); - if (!NT_STATUS_IS_OK(status)) { - errcode = errno; - ERROR_BOTH(map_nt_error_from_unix(errcode), - ERRHRD, ERRdiskfull); - srv_set_message(outbuf,0,0,true); - DEBUG(5,("handle_aio_write: sync_file for %s returned %s\n", - fsp_str_dbg(fsp), nt_errstr(status))); - } - - aio_ex->fsp->fh->pos = aio_ex->acb.aio_offset + nwritten; - } - - show_msg(outbuf); - if (!srv_send_smb(aio_ex->smbreq->sconn, outbuf, - true, aio_ex->smbreq->seqnum+1, - IS_CONN_ENCRYPTED(fsp->conn), - NULL)) { - exit_server_cleanly("handle_aio_write_complete: " - "srv_send_smb failed."); - } - - DEBUG(10,("handle_aio_write_complete: scheduled aio_write completed " - "for file %s, offset %.0f, requested %u, written = %u\n", - fsp_str_dbg(fsp), (double)aio_ex->acb.aio_offset, - (unsigned int)numtowrite, (unsigned int)nwritten )); - - return errcode; -} - -/**************************************************************************** - Complete the read and return the data or error back to the client. - Returns errno or zero if all ok. -*****************************************************************************/ - -static int handle_aio_smb2_read_complete(struct aio_extra *aio_ex, int errcode) -{ - NTSTATUS status; + struct aio_extra *aio_ex = tevent_req_callback_data( + req, struct aio_extra); + ssize_t numtowrite = aio_ex->nbyte; struct tevent_req *subreq = aio_ex->smbreq->smb2req->subreq; - ssize_t nread = SMB_VFS_AIO_RETURN(aio_ex->fsp,&aio_ex->acb); - - /* Common error or success code processing for async or sync - read returns. */ - - status = smb2_read_complete(subreq, nread, errcode); - - if (nread > 0) { - aio_ex->fsp->fh->pos = aio_ex->acb.aio_offset + nread; - aio_ex->fsp->fh->position_information = aio_ex->fsp->fh->pos; - } - - DEBUG(10,("smb2: scheduled aio_read completed " - "for file %s, offset %.0f, len = %u " - "(errcode = %d, NTSTATUS = %s)\n", - fsp_str_dbg(aio_ex->fsp), - (double)aio_ex->acb.aio_offset, - (unsigned int)nread, - errcode, - nt_errstr(status) )); - - if (!NT_STATUS_IS_OK(status)) { - tevent_req_nterror(subreq, status); - return errcode; - } - - tevent_req_done(subreq); - return errcode; -} - -/**************************************************************************** - Complete the SMB2 write and return the data or error back to the client. - Returns error code or zero if all ok. -*****************************************************************************/ - -static int handle_aio_smb2_write_complete(struct aio_extra *aio_ex, int errcode) -{ files_struct *fsp = aio_ex->fsp; - ssize_t numtowrite = aio_ex->acb.aio_nbytes; - ssize_t nwritten = SMB_VFS_AIO_RETURN(fsp,&aio_ex->acb); - struct tevent_req *subreq = aio_ex->smbreq->smb2req->subreq; NTSTATUS status; + ssize_t nwritten; + int err = 0; - status = smb2_write_complete(subreq, nwritten, errcode); - - DEBUG(10,("smb2: scheduled aio_write completed " - "for file %s, offset %.0f, requested %u, " - "written = %u (errcode = %d, NTSTATUS = %s)\n", - fsp_str_dbg(fsp), - (double)aio_ex->acb.aio_offset, - (unsigned int)numtowrite, - (unsigned int)nwritten, - errcode, - nt_errstr(status) )); - - if (!NT_STATUS_IS_OK(status)) { - tevent_req_nterror(subreq, status); - return errcode; - } - - tevent_req_done(subreq); - return errcode; -} - -/**************************************************************************** - Handle any aio completion. Returns True if finished (and sets *perr if err - was non-zero), False if not. -*****************************************************************************/ - -static bool handle_aio_completed(struct aio_extra *aio_ex, int *perr) -{ - files_struct *fsp = NULL; - int err; + nwritten = pwrite_fsync_recv(req, &err); + TALLOC_FREE(req); - if(!aio_ex) { - DEBUG(3, ("handle_aio_completed: Non-existing aio_ex passed\n")); - return false; - } + DEBUG(10, ("pwrite_recv returned %d, err = %s\n", (int)nwritten, + (nwritten == -1) ? strerror(err) : "no error")); - fsp = aio_ex->fsp; - - /* Ensure the operation has really completed. */ - err = SMB_VFS_AIO_ERROR(fsp, &aio_ex->acb); - if (err == EINPROGRESS) { - DEBUG(10,( "handle_aio_completed: operation mid %llu still in " - "process for file %s\n", - (unsigned long long)aio_ex->smbreq->mid, - fsp_str_dbg(aio_ex->fsp))); - return False; + if (fsp == NULL) { + DEBUG( 3, ("aio_pwrite_smb2_done: file closed whilst " + "aio outstanding (mid[%llu]).\n", + (unsigned long long)aio_ex->smbreq->mid)); + TALLOC_FREE(aio_ex); + return; } /* Unlock now we're done. */ SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &aio_ex->lock); - if (err == ECANCELED) { - /* If error is ECANCELED then don't return anything to the - * client. */ - DEBUG(10,( "handle_aio_completed: operation mid %llu" - " canceled\n", - (unsigned long long)aio_ex->smbreq->mid)); - return True; - } - - err = aio_ex->handle_completion(aio_ex, err); - if (err) { - *perr = err; /* Only save non-zero errors. */ - } - - return True; -} - -/**************************************************************************** - Handle any aio completion inline. -*****************************************************************************/ - -void smbd_aio_complete_aio_ex(struct aio_extra *aio_ex) -{ - files_struct *fsp = NULL; - int ret = 0; - - outstanding_aio_calls--; - - DEBUG(10,("smbd_aio_complete_mid: mid[%llu]\n", - (unsigned long long)aio_ex->smbreq->mid)); + status = smb2_write_complete_nosync(subreq, nwritten, err); - fsp = aio_ex->fsp; - if (fsp == NULL) { - /* file was closed whilst I/O was outstanding. Just - * ignore. */ - DEBUG( 3,( "smbd_aio_complete_mid: file closed whilst " - "aio outstanding (mid[%llu]).\n", - (unsigned long long)aio_ex->smbreq->mid)); - return; - } + DEBUG(10, ("smb2: scheduled aio_write completed " + "for file %s, offset %.0f, requested %u, " + "written = %u (errcode = %d, NTSTATUS = %s)\n", + fsp_str_dbg(fsp), + (double)aio_ex->offset, + (unsigned int)numtowrite, + (unsigned int)nwritten, + err, nt_errstr(status))); - if (!handle_aio_completed(aio_ex, &ret)) { + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(subreq, status); return; } + tevent_req_done(subreq); } - -/**************************************************************************** - We're doing write behind and the client closed the file. Wait up to 30 - seconds (my arbitrary choice) for the aio to complete. Return 0 if all writes - completed, errno to return if not. -*****************************************************************************/ - -#define SMB_TIME_FOR_AIO_COMPLETE_WAIT 29 - -int wait_for_aio_completion(files_struct *fsp) -{ - struct aio_extra *aio_ex; - const SMB_STRUCT_AIOCB **aiocb_list; - int aio_completion_count = 0; - time_t start_time = time_mono(NULL); - int seconds_left; - - for (seconds_left = SMB_TIME_FOR_AIO_COMPLETE_WAIT; - seconds_left >= 0;) { - int err = 0; - int i; - struct timespec ts; - - aio_completion_count = 0; - for( aio_ex = aio_list_head; aio_ex; aio_ex = aio_ex->next) { - if (aio_ex->fsp == fsp) { - aio_completion_count++; - } - } - - if (!aio_completion_count) { - return 0; - } - - DEBUG(3,("wait_for_aio_completion: waiting for %d aio events " - "to complete.\n", aio_completion_count )); - - aiocb_list = SMB_MALLOC_ARRAY(const SMB_STRUCT_AIOCB *, - aio_completion_count); - if (!aiocb_list) { - return ENOMEM; - } - - for( i = 0, aio_ex = aio_list_head; - aio_ex; - aio_ex = aio_ex->next) { - if (aio_ex->fsp == fsp) { - aiocb_list[i++] = &aio_ex->acb; - } - } - - /* Now wait up to seconds_left for completion. */ - ts.tv_sec = seconds_left; - ts.tv_nsec = 0; - - DEBUG(10,("wait_for_aio_completion: %d events, doing a wait " - "of %d seconds.\n", - aio_completion_count, seconds_left )); - - err = SMB_VFS_AIO_SUSPEND(fsp, aiocb_list, - aio_completion_count, &ts); - - DEBUG(10,("wait_for_aio_completion: returned err = %d, " - "errno = %s\n", err, strerror(errno) )); - - if (err == -1 && errno == EAGAIN) { - DEBUG(0,("wait_for_aio_completion: aio_suspend timed " - "out waiting for %d events after a wait of " - "%d seconds\n", aio_completion_count, - seconds_left)); - /* Timeout. */ - cancel_aio_by_fsp(fsp); - SAFE_FREE(aiocb_list); - return EIO; - } - - /* One or more events might have completed - process them if - * so. */ - for( i = 0; i < aio_completion_count; i++) { - aio_ex = (struct aio_extra *)aiocb_list[i]->aio_sigevent.sigev_value.sival_ptr; - - if (!handle_aio_completed(aio_ex, &err)) { - continue; - } - TALLOC_FREE(aio_ex); - } - - SAFE_FREE(aiocb_list); - seconds_left = SMB_TIME_FOR_AIO_COMPLETE_WAIT - - (time_mono(NULL) - start_time); - } - - /* We timed out - we don't know why. Return ret if already an error, - * else EIO. */ - DEBUG(10,("wait_for_aio_completion: aio_suspend timed out waiting " - "for %d events\n", - aio_completion_count)); - - return EIO; -} - -/**************************************************************************** - Cancel any outstanding aio requests. The client doesn't care about the reply. -*****************************************************************************/ - -void cancel_aio_by_fsp(files_struct *fsp) -{ - struct aio_extra *aio_ex; - - for( aio_ex = aio_list_head; aio_ex; aio_ex = aio_ex->next) { - if (aio_ex->fsp == fsp) { - /* Unlock now we're done. */ - SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &aio_ex->lock); - - /* Don't delete the aio_extra record as we may have - completed and don't yet know it. Just do the - aio_cancel call and return. */ - SMB_VFS_AIO_CANCEL(fsp, &aio_ex->acb); - aio_ex->fsp = NULL; /* fsp will be closed when we - * return. */ - } - } -} - -#else -NTSTATUS schedule_aio_read_and_X(connection_struct *conn, - struct smb_request *smbreq, - files_struct *fsp, SMB_OFF_T startpos, - size_t smb_maxcnt) -{ - return NT_STATUS_RETRY; -} - -NTSTATUS schedule_aio_write_and_X(connection_struct *conn, - struct smb_request *smbreq, - files_struct *fsp, char *data, - SMB_OFF_T startpos, - size_t numtowrite) -{ - return NT_STATUS_RETRY; -} - -NTSTATUS schedule_smb2_aio_read(connection_struct *conn, - struct smb_request *smbreq, - files_struct *fsp, - TALLOC_CTX *ctx, - DATA_BLOB *preadbuf, - SMB_OFF_T startpos, - size_t smb_maxcnt) -{ - return NT_STATUS_RETRY; -} - -NTSTATUS schedule_aio_smb2_write(connection_struct *conn, - struct smb_request *smbreq, - files_struct *fsp, - uint64_t in_offset, - DATA_BLOB in_data, - bool write_through) -{ - return NT_STATUS_RETRY; -} - -void cancel_aio_by_fsp(files_struct *fsp) -{ -} - -int wait_for_aio_completion(files_struct *fsp) -{ - return 0; -} - -void smbd_aio_complete_mid(uint64_t mid); - -#endif |