2 * Christophe Varoqui (2004)
3 * This code is GPLv2, see license file
5 * This path prioritizer aims to balance logical units over all
6 * controllers available. The logic is :
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
22 #include <libdevmapper.h>
28 #include <sys/ioctl.h>
29 #include <sys/types.h>
34 #define SERIAL_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
49 #define debug(format, arg...) fprintf(stderr, format "\n", ##arg)
51 #define debug(format, arg...) do {} while(0)
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
60 char dev_t[WORD_SIZE];
61 char serial[SERIAL_SIZE];
65 char serial[SERIAL_SIZE];
77 opennode (char * devt, int mode)
79 char devpath[FILE_NAME_SIZE];
84 sscanf(devt, "%u:%u", &major, &minor);
85 memset(devpath, 0, FILE_NAME_SIZE);
87 if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode",
89 fprintf(stderr, "devpath too small\n");
93 mknod(devpath, S_IFBLK|S_IRUSR|S_IWUSR, makedev(major, minor));
94 fd = open(devpath, mode);
104 closenode (char * devt, int fd)
106 char devpath[FILE_NAME_SIZE];
113 sscanf(devt, "%u:%u", &major, &minor);
114 if (safe_sprintf(devpath, "/tmp/.pp_balance.%u.%u.devnode",
116 fprintf(stderr, "devpath too small\n");
123 do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
124 void *resp, int mx_resp_len, int noisy)
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;
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;
149 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
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))
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)) {
162 unsigned char * sense_buffer = io_hdr.sbp;
163 if (sense_buffer[0] & 0x2)
164 sense_key = sense_buffer[1] & 0xf;
166 sense_key = sense_buffer[2] & 0xf;
167 if(RECOVERED_ERROR == sense_key)
175 get_serial (char * str, int maxlen, char * devt)
179 char buff[MX_ALLOC_LEN + 1];
181 fd = opennode(devt, O_RDONLY);
186 if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) {
191 memcpy(str, buff + 4, len);
205 struct dm_task *dmt, *dmt1;
206 struct dm_names *names = NULL;
209 uint64_t start, length;
210 char *target_type = NULL;
213 vector paramsvec = NULL;
215 if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
218 if (!dm_task_run(dmt))
221 if (!(names = dm_task_get_names(dmt)))
225 debug("no devmap found");
230 * keep only multipath maps
232 names = (void *) names + next;
234 debug("devmap %s :", names->name);
236 if (!(dmt1 = dm_task_create(DM_DEVICE_TABLE)))
239 if (!dm_task_set_name(dmt1, names->name))
242 if (!dm_task_run(dmt1))
246 nexttgt = dm_get_next_target(dmt1, nexttgt,
251 debug("\\_ %lu %lu %s", (unsigned long) start,
252 (unsigned long) length,
256 debug("unknown target type");
260 if (!strncmp(target_type, "multipath", 9)) {
262 paramsvec = vector_alloc();
264 pp = malloc(PARAMS_SIZE);
265 strncpy(pp, params, PARAMS_SIZE);
266 vector_alloc_slot(paramsvec);
267 vector_set_slot(paramsvec, pp);
269 debug("skip non multipath target");
272 dm_task_destroy(dmt1);
276 dm_task_destroy(dmt);
281 get_word (char *sentence, char *word)
286 while (*sentence == ' ') {
292 while (*p != ' ' && *p != '\0')
295 skip += (p - sentence);
297 if (p - sentence > WORD_SIZE) {
298 fprintf(stderr, "word too small\n");
301 strncpy(word, sentence, WORD_SIZE);
302 word += p - sentence;
312 is_path (char * word)
330 get_paths (vector pathvec)
332 vector paramsvec = NULL;
336 enum where {BEFOREPG, INPG, AFTERPG};
342 if (!(paramsvec = get_params()))
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);
351 if (!is_path(pp->dev_t)) {
352 debug("skip \"%s\"", pp->dev_t);
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);
375 find_controller (vector controllers, char * serial)
378 struct controller * cp;
383 vector_foreach_slot (controllers, cp, i)
384 if (!strncmp(cp->serial, serial, SERIAL_SIZE))
390 get_controllers (vector controllers, vector pathvec)
394 struct controller * cp;
399 vector_foreach_slot (pathvec, pp, i) {
400 if (!pp || !strlen(pp->serial))
403 cp = find_controller(controllers, pp->serial);
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);
416 get_max_path_count (vector controllers)
420 struct controller * cp;
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;
430 debug("max_path_count = %i", max);
435 main (int argc, char **argv)
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;
443 ref_path = zalloc(sizeof(struct path));
452 strncpy(ref_path->dev_t, argv[optind], WORD_SIZE);
454 get_serial(ref_path->serial, SERIAL_SIZE, ref_path->dev_t);
456 if (!ref_path->serial || !strlen(ref_path->serial))
459 pathvec = vector_alloc();
460 controllers = vector_alloc();
463 get_controllers(controllers, pathvec);
464 max_path_count = get_max_path_count(controllers);
465 cp = find_controller(controllers, ref_path->serial);
468 debug("no other active path on serial %s\n",
473 printf("%i\n", max_path_count - cp->path_count + 1);