sg: fix short reads
authorJens Axboe <axboe@fb.com>
Wed, 30 Sep 2015 16:31:12 +0000 (18:31 +0200)
committerJens Axboe <axboe@fb.com>
Wed, 30 Sep 2015 16:31:12 +0000 (18:31 +0200)
We can't assume that if we read some data, we read all of it.
Check for this condition.

Don't error out if a read failed, but we already got valid
events.

Signed-off-by: Jens Axboe <axboe@fb.com>
engines/sg.c

index c24b323fc5d609bcf984dc1095f98a75f71e6161..360775fb831c32a0b0739a97c7677672f31fe973 100644 (file)
@@ -69,6 +69,35 @@ static int pollin_events(struct pollfd *pfds, int fds)
        return 0;
 }
 
+static int sg_fd_read(int fd, void *data, size_t size)
+{
+       int err = 0;
+
+       while (size) {
+               ssize_t ret;
+
+               ret = read(fd, data, size);
+               if (ret < 0) {
+                       if (errno == EAGAIN || errno == EINTR)
+                               continue;
+                       err = errno;
+                       break;
+               } else if (!ret)
+                       break;
+               else {
+                       data += ret;
+                       size -= ret;
+               }
+       }
+
+       if (err)
+               return err;
+       if (size)
+               return EAGAIN;
+
+       return 0;
+}
+
 static int fio_sgio_getevents(struct thread_data *td, unsigned int min,
                              unsigned int max,
                              const struct timespec fio_unused *t)
@@ -125,27 +154,20 @@ re_read:
                events = 0;
                for_each_file(td, f, i) {
                        for (eventNum = 0; eventNum < left; eventNum++) {
-                               ret = read(f->fd, p, sizeof(struct sg_io_hdr));
+                               ret = sg_fd_read(f->fd, p, sizeof(struct sg_io_hdr));
                                dprint(FD_IO, "sgio_getevents: ret: %d\n", ret);
-                               if (ret < 0) {
-                                       /*
-                                        *  not sure if EINTR is needed,
-                                        *  but seems like it should be.
-                                        */
-                                       if (errno == EAGAIN || errno == EINTR)
-                                               continue;
-                                       r = -errno;
-                                       td_verror(td, errno, "read");
+                               if (ret) {
+                                       r = -ret;
+                                       td_verror(td, r, "sg_read");
                                        break;
-                               } else if (ret) {
-                                       p += ret;
-                                       events += 1;  /* ret / sizeof(struct sg_io_hdr); */
-                                       dprint(FD_IO, "sgio_getevents: events: %d\n", events);
                                }
+                               p += sizeof(struct sg_io_hdr);
+                               events++;
+                               dprint(FD_IO, "sgio_getevents: events: %d\n", events);
                        }
                }
 
-               if (r < 0)
+               if (r < 0 && !events)
                        break;
                if (!events) {
                        usleep(1000);