Update misleading debug messages for directio checker
[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
153         if (r < 0 ) {
154                 LOG(3, "async io getevents returned %li (errno=%s)", r,
155                     strerror(errno));
156                 rc = PATH_UNCHECKED;
157         } else if (r < 1L) {
158                 if (ct->running > ASYNC_TIMEOUT_SEC || sync) {
159                         LOG(3, "abort check on timeout");
160                         rc = PATH_DOWN;
161                 } else {
162                         LOG(3, "async io pending");
163                         rc = PATH_PENDING;
164                 }
165         } else {
166                 LOG(3, "io finished %lu/%lu", event.res, event.res2);
167                 ct->running = 0;
168                 rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN;
169         }
170
171         return rc;
172 }
173
174 int libcheck_check (struct checker * c)
175 {
176         int ret;
177         struct directio_context * ct = (struct directio_context *)c->context;
178
179         if (!ct)
180                 return PATH_UNCHECKED;
181
182         ret = check_state(c->fd, ct, c->sync);
183
184         switch (ret)
185         {
186         case PATH_UNCHECKED:
187                 MSG(c, MSG_DIRECTIO_UNKNOWN);
188                 break;
189         case PATH_DOWN:
190                 MSG(c, MSG_DIRECTIO_DOWN);
191                 break;
192         case PATH_UP:
193                 MSG(c, MSG_DIRECTIO_UP);
194                 break;
195         case PATH_PENDING:
196                 MSG(c, MSG_DIRECTIO_PENDING);
197                 break;
198         default:
199                 break;
200         }
201         return ret;
202 }