239a811c4eaa61897daaddc12db0cb25cbd4da47
[multipath-tools/.git] / libmultipath / checkers / directio.c
1 /*
2  * Copyright (c) 2005 Hannes Reinecke, Suse
3  */
4 #define _GNU_SOURCE
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <sys/ioctl.h>
13 #include <linux/fs.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include <libaio.h>
17
18 #include "checkers.h"
19 #include "../libmultipath/debug.h"
20
21 enum {
22         MSG_DIRECTIO_UNKNOWN = CHECKER_FIRST_MSGID,
23         MSG_DIRECTIO_PENDING,
24         MSG_DIRECTIO_BLOCKSIZE,
25 };
26
27 #define _IDX(x) (MSG_DIRECTIO_##x - CHECKER_FIRST_MSGID)
28 const char *libcheck_msgtable[] = {
29         [_IDX(UNKNOWN)] = " is not available",
30         [_IDX(PENDING)] = " is waiting on aio",
31         [_IDX(BLOCKSIZE)] = " cannot get blocksize, set default",
32         NULL,
33 };
34
35 #define LOG(prio, fmt, args...) condlog(prio, "directio: " fmt, ##args)
36
37 struct directio_context {
38         int             running;
39         int             reset_flags;
40         unsigned int    blksize;
41         unsigned char * buf;
42         unsigned char * ptr;
43         io_context_t    ioctx;
44         struct iocb     io;
45 };
46
47
48 int libcheck_init (struct checker * c)
49 {
50         unsigned long pgsize = getpagesize();
51         struct directio_context * ct;
52         long flags;
53
54         ct = malloc(sizeof(struct directio_context));
55         if (!ct)
56                 return 1;
57         memset(ct, 0, sizeof(struct directio_context));
58
59         if (io_setup(1, &ct->ioctx) != 0) {
60                 condlog(1, "io_setup failed");
61                 free(ct);
62                 return 1;
63         }
64
65         if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) {
66                 c->msgid = MSG_DIRECTIO_BLOCKSIZE;
67                 ct->blksize = 512;
68         }
69         if (ct->blksize > 4096) {
70                 /*
71                  * Sanity check for DASD; BSZGET is broken
72                  */
73                 ct->blksize = 4096;
74         }
75         if (!ct->blksize)
76                 goto out;
77         ct->buf = (unsigned char *)malloc(ct->blksize + pgsize);
78         if (!ct->buf)
79                 goto out;
80
81         flags = fcntl(c->fd, F_GETFL);
82         if (flags < 0)
83                 goto out;
84         if (!(flags & O_DIRECT)) {
85                 flags |= O_DIRECT;
86                 if (fcntl(c->fd, F_SETFL, flags) < 0)
87                         goto out;
88                 ct->reset_flags = 1;
89         }
90
91         ct->ptr = (unsigned char *) (((unsigned long)ct->buf + pgsize - 1) &
92                   (~(pgsize - 1)));
93
94         /* Successfully initialized, return the context. */
95         c->context = (void *) ct;
96         return 0;
97
98 out:
99         if (ct->buf)
100                 free(ct->buf);
101         io_destroy(ct->ioctx);
102         free(ct);
103         return 1;
104 }
105
106 void libcheck_free (struct checker * c)
107 {
108         struct directio_context * ct = (struct directio_context *)c->context;
109         long flags;
110
111         if (!ct)
112                 return;
113
114         if (ct->reset_flags) {
115                 if ((flags = fcntl(c->fd, F_GETFL)) >= 0) {
116                         int ret __attribute__ ((unused));
117
118                         flags &= ~O_DIRECT;
119                         /* No point in checking for errors */
120                         ret = fcntl(c->fd, F_SETFL, flags);
121                 }
122         }
123
124         if (ct->buf)
125                 free(ct->buf);
126         io_destroy(ct->ioctx);
127         free(ct);
128 }
129
130 static int
131 check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
132 {
133         struct timespec timeout = { .tv_nsec = 5 };
134         struct io_event event;
135         struct stat     sb;
136         int             rc = PATH_UNCHECKED;
137         long            r;
138
139         if (fstat(fd, &sb) == 0) {
140                 LOG(4, "called for %x", (unsigned) sb.st_rdev);
141         }
142         if (sync > 0) {
143                 LOG(4, "called in synchronous mode");
144                 timeout.tv_sec  = timeout_secs;
145                 timeout.tv_nsec = 0;
146         }
147
148         if (!ct->running) {
149                 struct iocb *ios[1] = { &ct->io };
150
151                 LOG(3, "starting new request");
152                 memset(&ct->io, 0, sizeof(struct iocb));
153                 io_prep_pread(&ct->io, fd, ct->ptr, ct->blksize, 0);
154                 if (io_submit(ct->ioctx, 1, ios) != 1) {
155                         LOG(3, "io_submit error %i", errno);
156                         return PATH_UNCHECKED;
157                 }
158         }
159         ct->running++;
160
161         errno = 0;
162         r = io_getevents(ct->ioctx, 1L, 1L, &event, &timeout);
163
164         if (r < 0 ) {
165                 LOG(3, "async io getevents returned %li (errno=%s)", r,
166                     strerror(errno));
167                 ct->running = 0;
168                 rc = PATH_UNCHECKED;
169         } else if (r < 1L) {
170                 if (ct->running > timeout_secs || sync) {
171                         struct iocb *ios[1] = { &ct->io };
172
173                         LOG(3, "abort check on timeout");
174                         r = io_cancel(ct->ioctx, ios[0], &event);
175                         /*
176                          * Only reset ct->running if we really
177                          * could abort the pending I/O
178                          */
179                         if (r)
180                                 LOG(3, "io_cancel error %i", errno);
181                         else
182                                 ct->running = 0;
183                         rc = PATH_DOWN;
184                 } else {
185                         LOG(3, "async io pending");
186                         rc = PATH_PENDING;
187                 }
188         } else {
189                 LOG(3, "io finished %lu/%lu", event.res, event.res2);
190                 ct->running = 0;
191                 rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN;
192         }
193
194         return rc;
195 }
196
197 int libcheck_check (struct checker * c)
198 {
199         int ret;
200         struct directio_context * ct = (struct directio_context *)c->context;
201
202         if (!ct)
203                 return PATH_UNCHECKED;
204
205         ret = check_state(c->fd, ct, checker_is_sync(c), c->timeout);
206
207         switch (ret)
208         {
209         case PATH_UNCHECKED:
210                 c->msgid = MSG_DIRECTIO_UNKNOWN;
211                 break;
212         case PATH_DOWN:
213                 c->msgid = CHECKER_MSGID_DOWN;
214                 break;
215         case PATH_UP:
216                 c->msgid = CHECKER_MSGID_UP;
217                 break;
218         case PATH_PENDING:
219                 c->msgid = MSG_DIRECTIO_PENDING;
220                 break;
221         default:
222                 break;
223         }
224         return ret;
225 }