2 * UCW Library -- Fast Buffered I/O on O_DIRECT Files
4 * (c) 2006--2007 Martin Mares <mj@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
11 * This is a fastbuf backend for fast streaming I/O using O_DIRECT and
12 * the asynchronous I/O module. It's designed for use on large files
13 * which don't fit in the disk cache.
17 * - All operations with a single fbdirect handle must be done
18 * within a single thread, unless you provide a custom I/O queue
19 * and take care of locking.
21 * FIXME: what if the OS doesn't support O_DIRECT?
22 * FIXME: unaligned seeks and partial writes?
23 * FIXME: append to unaligned file
29 #include "lib/fastbuf.h"
33 #include "lib/threads.h"
42 static struct cf_section fbdir_cf = {
44 CF_UNS("Cheat", &fbdir_cheat),
49 #define FBDIR_ALIGN 512
51 enum fbdir_mode { // Current operating mode
59 int fd; // File descriptor
61 struct asio_queue *io_queue; // I/O queue to use
62 struct asio_queue *user_queue; // If io_queue was supplied by the user
63 struct asio_request *pending_read;
64 struct asio_request *done_read;
65 struct asio_request *active_buffer;
69 #define FB_DIRECT(f) ((struct fb_direct *)(f)->is_fastbuf)
71 static void CONSTRUCTOR
72 fbdir_global_init(void)
74 cf_declare_section("FBDirect", &fbdir_cf, 0);
78 fbdir_read_sync(struct fb_direct *F)
80 while (F->pending_read)
82 struct asio_request *r = asio_wait(F->io_queue);
84 struct fb_direct *G = r->user_data;
86 ASSERT(G->pending_read == r && !G->done_read);
87 G->pending_read = NULL;
93 fbdir_change_mode(struct fb_direct *F, enum fbdir_mode mode)
97 DBG("FB-DIRECT: Switching mode to %d", mode);
103 fbdir_read_sync(F); // Wait for read-ahead requests to finish
104 if (F->done_read) // Return read-ahead requests if any
106 asio_put(F->done_read);
111 asio_sync(F->io_queue); // Wait for pending writebacks
114 if (F->active_buffer)
116 asio_put(F->active_buffer);
117 F->active_buffer = NULL;
123 fbdir_submit_read(struct fb_direct *F)
125 struct asio_request *r = asio_get(F->io_queue);
128 r->len = F->io_queue->buffer_size;
135 fbdir_refill(struct fastbuf *f)
137 struct fb_direct *F = FB_DIRECT(f);
139 DBG("FB-DIRECT: Refill");
143 if (!F->pending_read)
145 fbdir_change_mode(F, M_READ);
146 fbdir_submit_read(F);
149 ASSERT(F->done_read);
152 struct asio_request *r = F->done_read;
154 if (F->active_buffer)
155 asio_put(F->active_buffer);
156 F->active_buffer = r;
160 die("Error reading %s: %s", f->name, strerror(r->returned_errno));
161 f->bptr = f->buffer = r->buffer;
162 f->bstop = f->bufend = f->buffer + r->status;
165 fbdir_submit_read(F); // Read-ahead the next block
171 fbdir_spout(struct fastbuf *f)
173 struct fb_direct *F = FB_DIRECT(f);
174 struct asio_request *r;
176 DBG("FB-DIRECT: Spout");
178 fbdir_change_mode(F, M_WRITE);
179 r = F->active_buffer;
180 if (r && f->bptr > f->bstop)
182 r->op = ASIO_WRITE_BACK;
184 r->len = f->bptr - f->bstop;
185 ASSERT(!(f->pos % FBDIR_ALIGN) || fbdir_cheat);
187 if (!fbdir_cheat && r->len % FBDIR_ALIGN) // Have to simulate incomplete writes
189 r->len = ALIGN_TO(r->len, FBDIR_ALIGN);
191 asio_sync(F->io_queue);
192 DBG("FB-DIRECT: Truncating at %llu", (long long)f->pos);
193 if (sh_ftruncate(F->fd, f->pos) < 0)
194 die("Error truncating %s: %m", f->name);
201 r = asio_get(F->io_queue);
202 f->bstop = f->bptr = f->buffer = r->buffer;
203 f->bufend = f->buffer + F->io_queue->buffer_size;
204 F->active_buffer = r;
208 fbdir_seek(struct fastbuf *f, sh_off_t pos, int whence)
210 DBG("FB-DIRECT: Seek %llu %d", (long long)pos, whence);
212 if (whence == SEEK_SET && pos == f->pos)
215 fbdir_change_mode(FB_DIRECT(f), M_NULL); // Wait for all async requests to finish
216 sh_off_t l = sh_seek(FB_DIRECT(f)->fd, pos, whence);
223 static struct asio_queue *
224 fbdir_get_io_queue(uns buffer_size, uns write_back)
226 struct ucwlib_context *ctx = ucwlib_thread_context();
227 struct asio_queue *q = ctx->io_queue;
230 q = xmalloc_zero(sizeof(struct asio_queue));
231 q->buffer_size = buffer_size;
232 q->max_writebacks = write_back;
237 DBG("FB-DIRECT: Got I/O queue, uc=%d", q->use_count);
242 fbdir_put_io_queue(void)
244 struct ucwlib_context *ctx = ucwlib_thread_context();
245 struct asio_queue *q = ctx->io_queue;
247 DBG("FB-DIRECT: Put I/O queue, uc=%d", q->use_count);
250 asio_cleanup_queue(q);
252 ctx->io_queue = NULL;
257 fbdir_close(struct fastbuf *f)
259 struct fb_direct *F = FB_DIRECT(f);
261 DBG("FB-DIRECT: Close");
263 fbdir_change_mode(F, M_NULL);
265 fbdir_put_io_queue();
267 bclose_file_helper(f, F->fd, F->is_temp_file);
272 fbdir_config(struct fastbuf *f, uns item, int value)
278 case BCONFIG_IS_TEMP_FILE:
279 orig = FB_DIRECT(f)->is_temp_file;
280 FB_DIRECT(f)->is_temp_file = value;
288 fbdir_open_fd_internal(int fd, const char *name, struct asio_queue *q, uns buffer_size, uns read_ahead UNUSED, uns write_back)
290 int namelen = strlen(name) + 1;
291 struct fb_direct *F = xmalloc(sizeof(struct fb_direct) + namelen);
292 struct fastbuf *f = &F->fb;
294 DBG("FB-DIRECT: Open");
295 bzero(F, sizeof(*F));
297 memcpy(f->name, name, namelen);
300 F->io_queue = F->user_queue = q;
302 F->io_queue = fbdir_get_io_queue(buffer_size, write_back);
303 f->refill = fbdir_refill;
304 f->spout = fbdir_spout;
305 f->seek = fbdir_seek;
306 f->close = fbdir_close;
307 f->config = fbdir_config;
308 f->can_overwrite_buffer = 2;
314 #include "lib/getopt.h"
316 int main(int argc, char **argv)
318 struct fb_params par = { .type = FB_DIRECT };
319 struct fastbuf *f, *t;
322 if (cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) >= 0)
323 die("Hey, whaddya want?");
324 f = (optind < argc) ? bopen_file(argv[optind++], O_RDONLY, &par) : bopen_fd(0, &par);
325 t = (optind < argc) ? bopen_file(argv[optind++], O_RDWR | O_CREAT | O_TRUNC, &par) : bopen_fd(1, &par);
328 ASSERT(btell(f) == btell(t));
330 #if 0 // This triggers unaligned write
337 ASSERT(btell(t) == 1);