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
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
of the aio_read call.
*****************************************************************************/
-static struct aio_extra *create_aio_ex_read(files_struct *fsp,
- size_t buflen,
- uint16 mid,
- const char *inbuf)
+static struct aio_extra *create_aio_ex_read(files_struct *fsp, size_t buflen,
+ uint16 mid)
{
struct aio_extra *aio_ex = SMB_MALLOC_P(struct aio_extra);
SAFE_FREE(aio_ex);
return NULL;
}
- /* Save the first 8 bytes of inbuf for possible enc data. */
- aio_ex->inbuf = SMB_MALLOC_ARRAY(char, 8);
- if (!aio_ex->inbuf) {
- SAFE_FREE(aio_ex->outbuf);
- SAFE_FREE(aio_ex);
- return NULL;
- }
- memcpy(aio_ex->inbuf, inbuf, 8);
DLIST_ADD(aio_list_head, aio_ex);
aio_ex->fsp = fsp;
aio_ex->read_req = True;
*****************************************************************************/
BOOL schedule_aio_read_and_X(connection_struct *conn,
- char *inbuf, char *outbuf,
- int length, int len_outbuf,
+ struct smb_request *req,
files_struct *fsp, SMB_OFF_T startpos,
size_t smb_maxcnt)
{
/* Only do this on non-chained and non-chaining reads not using the
* write cache. */
- if (chain_size !=0 || (CVAL(inbuf,smb_vwv0) != 0xFF)
+ if (chain_size !=0 || (CVAL(req->inbuf,smb_vwv0) != 0xFF)
|| (lp_write_cache_size(SNUM(conn)) != 0) ) {
return False;
}
return False;
}
- /* The following is safe from integer wrap as we've already
- checked smb_maxcnt is 128k or less. */
- bufsize = PTR_DIFF(smb_buf(outbuf),outbuf) + smb_maxcnt;
+ /* The following is safe from integer wrap as we've already checked
+ smb_maxcnt is 128k or less. Wct is 12 for read replies */
+
+ bufsize = smb_size + 12 * 2 + smb_maxcnt;
- if ((aio_ex = create_aio_ex_read(fsp, bufsize,
- SVAL(inbuf,smb_mid))) == NULL) {
+ if ((aio_ex = create_aio_ex_read(fsp, bufsize, req->mid)) == NULL) {
DEBUG(10,("schedule_aio_read_and_X: malloc fail.\n"));
return False;
}
- /* Copy the SMB header already setup in outbuf. */
- memcpy(aio_ex->outbuf, outbuf, smb_buf(outbuf) - outbuf);
+ construct_reply_common((char *)req->inbuf, aio_ex->outbuf);
+ set_message(aio_ex->outbuf, 12, 0, True);
SCVAL(aio_ex->outbuf,smb_vwv0,0xFF); /* Never a chained reply. */
a = &aio_ex->acb;
*****************************************************************************/
BOOL schedule_aio_write_and_X(connection_struct *conn,
- char *inbuf, char *outbuf,
- int length, int len_outbuf,
- files_struct *fsp, char *data,
- SMB_OFF_T startpos,
- size_t numtowrite)
+ struct smb_request *req,
+ files_struct *fsp, char *data,
+ SMB_OFF_T startpos,
+ size_t numtowrite)
{
struct aio_extra *aio_ex;
SMB_STRUCT_AIOCB *a;
size_t inbufsize, outbufsize;
+ BOOL write_through = BITSETW(req->inbuf+smb_vwv7,0);
size_t min_aio_write_size = lp_aio_write_size(SNUM(conn));
if (!min_aio_write_size || (numtowrite < min_aio_write_size)) {
/* Only do this on non-chained and non-chaining reads not using the
* write cache. */
- if (chain_size !=0 || (CVAL(inbuf,smb_vwv0) != 0xFF)
+ if (chain_size !=0 || (CVAL(req->inbuf,smb_vwv0) != 0xFF)
|| (lp_write_cache_size(SNUM(conn)) != 0) ) {
return False;
}
"(mid = %u)\n",
fsp->fsp_name, (double)startpos,
(unsigned int)numtowrite,
- (unsigned int)SVAL(inbuf,smb_mid) ));
+ (unsigned int)req->mid ));
return False;
}
- inbufsize = smb_len(inbuf) + 4;
- outbufsize = smb_len(outbuf) + 4;
+ inbufsize = smb_len(req->inbuf) + 4;
+ reply_outbuf(req, 6, 0);
+ outbufsize = smb_len(req->outbuf) + 4;
if (!(aio_ex = create_aio_ex_write(fsp, inbufsize, outbufsize,
- SVAL(inbuf,smb_mid)))) {
+ req->mid))) {
DEBUG(0,("schedule_aio_write_and_X: malloc fail.\n"));
return False;
}
/* Copy the SMB header already setup in outbuf. */
- memcpy(aio_ex->inbuf, inbuf, inbufsize);
+ memcpy(aio_ex->inbuf, req->inbuf, inbufsize);
/* Copy the SMB header already setup in outbuf. */
- memcpy(aio_ex->outbuf, outbuf, outbufsize);
+ memcpy(aio_ex->outbuf, req->outbuf, outbufsize);
+ TALLOC_FREE(req->outbuf);
SCVAL(aio_ex->outbuf,smb_vwv0,0xFF); /* Never a chained reply. */
a = &aio_ex->acb;
/* Now set up the aio record for the write call. */
a->aio_fildes = fsp->fh->fd;
- a->aio_buf = aio_ex->inbuf + (PTR_DIFF(data, inbuf));
+ a->aio_buf = aio_ex->inbuf + (PTR_DIFF(data, req->inbuf));
a->aio_nbytes = numtowrite;
a->aio_offset = startpos;
a->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
return False;
}
- srv_defer_sign_response(aio_ex->mid);
+ if (!write_through && !lp_syncalways(SNUM(fsp->conn))
+ && fsp->aio_write_behind) {
+ /* Lie to the client and immediately claim we finished the
+ * write. */
+ SSVAL(aio_ex->outbuf,smb_vwv2,numtowrite);
+ SSVAL(aio_ex->outbuf,smb_vwv4,(numtowrite>>16)&1);
+ show_msg(aio_ex->outbuf);
+ if (!send_smb(smbd_server_fd(),aio_ex->outbuf)) {
+ exit_server_cleanly("handle_aio_write: send_smb "
+ "failed.");
+ }
+ DEBUG(10,("schedule_aio_write_and_X: scheduled aio_write "
+ "behind for file %s\n", fsp->fsp_name ));
+ } else {
+ srv_defer_sign_response(aio_ex->mid);
+ }
outstanding_aio_calls++;
DEBUG(10,("schedule_aio_write_and_X: scheduled aio_write for file "
DEBUG( 3, ( "handle_aio_read_complete file %s max=%d "
"nread=%d\n",
aio_ex->fsp->fsp_name,
- aio_ex->acb.aio_nbytes, (int)nread ) );
+ (int)aio_ex->acb.aio_nbytes, (int)nread ) );
}
- smb_setlen(outbuf,outsize - 4,aio_ex->inbuf);
+ smb_setlen(outbuf,outsize - 4);
show_msg(outbuf);
if (!send_smb(smbd_server_fd(),outbuf)) {
exit_server_cleanly("handle_aio_read_complete: send_smb "
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->fsp_name, strerror(errno) ));
+ ret = errno;
+ } else {
+ DEBUG(0,("handle_aio_write_complete: "
+ "aio_write_behind failed ! File %s "
+ "is corrupt ! Wanted %u bytes but "
+ "only wrote %d\n", fsp->fsp_name,
+ (unsigned int)numtowrite,
+ (int)nwritten ));
+ ret = EIO;
+ }
+ } else {
+ DEBUG(10,("handle_aio_write_complete: "
+ "aio_write_behind completed for file %s\n",
+ fsp->fsp_name ));
+ }
+ 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. */
ret = errno;
} else {
BOOL write_through = BITSETW(aio_ex->inbuf+smb_vwv7,0);
+ NTSTATUS status;
SSVAL(outbuf,smb_vwv2,nwritten);
SSVAL(outbuf,smb_vwv4,(nwritten>>16)&1);
DEBUG(3,("handle_aio_write: fnum=%d num=%d wrote=%d\n",
fsp->fnum, (int)numtowrite, (int)nwritten));
- sync_file(fsp->conn,fsp, write_through);
+ status = sync_file(fsp->conn,fsp, write_through);
+ if (!NT_STATUS_IS_OK(status)) {
+ UNIXERROR(ERRHRD,ERRdiskfull);
+ ret = errno;
+ DEBUG(5,("handle_aio_write: sync_file for %s returned %s\n",
+ fsp->fsp_name, nt_errstr(status) ));
+ }
}
show_msg(outbuf);
return ret;
}
+/****************************************************************************
+ 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(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++) {
+ uint16 mid = aiocb_list[i]->aio_sigevent.sigev_value.sival_int;
+
+ aio_ex = find_aio_ex(mid);
+
+ if (!aio_ex) {
+ DEBUG(0, ("wait_for_aio_completion: mid %u "
+ "doesn't match an aio record\n",
+ (unsigned int)mid ));
+ continue;
+ }
+
+ if (!handle_aio_completed(aio_ex, &err)) {
+ continue;
+ }
+ delete_aio_ex(aio_ex);
+ }
+
+ SAFE_FREE(aiocb_list);
+ seconds_left = SMB_TIME_FOR_AIO_COMPLETE_WAIT
+ - (time(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.
*****************************************************************************/
}
BOOL schedule_aio_read_and_X(connection_struct *conn,
- char *inbuf, char *outbuf,
- int length, int len_outbuf,
+ struct smb_request *req,
files_struct *fsp, SMB_OFF_T startpos,
size_t smb_maxcnt)
{
}
BOOL schedule_aio_write_and_X(connection_struct *conn,
- char *inbuf, char *outbuf,
- int length, int len_outbuf,
- files_struct *fsp, char *data,
- SMB_OFF_T startpos,
- size_t numtowrite)
+ struct smb_request *req,
+ files_struct *fsp, char *data,
+ SMB_OFF_T startpos,
+ size_t numtowrite)
{
return False;
}
{
}
+BOOL wait_for_aio_completion(files_struct *fsp)
+{
+ return True;
+}
#endif