s3:smbd: convert aio to use tevent_signal
[metze/samba/wip.git] / source3 / smbd / aio.c
index c3fd0a2bc0e3f1c3a448b72c4f0738401ca1f426..4ce43c95e80f2cd04bf5b84e4be5d90f80d8d530 100644 (file)
@@ -19,6 +19,7 @@
 */
 
 #include "includes.h"
+#include "smbd/globals.h"
 
 #if defined(WITH_AIO)
 
@@ -45,94 +46,50 @@ struct aio_extra {
        struct aio_extra *next, *prev;
        SMB_STRUCT_AIOCB acb;
        files_struct *fsp;
-       bool read_req;
-       uint16 mid;
-       char *inbuf;
+       struct smb_request *req;
        char *outbuf;
+       int (*handle_completion)(struct aio_extra *ex);
 };
 
-static struct aio_extra *aio_list_head;
+static int handle_aio_read_complete(struct aio_extra *aio_ex);
+static int handle_aio_write_complete(struct aio_extra *aio_ex);
 
-/****************************************************************************
- Create the extended aio struct we must keep around for the lifetime
- of the aio_read call.
-*****************************************************************************/
-
-static struct aio_extra *create_aio_ex_read(files_struct *fsp, size_t buflen,
-                                           uint16 mid)
+static int aio_extra_destructor(struct aio_extra *aio_ex)
 {
-       struct aio_extra *aio_ex = SMB_MALLOC_P(struct aio_extra);
-
-       if (!aio_ex) {
-               return NULL;
-       }
-       ZERO_STRUCTP(aio_ex);
-       /* The output buffer stored in the aio_ex is the start of
-          the smb return buffer. The buffer used in the acb
-          is the start of the reply data portion of that buffer. */
-       aio_ex->outbuf = SMB_MALLOC_ARRAY(char, buflen);
-       if (!aio_ex->outbuf) {
-               SAFE_FREE(aio_ex);
-               return NULL;
-       }
-       DLIST_ADD(aio_list_head, aio_ex);
-       aio_ex->fsp = fsp;
-       aio_ex->read_req = True;
-       aio_ex->mid = mid;
-       return aio_ex;
+       DLIST_REMOVE(aio_list_head, aio_ex);
+       return 0;
 }
 
 /****************************************************************************
  Create the extended aio struct we must keep around for the lifetime
- of the aio_write call.
+ of the aio call.
 *****************************************************************************/
 
