s3-vfs: Add pread_send/recv to vfs modules
[kai/samba.git] / source3 / modules / vfs_aio_posix.c
1 /*
2  * Simulate pread_send/recv and pwrite_send/recv using posix aio
3  *
4  * Copyright (C) Volker Lendecke 2012
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "system/shmem.h"
24 #include "smbd/smbd.h"
25 #include "smbd/globals.h"
26 #include "lib/util/tevent_unix.h"
27 #include <aio.h>
28
29 /* The signal we'll use to signify aio done. */
30 #ifndef RT_SIGNAL_AIO
31 #define RT_SIGNAL_AIO   (SIGRTMIN+3)
32 #endif
33
34 #ifndef HAVE_STRUCT_SIGEVENT_SIGEV_VALUE_SIVAL_PTR
35 #ifdef HAVE_STRUCT_SIGEVENT_SIGEV_VALUE_SIGVAL_PTR
36 #define sival_int       sigval_int
37 #define sival_ptr       sigval_ptr
38 #endif
39 #endif
40
41 static struct tevent_signal *aio_signal_event = NULL;
42
43 struct aio_posix_state {
44         struct aiocb acb;
45         ssize_t ret;
46         int err;
47 };
48
49 static int aio_posix_state_destructor(struct aio_posix_state *s)
50 {
51         int ret;
52
53         /*
54          * We could do better here. This destructor is run when a
55          * request is prematurely cancelled. We wait for the aio to
56          * complete, so that we do not have to maintain aiocb structs
57          * beyond the life of an aio_posix_state. Possible, but not
58          * sure the effort is worth it right now.
59          */
60
61         do {
62                 const struct aiocb *a = &s->acb;
63                 ret = aio_suspend(&a, 1, NULL);
64         } while ((ret == -1) && (errno == EINTR));
65
66         return 0;
67 }
68
69 static struct tevent_req *aio_posix_pread_send(
70         struct vfs_handle_struct *handle,
71         TALLOC_CTX *mem_ctx, struct tevent_context *ev,
72         struct files_struct *fsp, void *data, size_t n, off_t offset)
73 {
74         struct tevent_req *req;
75         struct aio_posix_state *state;
76         struct aiocb *a;
77         int ret;
78
79         req = tevent_req_create(mem_ctx, &state, struct aio_posix_state);
80         if (req == NULL) {
81                 return NULL;
82         }
83
84         a = &state->acb;
85
86         a->aio_fildes = fsp->fh->fd;
87         a->aio_buf = data;
88         a->aio_nbytes = n;
89         a->aio_offset = offset;
90         a->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
91         a->aio_sigevent.sigev_signo  = RT_SIGNAL_AIO;
92         a->aio_sigevent.sigev_value.sival_ptr = req;
93
94         ret = aio_read(a);
95         if (ret == 0) {
96                 talloc_set_destructor(state, aio_posix_state_destructor);
97                 return req;
98         }
99
100         if (errno == EAGAIN) {
101                 /*
102                  * aio overloaded, do the sync fallback
103                  */
104                 state->ret = sys_pread(fsp->fh->fd, data, n, offset);
105                 if (state->ret == -1) {
106                         state->err = errno;
107                 }
108                 tevent_req_done(req);
109                 return tevent_req_post(req, ev);
110         }
111
112         tevent_req_error(req, errno);
113         return tevent_req_post(req, ev);
114 }
115
116 static struct tevent_req *aio_posix_pwrite_send(
117         struct vfs_handle_struct *handle,
118         TALLOC_CTX *mem_ctx, struct tevent_context *ev,
119         struct files_struct *fsp, const void *data, size_t n, off_t offset)
120 {
121         struct tevent_req *req;
122         struct aio_posix_state *state;
123         struct aiocb *a;
124         int ret;
125
126         req = tevent_req_create(mem_ctx, &state, struct aio_posix_state);
127         if (req == NULL) {
128                 return NULL;
129         }
130
131         a = &state->acb;
132
133         a->aio_fildes = fsp->fh->fd;
134         a->aio_buf = discard_const(data);
135         a->aio_nbytes = n;
136         a->aio_offset = offset;
137         a->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
138         a->aio_sigevent.sigev_signo  = RT_SIGNAL_AIO;
139         a->aio_sigevent.sigev_value.sival_ptr = req;
140
141         ret = aio_write(a);
142         if (ret == 0) {
143                 talloc_set_destructor(state, aio_posix_state_destructor);
144                 return req;
145         }
146
147         if (errno == EAGAIN) {
148                 /*
149                  * aio overloaded, do the sync fallback
150                  */
151                 state->ret = sys_pwrite(fsp->fh->fd, data, n, offset);
152                 if (state->ret == -1) {
153                         state->err = errno;
154                 }
155                 tevent_req_done(req);
156                 return tevent_req_post(req, ev);
157         }
158
159         tevent_req_error(req, errno);
160         return tevent_req_post(req, ev);
161 }
162
163 static void aio_posix_signal_handler(struct tevent_context *ev,
164                                      struct tevent_signal *se,
165                                      int signum, int count,
166                                      void *_info, void *private_data)
167 {
168         siginfo_t *info;
169         struct tevent_req *req;
170         struct aio_posix_state *state;
171         int err;
172
173         info = (siginfo_t *)_info;
174         req = talloc_get_type_abort(info->si_value.sival_ptr,
175                                     struct tevent_req);
176         state = tevent_req_data(req, struct aio_posix_state);
177
178         err = aio_error(&state->acb);
179         if (err == EINPROGRESS) {
180                 DEBUG(10, ("aio_posix_signal_handler: operation req %p "
181                            "still in progress\n", req));
182                 return;
183         }
184         if (err == ECANCELED) {
185                 DEBUG(10, ("aio_posix_signal_handler: operation req %p "
186                            "canceled\n", req));
187                 return;
188         }
189
190         /*
191          * No need to suspend for this in the destructor anymore
192          */
193         talloc_set_destructor(state, NULL);
194
195         state->ret = aio_return(&state->acb);
196         state->err = err;
197         tevent_req_done(req);
198 }
199
200 static ssize_t aio_posix_recv(struct tevent_req *req, int *err)
201 {
202         struct aio_posix_state *state = tevent_req_data(
203                 req, struct aio_posix_state);
204
205         if (tevent_req_is_unix_error(req, err)) {
206                 return -1;
207         }
208         *err = state->err;
209         return state->ret;
210 }
211
212 static int aio_posix_connect(vfs_handle_struct *handle, const char *service,
213                              const char *user)
214 {
215         if (aio_signal_event == NULL) {
216                 struct tevent_context *ev = handle->conn->sconn->ev_ctx;
217
218                 aio_signal_event = tevent_add_signal(
219                         ev, ev, RT_SIGNAL_AIO, SA_SIGINFO,
220                         aio_posix_signal_handler, NULL);
221
222                 if (aio_signal_event == NULL) {
223                         DEBUG(1, ("tevent_add_signal failed\n"));
224                         return -1;
225                 }
226         }
227         return SMB_VFS_NEXT_CONNECT(handle, service, user);
228 }
229
230 static struct vfs_fn_pointers vfs_aio_posix_fns = {
231         .connect_fn = aio_posix_connect,
232         .pread_send_fn = aio_posix_pread_send,
233         .pread_recv_fn = aio_posix_recv,
234         .pwrite_send_fn = aio_posix_pwrite_send,
235         .pwrite_recv_fn = aio_posix_recv,
236 };
237
238 NTSTATUS vfs_aio_posix_init(void);
239 NTSTATUS vfs_aio_posix_init(void)
240 {
241         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
242                                 "aio_posix", &vfs_aio_posix_fns);
243 }