]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/blkmapd/device-inq.c
Merge branch 'sid'
[nfs-utils.git] / utils / blkmapd / device-inq.c
1 /*
2  * device-inq.c: inquire SCSI device information.
3  *
4  * Copyright (c) 2010 EMC Corporation, Haiying Tang <Tang_Haiying@emc.com>
5  * All rights reserved.
6  *
7  * This program refers to "SCSI Primary Commands - 3 (SPC-3)
8  * at http://www.t10.org and sg_inq.c in sg3_utils-1.26 for
9  * Linux OS SCSI subsystem, by D. Gilbert.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/ioctl.h>
34 #include <sys/mount.h>
35 #include <sys/select.h>
36 #include <scsi/scsi.h>
37 #include <scsi/scsi_ioctl.h>
38 #include <scsi/sg.h>
39
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <dirent.h>
46 #include <ctype.h>
47 #include <fcntl.h>
48 #include <libgen.h>
49 #include <errno.h>
50
51 #include "device-discovery.h"
52
53 #define DEF_ALLOC_LEN   255
54 #define MX_ALLOC_LEN    (0xc000 + 0x80)
55
56 static struct bl_serial *bl_create_scsi_string(int len, const char *bytes)
57 {
58         struct bl_serial *s;
59
60         s = malloc(sizeof(*s) + len);
61         if (s) {
62                 s->data = (char *)&s[1];
63                 s->len = len;
64                 memcpy(s->data, bytes, len);
65         }
66         return s;
67 }
68
69 static void bl_free_scsi_string(struct bl_serial *str)
70 {
71         if (str)
72                 free(str);
73 }
74
75 #define sg_io_ok(io_hdr) \
76         ((((io_hdr).status & 0x7e) == 0) && \
77         ((io_hdr).host_status == 0) && \
78         (((io_hdr).driver_status & 0x0f) == 0))
79
80 static int sg_timeout = 1 * 1000;
81
82 static int bldev_inquire_page(int fd, int page, char *buffer, int len)
83 {
84         unsigned char cmd[] = { INQUIRY, 0, 0, 0, 0, 0 };
85         unsigned char sense_b[28];
86         struct sg_io_hdr io_hdr;
87         if (page >= 0) {
88                 cmd[1] = 1;
89                 cmd[2] = page;
90         }
91         cmd[3] = (unsigned char)((len >> 8) & 0xff);
92         cmd[4] = (unsigned char)(len & 0xff);
93
94         memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
95         io_hdr.interface_id = 'S';
96         io_hdr.cmd_len = sizeof(cmd);
97         io_hdr.mx_sb_len = sizeof(sense_b);
98         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
99         io_hdr.dxfer_len = len;
100         io_hdr.dxferp = buffer;
101         io_hdr.cmdp = cmd;
102         io_hdr.sbp = sense_b;
103         io_hdr.timeout = sg_timeout;
104         if (ioctl(fd, SG_IO, &io_hdr) < 0)
105                 return -1;
106
107         if (sg_io_ok(io_hdr))
108                 return 0;
109         return -1;
110 }
111
112 static int bldev_inquire_pages(int fd, int page, char **buffer)
113 {
114         int status = 0;
115         char *tmp;
116         int len;
117
118         *buffer = calloc(DEF_ALLOC_LEN, sizeof(char));
119         if (!*buffer) {
120                 BL_LOG_ERR("%s: Out of memory!\n", __func__);
121                 return -ENOMEM;
122         }
123
124         status = bldev_inquire_page(fd, page, *buffer, DEF_ALLOC_LEN);
125         if (status)
126                 goto out;
127
128         status = -1;
129         if ((*(*buffer + 1) & 0xff) != page)
130                 goto out;
131
132         len = (*(*buffer + 2) << 8) + *(*buffer + 3) + 4;
133         if (len > MX_ALLOC_LEN) {
134                 BL_LOG_ERR("SCSI response length too long: %d\n", len);
135                 goto out;
136         }
137         if (len > DEF_ALLOC_LEN) {
138                 tmp = realloc(*buffer, len);
139                 if (!tmp) {
140                         BL_LOG_ERR("%s: Out of memory!\n", __func__);
141                         status = -ENOMEM;
142                         goto out;
143                 }
144                 *buffer = tmp;
145                 status = bldev_inquire_page(fd, page, *buffer, len);
146                 if (status)
147                         goto out;
148         }
149         status = 0;
150  out:
151         return status;
152 }
153
154 /* For EMC multipath devices, use VPD page (0xc0) to get status.
155  * For other devices, return ACTIVE for now
156  */
157 extern enum bl_path_state_e bldev_read_ap_state(int fd)
158 {
159         int status = 0;
160         char *buffer = NULL;
161         enum bl_path_state_e ap_state = BL_PATH_STATE_ACTIVE;
162
163         status = bldev_inquire_pages(fd, 0xc0, &buffer);
164         if (status)
165                 goto out;
166
167         if (buffer[4] < 0x02)
168                 ap_state = BL_PATH_STATE_PASSIVE;
169  out:
170         if (buffer)
171                 free(buffer);
172         return ap_state;
173 }
174
175 struct bl_serial *bldev_read_serial(int fd, const char *filename)
176 {
177         struct bl_serial *serial_out = NULL;
178         int status = 0;
179         char *buffer;
180         struct bl_dev_id *dev_root, *dev_id;
181         unsigned int pos, len, current_id = 0;
182
183         status = bldev_inquire_pages(fd, 0x83, &buffer);
184         if (status)
185                 goto out;
186
187         dev_root = (struct bl_dev_id *)buffer;
188
189         pos = 0;
190         current_id = 0;
191         len = dev_root->len;
192         while (pos < (len - sizeof(struct bl_dev_id) + sizeof(unsigned char))) {
193                 dev_id = (struct bl_dev_id *)&(dev_root->data[pos]);
194                 if ((dev_id->ids & 0xf) < current_id)
195                         continue;
196                 switch (dev_id->ids & 0xf) {
197                         /* We process SCSI ID with four ID cases: 0, 1, 2 and 3.
198                          * When more than one ID is available, priority is
199                          * 3>2>1>0.
200                          */
201                 case 2: /* EUI-64 based */
202                         if ((dev_id->len != 8) && (dev_id->len != 12) &&
203                             (dev_id->len != 16))
204                                 break;
205                 case 3: /* NAA */
206                         /* TODO: NAA validity judgement too complicated,
207                          * so just ingore it here.
208                          */
209                         if ((dev_id->type & 0xf) != 1) {
210                                 BL_LOG_ERR("Binary code_set expected\n");
211                                 break;
212                         }
213                 case 0: /* vendor specific */
214                 case 1: /* T10 vendor identification */
215                         current_id = dev_id->ids & 0xf;
216                         if (serial_out)
217                                 bl_free_scsi_string(serial_out);
218                         serial_out = bl_create_scsi_string(dev_id->len,
219                                                            dev_id->data);
220                         break;
221                 }
222                 if (current_id == 3)
223                         break;
224                 pos += (dev_id->len + sizeof(struct bl_dev_id) -
225                         sizeof(unsigned char));
226         }
227  out:
228         if (!serial_out)
229                 serial_out = bl_create_scsi_string(strlen(filename), filename);
230         if (buffer)
231                 free(buffer);
232         return serial_out;
233 }