xref: /aosp_15_r20/external/sg3_utils/testing/sg_iovec_tst.cpp (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (C) 2003-2021 D. Gilbert
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2, or (at your option)
6  * any later version.
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  *
10  * Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
11  * device driver.
12  * This C++ program will read a certain number of blocks of a given block
13  * size from a given sg device node using struct sg_iovec and write what is
14  * retrieved out to a normal file. The purpose is to test the sg_iovec
15  * mechanism within the sg_io_hdr and sg_io_v4 structures.
16  *
17  * struct sg_iovec and struct iovec [in include/uapi/uio.h] are basically
18  * the same thing: a pointer followed by a length (of type size_t). If
19  * applied to a disk then the pointer will hold a LBA and 'length' will
20  * be a number of logical blocks (which usually cannot exceed 2**32-1 .
21  *
22  */
23 
24 #include <unistd.h>
25 #include <signal.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <getopt.h>
31 #include <errno.h>
32 #include <poll.h>
33 #include <limits.h>
34 #include <time.h>
35 #define __STDC_FORMAT_MACROS 1
36 #include <inttypes.h>
37 #include <sys/ioctl.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 
41 #include <linux/bsg.h>
42 
43 #ifndef HAVE_LINUX_SG_V4_HDR
44 
45 /* Kernel uapi header contain __user decorations on user space pointers
46  * to indicate they are unsafe in the kernel space. However glibc takes
47  * all those __user decorations out from headers in /usr/include/linux .
48  * So to stop compile errors when directly importing include/uapi/scsi/sg.h
49  * undef __user before doing that include. */
50 #define __user
51 
52 /* Want to block the original sg.h header from also being included. That
53  * causes lots of multiple definition errors. This will only work if this
54  * header is included _before_ the original sg.h header.  */
55 #define _SCSI_GENERIC_H         /* original kernel header guard */
56 #define _SCSI_SG_H              /* glibc header guard */
57 
58 #include "uapi_sg.h"    /* local copy of include/uapi/scsi/sg.h */
59 
60 #else
61 #define __user
62 #endif  /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
63 
64 #include "sg_lib.h"
65 #include "sg_io_linux.h"
66 #include "sg_unaligned.h"
67 
68 // C++ local header
69 #include "sg_scat_gath.h"
70 
71 static const char * version_str = "1.08 20210214";
72 
73 #define ME "sg_iovec_tst: "
74 
75 #define IOVEC_ELEMS 1024  /* match current UIO_MAXIOV in <linux/uio.h> */
76 
77 #define DEF_BLK_SZ 512
78 #define SENSE_BUFF_LEN 32
79 #define DEF_TIMEOUT 40000       /* 40,000 milliseconds */
80 
81 static struct sg_iovec iovec[IOVEC_ELEMS];
82 
83 static int verbose;
84 
85 static struct option long_options[] = {
86         {"async", no_argument, 0, 'a'},
87         {"bs", required_argument, 0, 'b'},
88         {"elem_size", required_argument, 0, 'e'},
89         {"elem-size", required_argument, 0, 'e'},
90         {"elemsz", required_argument, 0, 'e'},
91         {"fill", required_argument, 0, 'f'},
92         {"from_skip", no_argument, 0, 'F'},
93         {"from-skip", no_argument, 0, 'F'},
94         {"help", no_argument, 0, 'h'},
95         {"num", required_argument, 0, 'n'},
96         {"num_blks", required_argument, 0, 'n'},
97         {"num-blks", required_argument, 0, 'n'},
98         {"sgl", required_argument, 0, 'S'},
99         {"sgv4", no_argument, 0, '4'},
100         {"skip", required_argument, 0, 's'},
101         {"verbose", no_argument, 0, 'v'},
102         {"version", no_argument, 0, 'V'},
103         {0, 0, 0, 0},
104 };
105 
106 
107 static void
usage(void)108 usage(void)
109 {
110     printf("Usage: sg_iovec_tst [--async] [--bs=BS] [--elem_sz=ES] "
111            "[--fill=F_ELEMS]\n"
112            "                    [from_skip] [--help] --num=NUM [--sgl=SFN] "
113            "[--sgv4]\n"
114            "                    [--skip=SKIP] [--verbose] [--version] "
115            "SG_DEV OUT_F\n");
116     printf("where:\n"
117            "    --async|-a       async sg usage (def: use ioctl(SG_IO) )\n");
118     printf("    --bs=BS|-b BS    logical block size of SG_DEV (def: 512 "
119            "bytes)\n");
120     printf("    --elem_sz=ES|-e ES    iovec element size (def: BS bytes)\n");
121     printf("    --fill=F_ELEMS|-f F_ELEMS    append F_ELEMS*ES zero bytes "
122            "onto OUT_F\n"
123            "                                 after each iovec element (def: "
124            "0)\n");
125     printf("    --from_skip|-F    sgl output starts from SKIP (def: 0)\n");
126     printf("    --help|-h        this usage message\n");
127     printf("    --num=NUM|-n NUM    number of blocks to read from SG_DEV\n");
128     printf("    --sgl=SFN|-S SFN    Sgl FileName (SFN) that is written to, "
129            "with\n"
130            "                        addresses and lengths having ES as "
131            "their unit\n");
132     printf("    --sgv4|-4        use the sg v4 interface (def: v3 "
133            "interface)\n");
134     printf("    --skip=SKIP|-s SKIP    SKIP blocks before reading S_DEV "
135            "(def: 0)\n");
136     printf("    --verbose|-v     increase verbosity\n");
137     printf("    --version|-V     print version and exit\n\n");
138     printf("Reads from SG_DEV and writes that data to OUT_F in binary. Uses "
139            "iovec\n(a scatter gather list) in linear mode (i.e. it cuts up "
140            "a contiguous\nbuffer). Example:\n"
141            "     sg_iovec_tst -n 8k -e 4k /dev/sg3 out.bin\n");
142 }
143 
144 /* Returns 0 if everything ok */
145 static int
sg_read(int sg_fd,uint8_t * buff,int num_blocks,int from_block,int bs,int elem_size,int async)146 sg_read(int sg_fd, uint8_t * buff, int num_blocks, int from_block, int bs,
147         int elem_size, int async)
148 {
149     uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
150     uint8_t senseBuff[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
151     struct sg_io_hdr io_hdr;
152     struct pollfd a_poll;
153     int dxfer_len = bs * num_blocks;
154     int k, pos, rem;
155 
156     sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
157     sg_put_unaligned_be16((uint16_t)num_blocks, rdCmd + 7);
158 
159     for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
160         iovec[k].iov_base = buff + pos;
161         iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
162         if (rem <= elem_size)
163             break;
164         pos += elem_size;
165         rem -= elem_size;
166     }
167     if (k >= IOVEC_ELEMS) {
168         fprintf(stderr, "Can't fit dxfer_len=%d bytes in %d iovec elements "
169                 "(would need %d)\n", dxfer_len, IOVEC_ELEMS,
170                 dxfer_len / elem_size);
171         fprintf(stderr, "Try expanding elem_size which is currently %d "
172                 "bytes\n", elem_size);
173         return -1;
174     }
175     memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
176     io_hdr.interface_id = 'S';
177     io_hdr.cmd_len = sizeof(rdCmd);
178     io_hdr.cmdp = rdCmd;
179     io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
180     io_hdr.dxfer_len = dxfer_len;
181     io_hdr.iovec_count = k + 1;
182     io_hdr.dxferp = iovec;
183     io_hdr.mx_sb_len = SENSE_BUFF_LEN;
184     io_hdr.sbp = senseBuff;
185     io_hdr.timeout = DEF_TIMEOUT;
186     io_hdr.pack_id = from_block;
187     if (verbose) {
188         char b[128];
189 
190         fprintf(stderr, "cdb: %s\n", sg_get_command_str(rdCmd, 10, true,
191                 sizeof(b), b));
192     }
193 
194     if (async) {
195         int res = write(sg_fd, &io_hdr, sizeof(io_hdr));
196 
197         if (res < 0) {
198             perror("write(<sg_device>), error");
199             return -1;
200         } else if (res < (int)sizeof(io_hdr)) {
201             fprintf(stderr, "write(<sg_device>) returned %d, expected %d\n",
202                     res, (int)sizeof(io_hdr));
203             return -1;
204         }
205         a_poll.fd = sg_fd;
206         a_poll.events = POLLIN;
207         a_poll.revents = 0;
208         res = poll(&a_poll, 1, 2000 /* millisecs */ );
209         if (res < 0) {
210             perror("poll error on <sg_device>");
211             return -1;
212         }
213         if (0 == (POLLIN & a_poll.revents)) {
214             fprintf(stderr, "strange, poll() completed without data to "
215                     "read\n");
216             return -1;
217         }
218         res = read(sg_fd, &io_hdr, sizeof(io_hdr));
219         if (res < 0) {
220             perror("read(<sg_device>), error");
221             return -1;
222         } else if (res < (int)sizeof(io_hdr)) {
223             fprintf(stderr, "read(<sg_device>) returned %d, expected %d\n",
224                     res, (int)sizeof(io_hdr));
225             return -1;
226         }
227     } else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
228         perror("reading (SG_IO) on sg device, error");
229         return -1;
230     }
231     switch (sg_err_category3(&io_hdr)) {
232     case SG_LIB_CAT_CLEAN:
233         break;
234     case SG_LIB_CAT_RECOVERED:
235         fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
236                from_block, num_blocks);
237         break;
238     case SG_LIB_CAT_UNIT_ATTENTION:
239         fprintf(stderr, "Unit attention\n");
240         return -1;
241     default:
242         sg_chk_n_print3("reading", &io_hdr, 1);
243         return -1;
244     }
245     return 0;
246 }
247 
248 /* Returns 0 if everything ok */
249 static int
sg_read_v4(int sg_fd,uint8_t * buff,int num_blocks,int from_block,int bs,int elem_size,int async)250 sg_read_v4(int sg_fd, uint8_t * buff, int num_blocks, int from_block, int bs,
251            int elem_size, int async)
252 {
253     uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
254     uint8_t senseBuff[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
255     struct sg_io_v4 io_hdr;
256     struct pollfd a_poll;
257     int dxfer_len = bs * num_blocks;
258     int k, pos, rem, res;
259 
260     sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
261     sg_put_unaligned_be16((uint16_t)num_blocks, rdCmd + 7);
262 
263     for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
264         iovec[k].iov_base = buff + pos;
265         iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
266         if (rem <= elem_size)
267             break;
268         pos += elem_size;
269         rem -= elem_size;
270     }
271     if (k >= IOVEC_ELEMS) {
272         fprintf(stderr, "Can't fit dxfer_len=%d bytes in %d iovec elements "
273                 "(would need %d)\n", dxfer_len, IOVEC_ELEMS,
274                 dxfer_len / elem_size);
275         fprintf(stderr, "Try expanding elem_size which is currently %d "
276                 "bytes\n", elem_size);
277         return -1;
278     }
279     memset(&io_hdr, 0, sizeof(struct sg_io_v4));
280     io_hdr.guard = 'Q';
281     io_hdr.request_len = sizeof(rdCmd);
282     io_hdr.request = (uint64_t)(uintptr_t)rdCmd;
283     io_hdr.din_xfer_len = dxfer_len;
284     io_hdr.din_xferp = (uint64_t)(uintptr_t)iovec;
285     io_hdr.din_iovec_count = k + 1;
286     io_hdr.max_response_len = SG_DXFER_FROM_DEV;
287     io_hdr.response = (uint64_t)(uintptr_t)senseBuff;
288     io_hdr.timeout = DEF_TIMEOUT;
289     io_hdr.request_extra = from_block;   /* pack_id */
290     if (verbose) {
291         char b[128];
292 
293         fprintf(stderr, "cdb: %s\n", sg_get_command_str(rdCmd, 10, true,
294                 sizeof(b), b));
295     }
296     if (async) {
297         res = ioctl(sg_fd, SG_IOSUBMIT, &io_hdr);
298         if (res < 0) {
299             perror("ioctl(SG_IOSUBMIT <sg_device>), error");
300             return -1;
301         }
302         a_poll.fd = sg_fd;
303         a_poll.events = POLLIN;
304         a_poll.revents = 0;
305         res = poll(&a_poll, 1, 2000 /* millisecs */ );
306         if (res < 0) {
307             perror("poll error on <sg_device>");
308             return -1;
309         }
310         if (0 == (POLLIN & a_poll.revents)) {
311             fprintf(stderr, "strange, poll() completed without data to "
312                     "read\n");
313             return -1;
314         }
315         res = ioctl(sg_fd, SG_IORECEIVE, &io_hdr);
316         if (res < 0) {
317             perror("ioctl(SG_IORECEIVE <sg_device>), error");
318             return -1;
319         }
320     } else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
321         perror("ioctl(SG_IO) on sg device, error");
322         return -1;
323     }
324 
325     res = sg_err_category_new(io_hdr.device_status, io_hdr.transport_status,
326                               io_hdr.driver_status,
327                               (const uint8_t *)(unsigned long)io_hdr.response,
328                               io_hdr.response_len);
329     switch (res) {
330     case SG_LIB_CAT_CLEAN:
331         break;
332     case SG_LIB_CAT_RECOVERED:
333         fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
334                from_block, num_blocks);
335         break;
336     case SG_LIB_CAT_UNIT_ATTENTION:
337         fprintf(stderr, "Unit attention\n");
338         return -1;
339     default:
340         sg_linux_sense_print("reading", io_hdr.device_status,
341                          io_hdr.transport_status, io_hdr.driver_status,
342                          senseBuff, io_hdr.response_len, true);
343         return -1;
344     }
345     return 0;
346 }
347 
348 
349 int
main(int argc,char * argv[])350 main(int argc, char * argv[])
351 {
352     bool do_sgv4 = false;
353     bool do_async = false;
354     bool do_help = false;
355     bool from_skip = false;
356     bool blk_size_given = false;
357     bool elem_size_given = false;
358     int sg_fd, fd, c, res, res2, err, dxfer_len;
359     unsigned int k;
360     int blk_size = DEF_BLK_SZ;
361     int elem_size = blk_size;
362     int num_blks = 0;
363     int f_elems = 0;
364     int64_t start_blk = 0;
365     char * sg_dev_name = 0;
366     char * out_file_name = 0;
367     char * sgl_fn = 0;
368     uint8_t * buffp;
369     uint8_t * fillp = NULL;
370     FILE * fp = NULL;
371 
372     while (1) {
373         int option_index = 0;
374 
375         c = getopt_long(argc, argv, "4ab:e:f:Fhn:s:S:vV",
376                         long_options, &option_index);
377         if (c == -1)
378             break;
379 
380         switch (c) {
381         case '4':
382             do_sgv4 = true;
383             break;
384         case 'a':
385             do_async = true;
386             break;
387         case 'b':
388             blk_size = sg_get_num(optarg);
389             if (blk_size < 1) {
390                 printf("Couldn't decode positive number after '--bs=' "
391                        "option\n");
392                 sg_dev_name = 0;
393             } else
394                 blk_size_given = true;
395             break;
396         case 'e':
397             elem_size = sg_get_num(optarg);
398             if (elem_size < 1) {
399                 printf("Couldn't decode positive number after '--elem_size=' "
400                        "option\n");
401                 sg_dev_name = 0;
402             } else
403                 elem_size_given = true;
404             break;
405         case 'f':
406             f_elems = sg_get_num(optarg);
407             if (f_elems < 0) {
408                 printf("Couldn't decode number after '--fill=' option\n");
409                 sg_dev_name = 0;
410             }
411             break;
412         case 'F':
413             from_skip = true;
414             break;
415         case 'h':
416             do_help = true;
417             break;
418         case 'n':
419             num_blks = sg_get_num(optarg);
420             if (num_blks < 1) {
421                 printf("Couldn't decode positive number after '--num=' "
422                        "option\n");
423                 sg_dev_name = 0;
424             }
425             break;
426         case 's':
427             start_blk = sg_get_llnum(optarg);
428             if ((start_blk < 0) || (start_blk > INT_MAX)) {
429                 printf("Couldn't decode number after '--skip=' option\n");
430                 sg_dev_name = 0;
431             }
432             break;
433         case 'S':
434             if (sgl_fn) {
435                 printf("Looks like --sgl=SFN has been given twice\n");
436                 sg_dev_name = 0;
437             } else
438                 sgl_fn = optarg;
439             break;
440         case 'v':
441             ++verbose;
442             break;
443         case 'V':
444             printf("Version: %s\n", version_str);
445             return 0;
446         default:
447             fprintf(stderr, "unrecognised option code 0x%x ??\n", c);
448             usage();
449             return SG_LIB_SYNTAX_ERROR;
450         }
451     }
452     if (optind < argc) {
453         if (NULL == sg_dev_name) {
454             sg_dev_name = argv[optind];
455             ++optind;
456         }
457         if (optind < argc) {
458             if (sg_dev_name) {
459                 out_file_name = argv[optind];
460                 ++optind;
461             }
462             if (optind < argc) {
463                 for (; optind < argc; ++optind)
464                     fprintf(stderr, "Unexpected extra argument: %s\n",
465                             argv[optind]);
466                 usage();
467                 return SG_LIB_SYNTAX_ERROR;
468             }
469         }
470     }
471     if (do_help) {
472         usage();
473         return 0;
474     }
475     if (NULL == sg_dev_name) {
476         printf(">>> need sg node name (e.g. /dev/sg3)\n\n");
477         usage();
478         return 1;
479     }
480     if (NULL == out_file_name) {
481         printf(">>> need out filename (to place what is fetched by READ\n\n");
482         usage();
483         return 1;
484     }
485     if (0 == num_blks) {
486         printf(">>> need number of blocks to READ\n\n");
487         usage();
488         return 1;
489     }
490 
491     if ((! elem_size_given) && blk_size_given)
492         elem_size = blk_size;
493 
494     if (do_async)
495         sg_fd = open(sg_dev_name, O_RDWR);
496     else
497         sg_fd = open(sg_dev_name, O_RDONLY);
498     if (sg_fd < 0) {
499         perror(ME "sg device node open error");
500         return 1;
501     }
502     /* Don't worry, being very careful not to write to a none-sg file ... */
503     res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
504     if ((res < 0) || (k < 30000)) {
505         printf(ME "not a sg device, or driver prior to 3.x\n");
506         return 1;
507     }
508     fd = open(out_file_name, O_WRONLY | O_CREAT, 0666);
509     if (fd < 0) {
510         perror(ME "output file open error");
511         return 1;
512     }
513     if (f_elems > 0) {
514         fillp = (uint8_t *)calloc(f_elems, elem_size);
515         if (NULL == fillp) {
516             fprintf(stderr, "fill calloc for %d bytes failed\n",
517                     f_elems * elem_size);
518             goto fini;
519         }
520     }
521     if (sgl_fn) {
522         time_t t = time(NULL);
523         struct tm *tm = localtime(&t);
524         char s[128];
525 
526         fp = fopen(sgl_fn, "w");
527         if (NULL == fp) {
528             err = errno;
529             fprintf(stderr, "Unable to open %s, error: %s\n", sgl_fn,
530                     strerror(err));
531             res = sg_convert_errno(err);
532             goto fini;
533         }
534         strftime(s, sizeof(s), "%c", tm);
535         fprintf(fp, "# Scatter gather list generated by sg_iovec_tst  "
536                 "%s\n#\n", s);
537     }
538 
539     dxfer_len = num_blks * blk_size;
540     buffp = (uint8_t *)calloc(num_blks, blk_size);
541     if (buffp) {
542         int dx_len;
543         int64_t curr_blk = from_skip ? start_blk : 0;
544 
545         if (do_sgv4) {
546             if (sg_read(sg_fd, buffp, num_blks, (int)start_blk, blk_size,
547                         elem_size, do_async))
548                 goto free_buff;
549         } else {
550             if (sg_read_v4(sg_fd, buffp, num_blks, (int)start_blk, blk_size,
551                            elem_size, do_async))
552                 goto free_buff;
553         }
554         if (f_elems > 0) {
555             int fill_len = f_elems * elem_size;
556 
557             for (dx_len = 0; dx_len < dxfer_len; dx_len += elem_size) {
558                 if (write(fd, buffp + dx_len, elem_size) < 0) {
559                     perror(ME "partial dxfer output write failed");
560                     break;
561                 }
562                 if (sgl_fn) {
563                     fprintf(fp, "%" PRId64 ",1\n", curr_blk);
564                     curr_blk += f_elems + 1;
565                 }
566                 if (write(fd, fillp, fill_len) < 0) {
567                     perror(ME "partial fill output write failed");
568                     break;
569                 }
570             }
571         } else if (write(fd, buffp, dxfer_len) < 0)
572             perror(ME "full output write failed");
573         else if (sgl_fn) {
574             for (dx_len = 0; dx_len < dxfer_len; dx_len += elem_size)
575                 fprintf(fp, "%" PRId64 ",1\n", curr_blk++);
576         }
577 free_buff:
578         free(buffp);
579     } else
580         fprintf(stderr, "user space calloc for %d bytes failed\n",
581                 dxfer_len);
582     res = close(fd);
583     if (res < 0) {
584         perror(ME "output file close error");
585         close(sg_fd);
586         return 1;
587     }
588 fini:
589     res2 = close(sg_fd);
590     if (res2 < 0) {
591         err = errno;
592         perror(ME "sg device close error");
593         if (0 == res)
594             res = sg_convert_errno(err);
595     }
596     if (fp)
597         fclose(fp);
598     return res;
599 }
600