source: trunk/packages/xen-common/xen-common/tools/blktap/drivers/block-aio.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 17 years ago

Add xen and xen-common

File size: 9.6 KB
Line 
1/* block-aio.c
2 *
3 * libaio-based raw disk implementation.
4 *
5 * (c) 2006 Andrew Warfield and Julian Chesterfield
6 *
7 * NB: This code is not thread-safe.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License version 2
11 * as published by the Free Software Foundation; or, when distributed
12 * separately from the Linux kernel or incorporated into other
13 * software packages, subject to the following license:
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a copy
16 * of this source file (the "Software"), to deal in the Software without
17 * restriction, including without limitation the rights to use, copy, modify,
18 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
19 * and to permit persons to whom the Software is furnished to do so, subject to
20 * the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included in
23 * all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
31 * IN THE SOFTWARE.
32 */
33
34
35#include <errno.h>
36#include <libaio.h>
37#include <fcntl.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <unistd.h>
41#include <sys/statvfs.h>
42#include <sys/stat.h>
43#include <sys/ioctl.h>
44#include <linux/fs.h>
45#include "tapdisk.h"
46
47
48/**
49 * We used a kernel patch to return an fd associated with the AIO context
50 * so that we can concurrently poll on synchronous and async descriptors.
51 * This is signalled by passing 1 as the io context to io_setup.
52 */
53#define REQUEST_ASYNC_FD 1
54
55#define MAX_AIO_REQS (MAX_REQUESTS * MAX_SEGMENTS_PER_REQ)
56
57struct pending_aio {
58        td_callback_t cb;
59        int id;
60        void *private;
61        uint64_t lsec;
62};
63
64struct tdaio_state {
65        int fd;
66       
67        /* libaio state */
68        io_context_t       aio_ctx;
69        struct iocb        iocb_list  [MAX_AIO_REQS];
70        struct iocb       *iocb_free  [MAX_AIO_REQS];
71        struct pending_aio pending_aio[MAX_AIO_REQS];
72        int                iocb_free_count;
73        struct iocb       *iocb_queue[MAX_AIO_REQS];
74        int                iocb_queued;
75        int                poll_fd; /* NB: we require aio_poll support */
76        struct io_event    aio_events[MAX_AIO_REQS];
77};
78
79#define IOCB_IDX(_s, _io) ((_io) - (_s)->iocb_list)
80
81/*Get Image size, secsize*/
82static int get_image_info(struct td_state *s, int fd)
83{
84        int ret;
85        long size;
86        unsigned long total_size;
87        struct statvfs statBuf;
88        struct stat stat;
89
90        ret = fstat(fd, &stat);
91        if (ret != 0) {
92                DPRINTF("ERROR: fstat failed, Couldn't stat image");
93                return -EINVAL;
94        }
95
96        if (S_ISBLK(stat.st_mode)) {
97                /*Accessing block device directly*/
98                s->size = 0;
99                if (ioctl(fd,BLKGETSIZE,&s->size)!=0) {
100                        DPRINTF("ERR: BLKGETSIZE failed, couldn't stat image");
101                        return -EINVAL;
102                }
103
104                DPRINTF("Image size: \n\tpre sector_shift  [%llu]\n\tpost "
105                        "sector_shift [%llu]\n",
106                        (long long unsigned)(s->size << SECTOR_SHIFT),
107                        (long long unsigned)s->size);
108
109                /*Get the sector size*/
110#if defined(BLKSSZGET)
111                {
112                        int arg;
113                        s->sector_size = DEFAULT_SECTOR_SIZE;
114                        ioctl(fd, BLKSSZGET, &s->sector_size);
115                       
116                        if (s->sector_size != DEFAULT_SECTOR_SIZE)
117                                DPRINTF("Note: sector size is %ld (not %d)\n",
118                                        s->sector_size, DEFAULT_SECTOR_SIZE);
119                }
120#else
121                s->sector_size = DEFAULT_SECTOR_SIZE;
122#endif
123
124        } else {
125                /*Local file? try fstat instead*/
126                s->size = (stat.st_size >> SECTOR_SHIFT);
127                s->sector_size = DEFAULT_SECTOR_SIZE;
128                DPRINTF("Image size: \n\tpre sector_shift  [%llu]\n\tpost "
129                        "sector_shift [%llu]\n",
130                        (long long unsigned)(s->size << SECTOR_SHIFT),
131                        (long long unsigned)s->size);
132        }
133
134        if (s->size == 0) {             
135                s->size =((uint64_t) 16836057);
136                s->sector_size = DEFAULT_SECTOR_SIZE;
137        }
138        s->info = 0;
139
140        return 0;
141}
142
143static inline void init_fds(struct disk_driver *dd)
144{
145        int i;
146        struct tdaio_state *prv = (struct tdaio_state *)dd->private;
147
148        for(i = 0; i < MAX_IOFD; i++) 
149                dd->io_fd[i] = 0;
150
151        dd->io_fd[0] = prv->poll_fd;
152}
153
154/* Open the disk file and initialize aio state. */
155int tdaio_open (struct disk_driver *dd, const char *name, td_flag_t flags)
156{
157        int i, fd, ret = 0, o_flags;
158        struct td_state    *s   = dd->td_state;
159        struct tdaio_state *prv = (struct tdaio_state *)dd->private;
160
161        DPRINTF("block-aio open('%s')", name);
162        /* Initialize AIO */
163        prv->iocb_free_count = MAX_AIO_REQS;
164        prv->iocb_queued     = 0;
165       
166        prv->aio_ctx = (io_context_t) REQUEST_ASYNC_FD;
167        prv->poll_fd = io_setup(MAX_AIO_REQS, &prv->aio_ctx);
168
169        if (prv->poll_fd < 0) {
170                ret = prv->poll_fd;
171                if (ret == -EAGAIN) {
172                        DPRINTF("Couldn't setup AIO context.  If you are "
173                                "trying to concurrently use a large number "
174                                "of blktap-based disks, you may need to "
175                                "increase the system-wide aio request limit. "
176                                "(e.g. 'echo echo 1048576 > /proc/sys/fs/"
177                                "aio-max-nr')\n");
178                } else {
179                        DPRINTF("Couldn't get fd for AIO poll support.  This "
180                                "is probably because your kernel does not "
181                                "have the aio-poll patch applied.\n");
182                }
183                goto done;
184        }
185
186        for (i=0;i<MAX_AIO_REQS;i++)
187                prv->iocb_free[i] = &prv->iocb_list[i];
188
189        /* Open the file */
190        o_flags = O_DIRECT | O_LARGEFILE | 
191                ((flags == TD_RDONLY) ? O_RDONLY : O_RDWR);
192        fd = open(name, o_flags);
193
194        if ( (fd == -1) && (errno == EINVAL) ) {
195
196                /* Maybe O_DIRECT isn't supported. */
197                o_flags &= ~O_DIRECT;
198                fd = open(name, o_flags);
199                if (fd != -1) DPRINTF("WARNING: Accessing image without"
200                                     "O_DIRECT! (%s)\n", name);
201
202        } else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name);
203       
204        if (fd == -1) {
205                DPRINTF("Unable to open [%s] (%d)!\n", name, 0 - errno);
206                ret = 0 - errno;
207                goto done;
208        }
209
210        prv->fd = fd;
211
212        init_fds(dd);
213        ret = get_image_info(s, fd);
214
215done:
216        return ret;     
217}
218
219int tdaio_queue_read(struct disk_driver *dd, uint64_t sector,
220                     int nb_sectors, char *buf, td_callback_t cb,
221                     int id, void *private)
222{
223        struct   iocb *io;
224        struct   pending_aio *pio;
225        struct   td_state    *s   = dd->td_state;
226        struct   tdaio_state *prv = (struct tdaio_state *)dd->private;
227        int      size    = nb_sectors * s->sector_size;
228        uint64_t offset  = sector * (uint64_t)s->sector_size;
229        long     ioidx;
230       
231        if (prv->iocb_free_count == 0)
232                return -ENOMEM;
233        io = prv->iocb_free[--prv->iocb_free_count];
234       
235        ioidx = IOCB_IDX(prv, io);
236        pio = &prv->pending_aio[ioidx];
237        pio->cb = cb;
238        pio->id = id;
239        pio->private = private;
240        pio->lsec = sector;
241       
242        io_prep_pread(io, prv->fd, buf, size, offset);
243        io->data = (void *)ioidx;
244       
245        prv->iocb_queue[prv->iocb_queued++] = io;
246
247        return 0;
248}
249                       
250int tdaio_queue_write(struct disk_driver *dd, uint64_t sector,
251                      int nb_sectors, char *buf, td_callback_t cb,
252                      int id, void *private)
253{
254        struct   iocb *io;
255        struct   pending_aio *pio;
256        struct   td_state    *s   = dd->td_state;
257        struct   tdaio_state *prv = (struct tdaio_state *)dd->private;
258        int      size    = nb_sectors * s->sector_size;
259        uint64_t offset  = sector * (uint64_t)s->sector_size;
260        long     ioidx;
261       
262        if (prv->iocb_free_count == 0)
263                return -ENOMEM;
264        io = prv->iocb_free[--prv->iocb_free_count];
265       
266        ioidx = IOCB_IDX(prv, io);
267        pio = &prv->pending_aio[ioidx];
268        pio->cb = cb;
269        pio->id = id;
270        pio->private = private;
271        pio->lsec = sector;
272       
273        io_prep_pwrite(io, prv->fd, buf, size, offset);
274        io->data = (void *)ioidx;
275       
276        prv->iocb_queue[prv->iocb_queued++] = io;
277
278        return 0;
279}
280                       
281int tdaio_submit(struct disk_driver *dd)
282{
283        int ret;
284        struct tdaio_state *prv = (struct tdaio_state *)dd->private;
285
286        if (!prv->iocb_queued)
287                return 0;
288
289        ret = io_submit(prv->aio_ctx, prv->iocb_queued, prv->iocb_queue);
290       
291        /* XXX: TODO: Handle error conditions here. */
292       
293        /* Success case: */
294        prv->iocb_queued = 0;
295       
296        return 0;
297}
298
299int tdaio_close(struct disk_driver *dd)
300{
301        struct tdaio_state *prv = (struct tdaio_state *)dd->private;
302       
303        io_destroy(prv->aio_ctx);
304        close(prv->fd);
305
306        return 0;
307}
308
309int tdaio_do_callbacks(struct disk_driver *dd, int sid)
310{
311        int ret, i, rsp = 0;
312        struct io_event *ep;
313        struct tdaio_state *prv = (struct tdaio_state *)dd->private;
314
315        /* Non-blocking test for completed io. */
316        ret = io_getevents(prv->aio_ctx, 0, MAX_AIO_REQS, prv->aio_events,
317                           NULL);
318                       
319        for (ep=prv->aio_events,i=ret; i-->0; ep++) {
320                struct iocb        *io  = ep->obj;
321                struct pending_aio *pio;
322               
323                pio = &prv->pending_aio[(long)io->data];
324                rsp += pio->cb(dd, ep->res == io->u.c.nbytes ? 0 : 1,
325                               pio->lsec, io->u.c.nbytes >> 9, 
326                               pio->id, pio->private);
327
328                prv->iocb_free[prv->iocb_free_count++] = io;
329        }
330        return rsp;
331}
332
333int tdaio_get_parent_id(struct disk_driver *dd, struct disk_id *id)
334{
335        return TD_NO_PARENT;
336}
337
338int tdaio_validate_parent(struct disk_driver *dd, 
339                          struct disk_driver *parent, td_flag_t flags)
340{
341        return -EINVAL;
342}
343
344struct tap_disk tapdisk_aio = {
345        .disk_type          = "tapdisk_aio",
346        .private_data_size  = sizeof(struct tdaio_state),
347        .td_open            = tdaio_open,
348        .td_queue_read      = tdaio_queue_read,
349        .td_queue_write     = tdaio_queue_write,
350        .td_submit          = tdaio_submit,
351        .td_close           = tdaio_close,
352        .td_do_callbacks    = tdaio_do_callbacks,
353        .td_get_parent_id   = tdaio_get_parent_id,
354        .td_validate_parent = tdaio_validate_parent
355};
Note: See TracBrowser for help on using the repository browser.