[typo] s/controler/controller/g
[platform/upstream/multipath-tools.git] / path_priority / pp_balance_units / pp_balance_units.c
1 /*
2  * Christophe Varoqui (2004)
3  * This code is GPLv2, see license file
4  *
5  * This path prioritizer aims to balance logical units over all
6  * controllers available. The logic is :
7  *
8  * - list all paths in all primary path groups
9  * - for each path, get the controller's serial
10  * - compute the number of active paths attached to each controller
11  * - compute the max number of paths attached to the same controller
12  * - if sums are already balanced or if the path passed as parameter is
13  *   attached to controller with less active paths, then return 
14  *   (max_path_attached_to_one_controller - number_of_paths_on_this_controller)
15  * - else, or if anything goes wrong, return 1 as a default prio
16  *
17  */
18 #define __user
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <libdevmapper.h>
23 #include <vector.h>
24 #include <memory.h>
25
26 #include <string.h>
27 #include <fcntl.h>
28 #include <sys/ioctl.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <scsi/sg.h>
33
34 #define SERIAL_SIZE 255
35 #define WORD_SIZE 255
36 #define PARAMS_SIZE 255
37 #define FILE_NAME_SIZE 255
38 #define INQUIRY_CMDLEN  6
39 #define INQUIRY_CMD     0x12
40 #define SENSE_BUFF_LEN  32
41 #define DEF_TIMEOUT     300000
42 #define RECOVERED_ERROR 0x01
43 #define MX_ALLOC_LEN    255
44 #define SCSI_CHECK_CONDITION    0x2
45 #define SCSI_COMMAND_TERMINATED 0x22
46 #define SG_ERR_DRIVER_SENSE     0x08
47
48 #if DEBUG
49 #define debug(format, arg...) fprintf(stderr, format "\n", ##arg)
50 #else
51 #define debug(format, arg...) do {} while(0)
52 #endif
53
54 #define safe_sprintf(var, format, args...)      \
55         snprintf(var, sizeof(var), format, ##args) >= sizeof(var)
56 #define safe_snprintf(var, size, format, args...)      \
57         snprintf(var, size, format, ##args) >= size
58
59 struct path {
60         char dev_t[WORD_SIZE];
61         char serial[SERIAL_SIZE];
62 };
63
64 struct controller {
65         char serial[SERIAL_SIZE];
66         int path_count;
67 };
68
69 static int
70 exit_tool (int ret)
71 {
72         printf("1\n");
73         exit(ret);
74 }
75
76 static int
77 opennode (char * devt, int mode)
78 {
79         char devpath[FILE_NAME_SIZE];
80         unsigned int major;
81         unsigned int minor;
82         int fd;
83
84         sscanf(devt, "%u:%u", &major, &minor);
85         memset(devpath, 0, FILE_NAME_SIZE);
86         
87         if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode",
88                          major, minor)) {
89                 fprintf(stderr, "devpath too small\n");
90                 return -1;
91         }
92         unlink (devpath);
93         mknod(devpath, S_IFBLK|S_IRUSR|S_IWUSR, makedev(major, minor));
94         fd = open(devpath, mode);
95         
96         if (fd < 0)
97                 unlink(devpath);
98
99         return fd;
100
101 }
102
103 static void
104 closenode (char * devt, int fd)
105 {
106         char devpath[FILE_NAME_SIZE];
107         unsigned int major;
108         unsigned int minor;
109
110         if (fd >= 0)
111                 close(fd);
112
113         sscanf(devt, "%u:%u", &major, &minor);
114         if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode",
115                          major, minor)) {
116                 fprintf(stderr, "devpath too small\n");
117                 return;
118         }
119         unlink(devpath);
120 }
121
122 static int
123 do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
124        void *resp, int mx_resp_len, int noisy)
125 {
126         unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
127             { INQUIRY_CMD, 0, 0, 0, 0, 0 };
128         unsigned char sense_b[SENSE_BUFF_LEN];
129         struct sg_io_hdr io_hdr;
130                                                                                                                  
131         if (cmddt)
132                 inqCmdBlk[1] |= 2;
133         if (evpd)
134                 inqCmdBlk[1] |= 1;
135         inqCmdBlk[2] = (unsigned char) pg_op;
136         inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff);
137         inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff);
138         memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
139         io_hdr.interface_id = 'S';
140         io_hdr.cmd_len = sizeof (inqCmdBlk);
141         io_hdr.mx_sb_len = sizeof (sense_b);
142         io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
143         io_hdr.dxfer_len = mx_resp_len;
144         io_hdr.dxferp = resp;
145         io_hdr.cmdp = inqCmdBlk;
146         io_hdr.sbp = sense_b;
147         io_hdr.timeout = DEF_TIMEOUT;
148  
149         if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
150                 return -1;
151  
152         /* treat SG_ERR here to get rid of sg_err.[ch] */
153         io_hdr.status &= 0x7e;
154         if ((0 == io_hdr.status) && (0 == io_hdr.host_status) &&
155             (0 == io_hdr.driver_status))
156                 return 0;
157         if ((SCSI_CHECK_CONDITION == io_hdr.status) ||
158             (SCSI_COMMAND_TERMINATED == io_hdr.status) ||
159             (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) {
160                 if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) {
161                         int sense_key;
162                         unsigned char * sense_buffer = io_hdr.sbp;
163                         if (sense_buffer[0] & 0x2)
164                                 sense_key = sense_buffer[1] & 0xf;
165                         else
166                                 sense_key = sense_buffer[2] & 0xf;
167                         if(RECOVERED_ERROR == sense_key)
168                                 return 0;
169                 }
170         }
171         return -1;
172 }
173
174 static int
175 get_serial (char * str, int maxlen, char * devt)
176 {
177         int fd;
178         int len;
179         char buff[MX_ALLOC_LEN + 1];
180
181         fd = opennode(devt, O_RDONLY);
182
183         if (fd < 0)
184                 return 1;
185
186         if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) {
187                 len = buff[3];
188                 if (len >= maxlen)
189                         return 1;
190                 if (len > 0) {
191                         memcpy(str, buff + 4, len);
192                         buff[len] = '\0';
193                 }
194                 close(fd);
195                 return 0;
196         }
197
198         closenode(devt, fd);
199         return 1;
200 }
201
202 static void *
203 get_params (void)
204 {
205         struct dm_task *dmt, *dmt1;
206         struct dm_names *names = NULL;
207         unsigned next = 0;
208         void *nexttgt;
209         uint64_t start, length;
210         char *target_type = NULL;
211         char *params;
212         char *pp;
213         vector paramsvec = NULL;
214
215         if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
216                 return NULL;
217
218         if (!dm_task_run(dmt))
219                 goto out;
220
221         if (!(names = dm_task_get_names(dmt)))
222                 goto out;
223
224         if (!names->dev) {
225                 debug("no devmap found");
226                 goto out;
227         }
228         do {
229                 /*
230                  * keep only multipath maps
231                  */
232                 names = (void *) names + next;
233                 nexttgt = NULL;
234                 debug("devmap %s :", names->name);
235
236                 if (!(dmt1 = dm_task_create(DM_DEVICE_TABLE)))
237                         goto out;
238
239                 if (!dm_task_set_name(dmt1, names->name))
240                         goto out1;
241
242                 if (!dm_task_run(dmt1))
243                         goto out1;
244
245                 do {
246                         nexttgt = dm_get_next_target(dmt1, nexttgt,
247                                                      &start,
248                                                      &length,
249                                                      &target_type,
250                                                      &params);
251                         debug("\\_ %lu %lu %s", (unsigned long) start,
252                                 (unsigned long) length,
253                                 target_type);
254
255                         if (!target_type) {
256                                 debug("unknown target type");
257                                 goto out1;
258                         }
259
260                         if (!strncmp(target_type, "multipath", 9)) {
261                                 if (!paramsvec)
262                                         paramsvec = vector_alloc();
263
264                                 pp = malloc(PARAMS_SIZE);
265                                 strncpy(pp, params, PARAMS_SIZE);
266                                 vector_alloc_slot(paramsvec);
267                                 vector_set_slot(paramsvec, pp);
268                         } else
269                                 debug("skip non multipath target");
270                 } while (nexttgt);
271 out1:
272                 dm_task_destroy(dmt1);
273                 next = names->next;
274         } while (next);
275 out:
276         dm_task_destroy(dmt);
277         return paramsvec;
278 }
279
280 static int
281 get_word (char *sentence, char *word)
282 {
283         char *p;
284         int skip = 0;
285         
286         while (*sentence ==  ' ') {
287                 sentence++;
288                 skip++;
289         }
290         p = sentence;
291
292         while (*p !=  ' ' && *p != '\0')
293                 p++;
294
295         skip += (p - sentence);
296
297         if (p - sentence > WORD_SIZE) {
298                 fprintf(stderr, "word too small\n");
299                 exit_tool(1);
300         }
301         strncpy(word, sentence, WORD_SIZE);
302         word += p - sentence;
303         *word = '\0';
304
305         if (*p == '\0')
306                 return 0;
307
308         return skip;
309 }
310
311 static int
312 is_path (char * word)
313 {
314         char *p;
315
316         if (!word)
317                 return 0;
318
319         p = word;
320
321         while (*p != '\0') {
322                 if (*p == ':')
323                         return 1;
324                 p++;
325         }
326         return 0;
327 }
328
329 static int
330 get_paths (vector pathvec)
331 {
332         vector paramsvec = NULL;
333         char * str;
334         struct path * pp;
335         int i;
336         enum where {BEFOREPG, INPG, AFTERPG};
337         int pos = BEFOREPG;
338
339         if (!pathvec)
340                 return 1;
341
342         if (!(paramsvec = get_params()))
343                 exit_tool(0);
344
345         vector_foreach_slot (paramsvec, str, i) {
346                 debug("params %s", str);
347                 while (pos != AFTERPG) {
348                         pp = zalloc(sizeof(struct path));
349                         str += get_word(str, pp->dev_t);
350
351                         if (!is_path(pp->dev_t)) {
352                                 debug("skip \"%s\"", pp->dev_t);
353                                 free(pp);
354
355                                 if (pos == INPG)
356                                         pos = AFTERPG;
357                                 
358                                 continue;
359                         }
360                         if (pos == BEFOREPG)
361                                 pos = INPG;
362
363                         get_serial(pp->serial, SERIAL_SIZE, pp->dev_t);
364                         vector_alloc_slot(pathvec);
365                         vector_set_slot(pathvec, pp);
366                         debug("store %s [%s]",
367                                 pp->dev_t, pp->serial);
368                 }
369                 pos = BEFOREPG;
370         }
371         return 0;
372 }
373
374 static void *
375 find_controller (vector controllers, char * serial)
376 {
377         int i;
378         struct controller * cp;
379
380         if (!controllers)
381                 return NULL;
382
383         vector_foreach_slot (controllers, cp, i)
384                 if (!strncmp(cp->serial, serial, SERIAL_SIZE))
385                                 return cp;
386         return NULL;
387 }
388
389 static void
390 get_controllers (vector controllers, vector pathvec)
391 {
392         int i;
393         struct path * pp;
394         struct controller * cp;
395         
396         if (!controllers)
397                 return;
398
399         vector_foreach_slot (pathvec, pp, i) {
400                 if (!pp || !strlen(pp->serial))
401                         continue;
402                 
403                 cp = find_controller(controllers, pp->serial);
404
405                 if (!cp) {
406                         cp = zalloc(sizeof(struct controller));
407                         vector_alloc_slot(controllers);
408                         vector_set_slot(controllers, cp);
409                         strncpy(cp->serial, pp->serial, SERIAL_SIZE);
410                 }
411                 cp->path_count++;       
412         }
413 }
414
415 static int
416 get_max_path_count (vector controllers)
417 {
418         int i;
419         int max = 0;
420         struct controller * cp;
421
422         if (!controllers)
423                 return 0;
424
425         vector_foreach_slot (controllers, cp, i) {
426                 debug("controller %s : %i paths", cp->serial, cp->path_count);
427                 if(cp->path_count > max)
428                         max = cp->path_count;
429         }
430         debug("max_path_count = %i", max);
431         return max;
432 }
433
434 int
435 main (int argc, char **argv)
436 {
437         vector pathvec = NULL;
438         vector controllers = NULL;
439         struct path * ref_path = NULL;
440         struct controller * cp = NULL;
441         int max_path_count = 0;
442
443         ref_path = zalloc(sizeof(struct path));
444
445         if (!ref_path)
446                 exit_tool(1);
447
448         if (argc != 2)
449                 exit_tool(1);
450
451         if (optind<argc)
452                 strncpy(ref_path->dev_t, argv[optind], WORD_SIZE);
453
454         get_serial(ref_path->serial, SERIAL_SIZE, ref_path->dev_t);
455
456         if (!ref_path->serial || !strlen(ref_path->serial))
457                 exit_tool(0);
458
459         pathvec = vector_alloc();
460         controllers = vector_alloc();
461
462         get_paths(pathvec);
463         get_controllers(controllers, pathvec);
464         max_path_count = get_max_path_count(controllers);
465         cp = find_controller(controllers, ref_path->serial);
466
467         if (!cp) {
468                 debug("no other active path on serial %s\n",
469                         ref_path->serial);
470                 exit_tool(0);
471         }
472
473         printf("%i\n", max_path_count - cp->path_count + 1);
474
475         return(0);
476 }