multipath-tools: replace <> with "" for local headers
[multipath-tools/.git] / libmultipath / prioritizers / hds.c
1 /*
2  * (C) Copyright HDS GmbH 2006. All Rights Reserved.
3  *
4  * pp_hds_modular.c
5  * Version 2.00
6  *
7  * Prioritizer for Device Mapper Multipath and HDS Storage
8  *
9  * Hitachis Modular Storage contains two controllers for redundancy. The
10  * Storage internal LUN (LDEV) will normally allocated via two pathes to the
11  * server (one path per controller). For performance reasons should the server
12  * access to a LDEV only via one controller. The other path to the other
13  * controller is stand-by. It is also possible to allocate more as one path
14  * for a LDEV per controller. Here is active/active access allowed. The other
15  * pathes via the other controller are stand-by.
16  *
17  * This prioritizer checks with inquiry command the represented LDEV and
18  * Controller number and gives back a priority followed by this scheme:
19  *
20  * CONTROLLER ODD  and LDEV  ODD: PRIORITY 1
21  * CONTROLLER ODD  and LDEV EVEN: PRIORITY 0
22  * CONTROLLER EVEN and LDEV  ODD: PRIORITY 0
23  * CONTROLLER EVEN and LDEV EVEN: PRIORITY 1
24  *
25  * In the storage you can define for each LDEV a owner controller. If the
26  * server makes IOs via the other controller the storage will switch the
27  * ownership automatically. In this case you can see in the storage that the
28  * current controller is different from the default controller, but this is
29  * absolutely no problem.
30  *
31  * With this prioritizer it is possible to establish a static load balancing.
32  * Half of the LUNs are accessed via one HBA/storage controller and the other
33  * half via the other HBA/storage controller.
34  *
35  * In cluster environmemnts (RAC) it also guarantees that all cluster nodes have
36  * access to the LDEVs via the same controller.
37  *
38  * You can run the prioritizer manually in verbose mode:
39  * # pp_hds_modular -v 8:224
40  * VENDOR:  HITACHI
41  * PRODUCT: DF600F-CM
42  * SERIAL:  0x0105
43  * LDEV:    0x00C6
44  * CTRL:    1
45  * PORT:    B
46  * CTRL ODD, LDEV EVEN, PRIO 0
47  *
48  * To compile this source please execute # cc pp_hds_modular.c -o /sbin/mpath_prio_hds_modular
49  *
50  * Changes 2006-07-16:
51  *         - Changed to forward declaration of functions
52  *         - The switch-statement was changed to a logical expression
53  *         - unlinking of the devpath now also occurs at the end of
54  *           hds_modular_prio to avoid old /tmp/.pp_balance.%u.%u.devnode
55  *           entries in /tmp-Directory
56  *         - The for-statements for passing variables where changed to
57  *           snprintf-commands in verbose mode
58  * Changes 2006-08-10:
59  *         - Back to the old switch statements because the regular expression does
60  *           not work under RHEL4 U3 i386
61  * Changes 2007-06-27:
62  *      - switched from major:minor argument to device node argument
63  *
64  * This file is released under the GPL.
65  *
66  */
67
68 #include <stdio.h>
69 #include <unistd.h>
70 #include <string.h>
71 #include <errno.h>
72 #include <sys/ioctl.h>
73 #include <stdlib.h>
74
75 #include "sg_include.h"
76 #include "debug.h"
77 #include "prio.h"
78 #include "structs.h"
79
80 #define INQ_REPLY_LEN 255
81 #define INQ_CMD_CODE 0x12
82 #define INQ_CMD_LEN 6
83
84 #define pp_hds_log(prio, fmt, args...) \
85         condlog(prio, "%s: hds prio: " fmt, dev, ##args)
86
87 int hds_modular_prio (const char *dev, int fd, unsigned int timeout)
88 {
89         int k;
90         char vendor[9];
91         char product[32];
92         char serial[32];
93         char ldev[32];
94         char ctrl[32];
95         char port[32];
96         unsigned char inqCmdBlk[INQ_CMD_LEN] = { INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0 };
97         unsigned char inqBuff[INQ_REPLY_LEN];
98         unsigned char *inqBuffp = inqBuff;
99         unsigned char sense_buffer[32];
100         sg_io_hdr_t io_hdr;
101
102         if ((ioctl (fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
103                 pp_hds_log(0, "can't use SG ioctl interface");
104                 return -1;
105         }
106
107         memset (&io_hdr, 0, sizeof (sg_io_hdr_t));
108         memset (inqBuff, 0, INQ_REPLY_LEN);
109         io_hdr.interface_id = 'S';
110         io_hdr.cmd_len = sizeof (inqCmdBlk);
111         io_hdr.mx_sb_len = sizeof (sense_buffer);
112         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
113         io_hdr.dxfer_len = INQ_REPLY_LEN;
114         io_hdr.dxferp = inqBuff;
115         io_hdr.cmdp = inqCmdBlk;
116         io_hdr.sbp = sense_buffer;
117         io_hdr.timeout = get_prio_timeout(timeout, 2000); /* TimeOut = 2 seconds */
118
119         if (ioctl (fd, SG_IO, &io_hdr) < 0) {
120                 pp_hds_log(0, "SG_IO error");
121                 return -1;
122         }
123         if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
124                 pp_hds_log(0, "SCSI error");
125                 return -1;
126         }
127
128         snprintf (vendor, 9, "%.8s", inqBuffp + 8);
129         snprintf (product, 17, "%.16s", inqBuffp + 16);
130         snprintf (serial, 5, "%.4s", inqBuffp + 40);
131         snprintf (ldev, 5, "%.4s", inqBuffp + 44);
132         snprintf (ctrl, 2, "%.1s", inqBuffp + 49);
133         snprintf (port, 2, "%.1s", inqBuffp + 50);
134
135         pp_hds_log(4, "VENDOR:  %s", vendor);
136         pp_hds_log(4, "PRODUCT: %s", product);
137         pp_hds_log(4, "SERIAL:  0x%s", serial);
138         pp_hds_log(4, "LDEV:    0x%s", ldev);
139         pp_hds_log(4, "CTRL:    %s", ctrl);
140         pp_hds_log(4, "PORT:    %s", port);
141
142         switch (ctrl[0]) {
143         case '0': case '2': case '4': case '6': case '8':
144                 switch (ldev[3]) {
145                 case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E':
146                         pp_hds_log(4, "CTRL EVEN, LDEV EVEN, PRIO 1");
147                         return 1;
148                         break;
149                 case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F':
150                         pp_hds_log(4, "CTRL EVEN, LDEV ODD, PRIO 0");
151                         return 0;
152                         break;
153                 }
154                 break;
155         case '1': case '3': case '5': case '7': case '9':
156                 switch (ldev[3]) {
157                 case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E':
158                         pp_hds_log(4, "CTRL ODD, LDEV EVEN, PRIO 0");
159                         return 0;
160                         break;
161                 case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F':
162                         pp_hds_log(4, "CTRL ODD, LDEV ODD, PRIO 1");
163                         return 1;
164                         break;
165                 }
166                 break;
167         }
168         return -1;
169 }
170
171 int getprio (struct path * pp, char * args, unsigned int timeout)
172 {
173         return hds_modular_prio(pp->dev, pp->fd, timeout);
174 }