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