Merge branch 'master' of git://git.kernel.org/pub/scm/linux/storage/multipath-tools/
[platform/upstream/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 <linux/kdev_t.h>
16 #include <asm/unistd.h>
17 #include <libaio.h>
18
19 #include "checkers.h"
20 #include "../libmultipath/debug.h"
21
22 #define MSG_DIRECTIO_UNKNOWN    "directio checker is not available"
23 #define MSG_DIRECTIO_UP         "directio checker reports path is up"
24 #define MSG_DIRECTIO_DOWN       "directio checker reports path is down"
25 #define MSG_DIRECTIO_PENDING    "directio checker is waiting on aio"
26
27 #define LOG(prio, fmt, args...) condlog(prio, "directio: " fmt, ##args)
28
29 struct directio_context {
30         int             running;
31         int             reset_flags;
32         int             blksize;
33         unsigned char * buf;
34         unsigned char * ptr;
35         io_context_t    ioctx;
36         struct iocb     io;
37 };
38
39
40 int libcheck_init (struct checker * c)
41 {
42         unsigned long pgsize = getpagesize();
43         struct directio_context * ct;
44         long flags;
45
46         ct = malloc(sizeof(struct directio_context));
47         if (!ct)
48                 return 1;
49         memset(ct, 0, sizeof(struct directio_context));
50
51         if (io_setup(1, &ct->ioctx) != 0) {
52                 condlog(1, "io_setup failed");
53                 free(ct);
54                 return 1;
55         }
56
57         if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) {
58                 MSG(c, "cannot get blocksize, set default");
59                 ct->blksize = 512;
60         }
61         if (ct->blksize > 4096) {
62                 /*
63                  * Sanity check for DASD; BSZGET is broken
64                  */
65                 ct->blksize = 4096;
66         }
67         if (!ct->blksize)
68                 goto out;
69         ct->buf = (unsigned char *)malloc(ct->blksize + pgsize);
70         if (!ct->buf)
71                 goto out;
72
73         flags = fcntl(c->fd, F_GETFL);
74         if (flags < 0)
75                 goto out;
76         if (!(flags & O_DIRECT)) {
77                 flags |= O_DIRECT;
78                 if (fcntl(c->fd, F_SETFL, flags) < 0)
79                         goto out;
80                 ct->reset_flags = 1;
81         }
82
83         ct->ptr = (unsigned char *) (((unsigned long)ct->buf + pgsize - 1) &
84                   (~(pgsize - 1)));
85
86         /* Sucessfully initialized, return the context. */
87         c->context = (void *) ct;
88         return 0;
89
90 out:
91         if (ct->buf)
92                 free(ct->buf);
93         io_destroy(ct->ioctx);
94         free(ct);
95         return 1;
96 }
97
98 void libcheck_free (struct checker * c)
99 {
100         struct directio_context * ct = (struct directio_context *)c->context;
101         long flags;
102
103         if (!ct)
104                 return;
105
106         if (ct->reset_flags) {
107                 if ((flags = fcntl(c->fd, F_GETFL)) >= 0) {
108                         flags &= ~O_DIRECT;
109                         /* No point in checking for errors */
110                         fcntl(c->fd, F_SETFL, flags);
111                 }
112         }
113
114         if (ct->buf)
115                 free(ct->buf);
116         io_destroy(ct->ioctx);
117         free(ct);
118 }
119
120 static int
121 check_state(int fd, struct directio_context *ct, int sync)
122 {
123         struct timespec timeout = { .tv_nsec = 5 };
124         struct io_event event;
125         struct stat     sb;
126         int             rc = PATH_UNCHECKED;
127         long            r;
128
129         if (fstat(fd, &sb) == 0) {
130                 LOG(4, "called for %x", (unsigned) sb.st_rdev);
131         }
132         if (sync) {
133                 LOG(4, "called in synchronous mode");
134                 timeout.tv_sec  = ASYNC_TIMEOUT_SEC;
135                 timeout.tv_nsec = 0;
136         }
137
138         if (!ct->running) {
139                 struct iocb *ios[1] = { &ct->io };
140
141                 LOG(3, "starting new request");
142                 memset(&ct->io, 0, sizeof(struct iocb));
143                 io_prep_pread(&ct->io, fd, ct->ptr, ct->blksize, 0);
144                 if (io_submit(ct->ioctx, 1, ios) != 1) {
145                         LOG(3, "io_submit error %i", errno);
146                         return PATH_UNCHECKED;
147                 }
148         }
149         ct->running++;
150
151         r = io_getevents(ct->ioctx, 1L, 1L, &event, &timeout);
152         LOG(3, "async io getevents returns %li (errno=%s)", r, strerror(errno));
153
154         if (r < 1L) {
155                 if (ct->running > ASYNC_TIMEOUT_SEC || sync) {
156                         LOG(3, "abort check on timeout");
157                         rc = PATH_DOWN;
158                 } else
159                         rc = PATH_PENDING;
160         } else {
161                 LOG(3, "io finished %lu/%lu", event.res, event.res2);
162                 ct->running = 0;
163                 rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN;
164         }
165
166         return rc;
167 }
168
169 int libcheck_check (struct checker * c)
170 {
171         int ret;
172         struct directio_context * ct = (struct directio_context *)c->context;
173
174         if (!ct)
175                 return PATH_UNCHECKED;
176
177         ret = check_state(c->fd, ct, c->sync);
178
179         switch (ret)
180         {
181         case PATH_UNCHECKED:
182                 MSG(c, MSG_DIRECTIO_UNKNOWN);
183                 break;
184         case PATH_DOWN:
185                 MSG(c, MSG_DIRECTIO_DOWN);
186                 break;
187         case PATH_UP:
188                 MSG(c, MSG_DIRECTIO_UP);
189                 break;
190         case PATH_PENDING:
191                 MSG(c, MSG_DIRECTIO_PENDING);
192                 break;
193         default:
194                 break;
195         }
196         return ret;
197 }