-static struct aio_extra *create_aio_ex_write(files_struct *fsp,
-                                            size_t inbuflen,
-                                            size_t outbuflen,
-                                            uint16 mid)
+static struct aio_extra *create_aio_extra(files_struct *fsp, size_t buflen)
 {
-       struct aio_extra *aio_ex = SMB_MALLOC_P(struct aio_extra);
+       struct aio_extra *aio_ex = TALLOC_ZERO_P(NULL, struct aio_extra);
 
        if (!aio_ex) {
                return NULL;
        }
-       ZERO_STRUCTP(aio_ex);
 
-       /* We need space for an output reply of outbuflen bytes. */
-       aio_ex->outbuf = SMB_MALLOC_ARRAY(char, outbuflen);
-       if (!aio_ex->outbuf) {
-               SAFE_FREE(aio_ex);
-               return NULL;
-       }
+       /* The output buffer stored in the aio_ex is the start of
+          the smb return buffer. The buffer used in the acb
+          is the start of the reply data portion of that buffer. */
 
-       if (!(aio_ex->inbuf = SMB_MALLOC_ARRAY(char, inbuflen))) {
-               SAFE_FREE(aio_ex->outbuf);
-               SAFE_FREE(aio_ex);
+       aio_ex->outbuf = TALLOC_ARRAY(aio_ex, char, buflen);
+       if (!aio_ex->outbuf) {
+               TALLOC_FREE(aio_ex);
                return NULL;
        }
-
        DLIST_ADD(aio_list_head, aio_ex);
+       talloc_set_destructor(aio_ex, aio_extra_destructor);
        aio_ex->fsp = fsp;
-       aio_ex->read_req = False;
-       aio_ex->mid = mid;
        return aio_ex;
 }
 
 /****************************************************************************
- Delete the extended aio struct.
-*****************************************************************************/
-
-static void delete_aio_ex(struct aio_extra *aio_ex)
-{
-       DLIST_REMOVE(aio_list_head, aio_ex);
-       SAFE_FREE(aio_ex->inbuf);
-       SAFE_FREE(aio_ex->outbuf);
-       SAFE_FREE(aio_ex);
-}
-
-/****************************************************************************
- Given the aiocb struct find the extended aio struct containing it.
+ Given the mid find the extended aio struct containing it.
 *****************************************************************************/
 
 static struct aio_extra *find_aio_ex(uint16 mid)
@@ -140,7 +97,7 @@ static struct aio_extra *find_aio_ex(uint16 mid)
        struct aio_extra *p;
 
        for( p = aio_list_head; p; p = p->next) {
-               if (mid == p->mid) {
+               if (mid == p->req->mid) {
                        return p;
                }
        }
@@ -151,63 +108,6 @@ static struct aio_extra *find_aio_ex(uint16 mid)
  We can have these many aio buffers in flight.
 *****************************************************************************/
 
-static int aio_pending_size;
-static sig_atomic_t signals_received;
-static int outstanding_aio_calls;
-static uint16 *aio_pending_array;
-
-/****************************************************************************
- Signal handler when an aio request completes.
-*****************************************************************************/
-
-void aio_request_done(uint16_t mid)
-{
-       if (signals_received < aio_pending_size) {
-               aio_pending_array[signals_received] = mid;
-               signals_received++;
-       }
-       /* Else signal is lost. */
-}
-
-static void signal_handler(int sig, siginfo_t *info, void *unused)
-{
-       aio_request_done(info->si_value.sival_int);
-       sys_select_signal(RT_SIGNAL_AIO);
-}
-
-/****************************************************************************
- Is there a signal waiting ?
-*****************************************************************************/
-
-bool aio_finished(void)
-{
-       return (signals_received != 0);
-}
-
-/****************************************************************************
- Initialize the signal handler for aio read/write.
-*****************************************************************************/
-
-void initialize_async_io_handler(void)
-{
-       struct sigaction act;
-
-       aio_pending_size = lp_maxmux();
-       aio_pending_array = SMB_MALLOC_ARRAY(uint16, aio_pending_size);
-       SMB_ASSERT(aio_pending_array != NULL);
-
-       ZERO_STRUCT(act);
-       act.sa_sigaction = signal_handler;
-       act.sa_flags = SA_SIGINFO;
-       sigemptyset( &act.sa_mask );
-       if (sigaction(RT_SIGNAL_AIO, &act, NULL) != 0) {
-                DEBUG(0,("Failed to setup RT_SIGNAL_AIO handler\n"));
-        }
-
-       /* the signal can start off blocked due to a bug in bash */
-       BlockSignals(False, RT_SIGNAL_AIO);
-}
-
 /****************************************************************************
  Set up an aio request from a SMBreadX call.
 *****************************************************************************/
@@ -221,6 +121,7 @@ bool schedule_aio_read_and_X(connection_struct *conn,
        SMB_STRUCT_AIOCB *a;
        size_t bufsize;
        size_t min_aio_read_size = lp_aio_read_size(SNUM(conn));
+       int ret;
 
        if (fsp->base_fsp != NULL) {
                /* No AIO on streams yet */
@@ -240,7 +141,7 @@ bool schedule_aio_read_and_X(connection_struct *conn,
 
        /* Only do this on non-chained and non-chaining reads not using the
         * write cache. */
-        if (chain_size !=0 || (CVAL(req->inbuf,smb_vwv0) != 0xFF)
+        if (chain_size !=0 || (CVAL(req->vwv+0, 0) != 0xFF)
            || (lp_write_cache_size(SNUM(conn)) != 0) ) {
                return False;
        }
@@ -257,43 +158,47 @@ bool schedule_aio_read_and_X(connection_struct *conn,
 
        bufsize = smb_size + 12 * 2 + smb_maxcnt;
 
-       if ((aio_ex = create_aio_ex_read(fsp, bufsize, req->mid)) == NULL) {
+       if ((aio_ex = create_aio_extra(fsp, bufsize)) == NULL) {
                DEBUG(10,("schedule_aio_read_and_X: malloc fail.\n"));
                return False;
        }
+       aio_ex->handle_completion = handle_aio_read_complete;
 
-       construct_reply_common((char *)req->inbuf, aio_ex->outbuf);
+       construct_reply_common_req(req, aio_ex->outbuf);
        srv_set_message(aio_ex->outbuf, 12, 0, True);
        SCVAL(aio_ex->outbuf,smb_vwv0,0xFF); /* Never a chained reply. */
 
        a = &aio_ex->acb;
 
        /* Now set up the aio record for the read call. */
-       
+
        a->aio_fildes = fsp->fh->fd;
        a->aio_buf = smb_buf(aio_ex->outbuf);
        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_int = aio_ex->mid;
+       a->aio_sigevent.sigev_value.sival_int = req->mid;
 
        become_root();
-       if (SMB_VFS_AIO_READ(fsp,a) == -1) {
+       ret = SMB_VFS_AIO_READ(fsp, a);
+       unbecome_root();
+
+       if (ret == -1) {
                DEBUG(0,("schedule_aio_read_and_X: aio_read failed. "
                         "Error %s\n", strerror(errno) ));
-               delete_aio_ex(aio_ex);
-               unbecome_root();
+               TALLOC_FREE(aio_ex);
                return False;
        }
-       unbecome_root();
+
+       aio_ex->req = talloc_move(aio_ex, &req);
 
        DEBUG(10,("schedule_aio_read_and_X: scheduled aio_read for file %s, "
                  "offset %.0f, len = %u (mid = %u)\n",
                  fsp->fsp_name, (double)startpos, (unsigned int)smb_maxcnt,
-                 (unsigned int)aio_ex->mid ));
+                 (unsigned int)aio_ex->req->mid ));
 
-       srv_defer_sign_response(aio_ex->mid);
+       srv_defer_sign_response(aio_ex->req->mid);
        outstanding_aio_calls++;
        return True;
 }
@@ -310,9 +215,10 @@ bool schedule_aio_write_and_X(connection_struct *conn,
 {
        struct aio_extra *aio_ex;
        SMB_STRUCT_AIOCB *a;
-       size_t inbufsize, outbufsize;
-       bool write_through = BITSETW(req->inbuf+smb_vwv7,0);
+       size_t bufsize;
+       bool write_through = BITSETW(req->vwv+7,0);
        size_t min_aio_write_size = lp_aio_write_size(SNUM(conn));
+       int ret;
 
        if (fsp->base_fsp != NULL) {
                /* No AIO on streams yet */
@@ -332,7 +238,7 @@ bool schedule_aio_write_and_X(connection_struct *conn,
 
        /* Only do this on non-chained and non-chaining reads not using the
         * write cache. */
-        if (chain_size !=0 || (CVAL(req->inbuf,smb_vwv0) != 0xFF)
+        if (chain_size !=0 || (CVAL(req->vwv+0, 0) != 0xFF)
            || (lp_write_cache_size(SNUM(conn)) != 0) ) {
                return False;
        }
@@ -350,45 +256,43 @@ bool schedule_aio_write_and_X(connection_struct *conn,
                return False;
        }
 
-       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,
-                                          req->mid))) {
+       bufsize = smb_size + 6*2;
+
+       if (!(aio_ex = create_aio_extra(fsp, bufsize))) {
                DEBUG(0,("schedule_aio_write_and_X: malloc fail.\n"));
                return False;
        }
+       aio_ex->handle_completion = handle_aio_write_complete;
 
-       /* Copy the SMB header already setup in outbuf. */
-       memcpy(aio_ex->inbuf, req->inbuf, inbufsize);
-
-       /* Copy the SMB header already setup in outbuf. */
-       memcpy(aio_ex->outbuf, req->outbuf, outbufsize);
-       TALLOC_FREE(req->outbuf);
+       construct_reply_common_req(req, aio_ex->outbuf);
+       srv_set_message(aio_ex->outbuf, 6, 0, True);
        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, req->inbuf));
+       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_int = aio_ex->mid;
+       a->aio_sigevent.sigev_value.sival_int = req->mid;
 
        become_root();
-       if (SMB_VFS_AIO_WRITE(fsp,a) == -1) {
+       ret = SMB_VFS_AIO_WRITE(fsp, a);
+       unbecome_root();
+
+       if (ret == -1) {
                DEBUG(3,("schedule_aio_wrote_and_X: aio_write failed. "
                         "Error %s\n", strerror(errno) ));
-               delete_aio_ex(aio_ex);
-               unbecome_root();
+               TALLOC_FREE(aio_ex);
                return False;
        }
-       unbecome_root();
-       
+
+       aio_ex->req = talloc_move(aio_ex, &req);
+
        release_level_2_oplocks_on_change(fsp);
 
        if (!write_through && !lp_syncalways(SNUM(fsp->conn))
@@ -406,7 +310,7 @@ bool schedule_aio_write_and_X(connection_struct *conn,
                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);
+               srv_defer_sign_response(aio_ex->req->mid);
        }
        outstanding_aio_calls++;
 
@@ -414,7 +318,7 @@ bool schedule_aio_write_and_X(connection_struct *conn,
                  "%s, offset %.0f, len = %u (mid = %u) "
                  "outstanding_aio_calls = %d\n",
                  fsp->fsp_name, (double)startpos, (unsigned int)numtowrite,
-                 (unsigned int)aio_ex->mid, outstanding_aio_calls ));
+                 (unsigned int)aio_ex->req->mid, outstanding_aio_calls ));
 
        return True;
 }
@@ -442,7 +346,7 @@ static int handle_aio_read_complete(struct aio_extra *aio_ex)
                /* If errno is ECANCELED then don't return anything to the
                 * client. */
                if (errno == ECANCELED) {
-                       srv_cancel_sign_response(aio_ex->mid);
+                       srv_cancel_sign_response(aio_ex->req->mid);
                        return 0;
                }
 
@@ -536,7 +440,7 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex)
                /* If errno is ECANCELED then don't return anything to the
                 * client. */
                if (errno == ECANCELED) {
-                       srv_cancel_sign_response(aio_ex->mid);
+                       srv_cancel_sign_response(aio_ex->req->mid);
                        return 0;
                }
 
@@ -544,7 +448,7 @@ static int handle_aio_write_complete(struct aio_extra *aio_ex)
                ERROR_BOTH(map_nt_error_from_unix(ret), ERRHRD, ERRdiskfull);
                srv_set_message(outbuf,0,0,true);
         } else {
-               bool write_through = BITSETW(aio_ex->inbuf+smb_vwv7,0);
+               bool write_through = BITSETW(aio_ex->req->vwv+7,0);
                NTSTATUS status;
 
                SSVAL(outbuf,smb_vwv2,nwritten);
@@ -600,16 +504,11 @@ static bool handle_aio_completed(struct aio_extra *aio_ex, int *perr)
        if (SMB_VFS_AIO_ERROR(aio_ex->fsp, &aio_ex->acb) == EINPROGRESS) {
                DEBUG(10,( "handle_aio_completed: operation mid %u still in "
                           "process for file %s\n",
-                          aio_ex->mid, aio_ex->fsp->fsp_name ));
+                          aio_ex->req->mid, aio_ex->fsp->fsp_name ));
                return False;
        }
 
-       if (aio_ex->read_req) {
-               err = handle_aio_read_complete(aio_ex);
-       } else {
-               err = handle_aio_write_complete(aio_ex);
-       }
-
+       err = aio_ex->handle_completion(aio_ex);
        if (err) {
                *perr = err; /* Only save non-zero errors. */
        }
@@ -622,57 +521,47 @@ static bool handle_aio_completed(struct aio_extra *aio_ex, int *perr)
  Returns non-zero errno if fail or zero if all ok.
 *****************************************************************************/
 
-int process_aio_queue(void)
+void smbd_aio_complete_mid(unsigned int mid)
 {
-       int i;
+       files_struct *fsp = NULL;
+       struct aio_extra *aio_ex = find_aio_ex(mid);
        int ret = 0;
 
-       BlockSignals(True, RT_SIGNAL_AIO);
-
-       DEBUG(10,("process_aio_queue: signals_received = %d\n",
-                 (int)signals_received));
-       DEBUG(10,("process_aio_queue: outstanding_aio_calls = %d\n",
-                 outstanding_aio_calls));
+       DEBUG(10,("smbd_aio_complete_mid: mid[%u]\n", mid));
 
-       if (!signals_received) {
-               BlockSignals(False, RT_SIGNAL_AIO);
-               return 0;
+       if (!aio_ex) {
+               DEBUG(3,("smbd_aio_complete_mid: Can't find record to "
+                        "match mid %u.\n", mid));
+               srv_cancel_sign_response(mid);
+               return;
        }
 
-       /* Drain all the complete aio_reads. */
-       for (i = 0; i < signals_received; i++) {
-               uint16 mid = aio_pending_array[i];
-               files_struct *fsp = NULL;
-               struct aio_extra *aio_ex = find_aio_ex(mid);
-
-               if (!aio_ex) {
-                       DEBUG(3,("process_aio_queue: Can't find record to "
-                                "match mid %u.\n", (unsigned int)mid));
-                       srv_cancel_sign_response(mid);
-                       continue;
-               }
+       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[%u]).\n", mid));
+               srv_cancel_sign_response(mid);
+               return;
+       }
 
-               fsp = aio_ex->fsp;
-               if (fsp == NULL) {
-                       /* file was closed whilst I/O was outstanding. Just
-                        * ignore. */
-                       DEBUG( 3,( "process_aio_queue: file closed whilst "
-                                  "aio outstanding.\n"));
-                       srv_cancel_sign_response(mid);
-                       continue;
-               }
+       if (!handle_aio_completed(aio_ex, &ret)) {
+               return;
+       }
 
-               if (!handle_aio_completed(aio_ex, &ret)) {
-                       continue;
-               }
+       TALLOC_FREE(aio_ex);
+}
 
-               delete_aio_ex(aio_ex);
-       }
+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;
+       unsigned int mid = (unsigned int)info->si_value.sival_int;
 
-       outstanding_aio_calls -= signals_received;
-       signals_received = 0;
-       BlockSignals(False, RT_SIGNAL_AIO);
-       return ret;
+       smbd_aio_complete_mid(mid);
 }
 
 /****************************************************************************
@@ -738,7 +627,7 @@ int wait_for_aio_completion(files_struct *fsp)
 
                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 "
@@ -767,7 +656,7 @@ int wait_for_aio_completion(files_struct *fsp)
                        if (!handle_aio_completed(aio_ex, &err)) {
                                continue;
                        }
-                       delete_aio_ex(aio_ex);
+                       TALLOC_FREE(aio_ex);
                }
 
                SAFE_FREE(aiocb_list);
@@ -804,19 +693,28 @@ void cancel_aio_by_fsp(files_struct *fsp)
        }
 }
 
-#else
-bool aio_finished(void)
-{
-       return False;
-}
+/****************************************************************************
+ Initialize the signal handler for aio read/write.
+*****************************************************************************/
 
 void initialize_async_io_handler(void)
 {
+       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) {
+               exit_server("Failed to setup RT_SIGNAL_AIO handler");
+       }
+
+       /* tevent supports 100 signal with SA_SIGINFO */
+       aio_pending_size = 100;
 }
 
-int process_aio_queue(void)
+#else
+void initialize_async_io_handler(void)
 {
-       return False;
 }
 
 bool schedule_aio_read_and_X(connection_struct *conn,
@@ -844,4 +742,7 @@ int wait_for_aio_completion(files_struct *fsp)
 {
        return ENOSYS;
 }
+
+void smbd_aio_complete_mid(unsigned int mid);
+
 #endif