1
0

aoeping.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /* aoeping.c - userland aoe pinger
  2. * Copyright 2009, CORAID, Inc., and licensed under GPL v.2.
  3. *
  4. * run without arguments for usage
  5. */
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <stdlib.h>
  9. #include <unistd.h>
  10. #include <stdint.h>
  11. #include <ctype.h>
  12. #include <netinet/in.h>
  13. #include <linux/hdreg.h>
  14. #include <errno.h>
  15. #include "dat.h"
  16. #include "fns.h"
  17. struct progopts {
  18. int shelf;
  19. int slot;
  20. char *netif;
  21. int verbose;
  22. int timeout;
  23. u32 tag;
  24. char *smart;
  25. char ata_ident;
  26. char pp_ataid; /* pretty print ATA device identify response */
  27. };
  28. static struct progopts defaults = {
  29. .shelf = 0,
  30. .slot = 0,
  31. .netif = NULL,
  32. .verbose = 0,
  33. .timeout = 0,
  34. .tag = 0,
  35. .smart = NULL,
  36. .ata_ident = 0,
  37. .pp_ataid = 0,
  38. };
  39. static struct progopts opts;
  40. struct smartcmd {
  41. char *name; /* subcommand name from ATA spec */
  42. int cmd; /* for features register */
  43. char data; /* does this subcommand xfer data? */
  44. };
  45. static struct smartcmd smarts[] = {
  46. { "read_data", 0xd0, SmartDataRet },
  47. // { "attr_autosave", 0xd2, 0 }, (unsupported b/c it overloads sector count)
  48. { "offline_immediate", 0xd4, 0 },
  49. { "read_log", 0xd5, SmartDataRet },
  50. { "write_log", 0xd6, SmartDataPut },
  51. { "enable", 0xd8, 0 },
  52. { "disable", 0xd9, 0 },
  53. { "return_status", 0xda, 0 },
  54. };
  55. static char *progname;
  56. static int sfd; /* raw socket file descriptor */
  57. static uchar mac[6];
  58. void
  59. usage(void)
  60. {
  61. fprintf(stderr,
  62. "usage:\t%s [options] {shelf} {slot} {netif}\n",
  63. progname);
  64. fprintf(stderr, "%s\n\t%s\n\t%s\n\t%s\n\t%s\n",
  65. "options:",
  66. "-i\tdo ATA device identify dump as raw hex",
  67. "-I\tdo ATA device identify print fields",
  68. "-v\tbe verbose",
  69. "-h\tshow this usage summary");
  70. fprintf(stderr, "%s\n\t%s\n\t%s\n\t%s\n",
  71. "options taking arguments:",
  72. "-s\ttimeout in seconds",
  73. "-S\tperform SMART command",
  74. "-t\tspecify number for starting AoE tag");
  75. }
  76. void
  77. hex_print(FILE *out, uchar *buf, int n, char *sep)
  78. {
  79. int i;
  80. int per_line = 16;
  81. for (i = 0; i < n;) {
  82. fprintf(out, "%02x%s", *buf++ & 0xff, sep);
  83. if (!(++i % per_line))
  84. putc('\n', out);
  85. }
  86. }
  87. void
  88. find_blade(Conf *c, struct progopts *opts)
  89. {
  90. int n;
  91. uchar buf[1400];
  92. u32 tag;
  93. Aoehdr *h = &c->h;
  94. memset(h, 0, sizeof *h);
  95. memset(h->dst, 0xff, sizeof h->dst);
  96. memmove(h->src, mac, sizeof h->src);
  97. h->type = htons(AOE_ETH_PROTO);
  98. h->flags = AoEver << 4;
  99. h->maj = htons(opts->shelf);
  100. h->min = opts->slot;
  101. h->cmd = Config;
  102. tag = htonl(opts->tag);
  103. memmove(h->tag, &tag, sizeof h->tag);
  104. c->bufcnt = 0;
  105. c->firmware = 0;
  106. c->scnt = 0;
  107. c->vercmd = Qread;
  108. c->len = htons(1024);
  109. memset(c->data, 0xED, sizeof c->data);
  110. if (write(sfd, c, sizeof *c) == -1) {
  111. perror("send config query");
  112. exit(EXIT_FAILURE);
  113. }
  114. for (;;) {
  115. n = read(sfd, buf, sizeof buf);
  116. if (n < 0) {
  117. perror("read network");
  118. exit(EXIT_FAILURE);
  119. }
  120. if (n < 60)
  121. continue;
  122. h = (Aoehdr *) buf;
  123. if (ntohs(h->type) != AOE_ETH_PROTO
  124. || ntohs(h->maj) != opts->shelf
  125. || h->min != opts->slot
  126. || memcmp(h->tag, &tag, sizeof h->tag))
  127. continue;
  128. break;
  129. }
  130. if (opts->verbose) {
  131. puts("config query response:");
  132. hex_print(stdout, buf, n, " ");
  133. putchar('\n');
  134. }
  135. memcpy(c, buf, sizeof *c);
  136. }
  137. /* read a packet that was sent by the device that returned *c earlier
  138. */
  139. int
  140. aoe_pkt_read(uchar *buf, size_t siz, Conf *c, u32 tag)
  141. {
  142. Aoehdr *h;
  143. int n;
  144. tag = htonl(tag);
  145. for (;;) {
  146. n = read(sfd, buf, siz);
  147. if (n < 0) {
  148. perror("read network");
  149. exit(EXIT_FAILURE);
  150. }
  151. if (n < 60)
  152. continue;
  153. h = (Aoehdr *) buf;
  154. if (ntohs(h->type) != AOE_ETH_PROTO
  155. || h->maj != c->h.maj
  156. || h->min != c->h.min
  157. || memcmp(&tag, h->tag, sizeof h->tag))
  158. continue;
  159. break;
  160. }
  161. return n;
  162. }
  163. /* prepare a packet for doing ATA to a device that gave us Conf *c
  164. */
  165. void
  166. ata_prep(Ata *a, Conf *c, u32 tag)
  167. {
  168. memset(a, 0, sizeof *a);
  169. memcpy(a->h.dst, c->h.src, sizeof a->h.dst);
  170. memcpy(a->h.src, mac, sizeof a->h.src);
  171. a->h.type = htons(AOE_ETH_PROTO);
  172. a->h.flags = AoEver << 4;
  173. a->h.maj = c->h.maj;
  174. a->h.min = c->h.min;
  175. a->h.cmd = ATAcmd;
  176. tag = htonl(tag);
  177. memmove(a->h.tag, &tag, sizeof a->h.tag);
  178. }
  179. /* pretty print ATA device identify text field
  180. * bytes have already been swapped
  181. */
  182. void
  183. pp_idtext(char *prefix, unsigned char *p, size_t len)
  184. {
  185. int i;
  186. fputs(prefix, stdout);
  187. for (i = 0; i < len; ++i, ++p) {
  188. if (*p == '\0')
  189. break;
  190. if (!isgraph((int) *p) && *p != ' ')
  191. break;
  192. putchar(*p);
  193. }
  194. putchar('\n');
  195. }
  196. int smart_supported(unsigned char *p)
  197. {
  198. u16 w;
  199. p += 82 * 2; /* skip to word 82 */
  200. w = *p++;
  201. w |= *p << 8;
  202. /* word 82 bit 0 is SMART support per ATA spec */
  203. return !!(w & 1);
  204. }
  205. void
  206. disk_identify(Conf *c, struct progopts *opts, int *smart)
  207. {
  208. int n;
  209. uchar buf[1400];
  210. Ata a;
  211. Ata *p;
  212. struct hd_driveid *id;
  213. ata_prep(&a, c, opts->tag);
  214. a.sectors = 1;
  215. a.cmd = ATAid_dev;
  216. a.lba[3] = 0xa0;
  217. if (write(sfd, &a, sizeof a) == -1) {
  218. perror("send ATA identify device");
  219. exit(EXIT_FAILURE);
  220. }
  221. n = aoe_pkt_read(buf, sizeof buf, c, opts->tag);
  222. p = (Ata *) buf;
  223. *smart = smart_supported(p->data);
  224. if (opts->ata_ident && !opts->pp_ataid) {
  225. puts("device identify response:");
  226. hex_print(stdout, p->data, 512, " ");
  227. return;
  228. }
  229. if (!opts->pp_ataid)
  230. return;
  231. for (n = 0; n < 1024; n += 2) {
  232. unsigned char ch;
  233. ch = p->data[n];
  234. p->data[n] = p->data[n+1];
  235. p->data[n+1] = ch;
  236. }
  237. id = (struct hd_driveid *) p->data;
  238. puts("device identify fields:");
  239. printf("vendor_specific_0: 0x%X\n", id->vendor0);
  240. printf("vendor_specific_1: 0x%X\n", id->vendor1);
  241. printf("vendor_specific_2: 0x%X\n", id->vendor2);
  242. pp_idtext("serial_number: ", id->serial_no, sizeof id->serial_no);
  243. pp_idtext("firmware_rev: ", id->fw_rev, sizeof id->fw_rev);
  244. pp_idtext("model: ", id->model, sizeof id->model);
  245. }
  246. struct smartcmd *
  247. smartcmd_lookup(char *nam)
  248. {
  249. int n = sizeof smarts / sizeof smarts[0];
  250. int i;
  251. for (i = 0; i < n; ++i) {
  252. char *p = strchr(nam, ':');
  253. if (p && !strncmp(smarts[i].name, nam, p - nam))
  254. return &smarts[i];
  255. else if (!strcmp(smarts[i].name, nam))
  256. return &smarts[i];
  257. }
  258. return nil;
  259. }
  260. void
  261. smart_registers(Ata *a, char *opts, struct smartcmd *s)
  262. {
  263. a->err = s->cmd;
  264. a->lba[1] = 0x4f;
  265. a->lba[2] = 0xc2;
  266. if (opts++)
  267. a->lba[0] = strtol(opts, NULL, 0);
  268. }
  269. int
  270. show_smart_regs(Ata *a)
  271. {
  272. if (a->err & ATAabrt) {
  273. fputs("SMART command aborted on target.\n",
  274. stderr);
  275. return -1;
  276. }
  277. puts("ATA registers:");
  278. char *names[] = {
  279. "Features", "Sector Count",
  280. "LBA Low", "LBA Mid", "LBA High",
  281. "Status",
  282. };
  283. int regs[] = {
  284. a->err, a->sectors,
  285. a->lba[0], a->lba[1], a->lba[2],
  286. a->cmd,
  287. };
  288. int i;
  289. for (i = 0; i < sizeof regs / sizeof regs[0]; ++i)
  290. printf("%20s: 0x%02x\n", names[i], regs[i]);
  291. return 0;
  292. }
  293. void
  294. smart(Conf *c, u32 tag, char *smart_cmd)
  295. {
  296. int n;
  297. uchar buf[1400];
  298. Ata a;
  299. Ata *p;
  300. struct smartcmd *s = smartcmd_lookup(smart_cmd);
  301. if (!s) {
  302. fprintf(stderr,
  303. "%s Error: no such SMART command: %s\n",
  304. progname, smart_cmd);
  305. exit(EXIT_FAILURE);
  306. }
  307. ata_prep(&a, c, tag);
  308. a.sectors = !!s->data; /* we only support one-sector data xfer */
  309. a.cmd = ATAsmart;
  310. smart_registers(&a, strchr(smart_cmd, ':'), s);
  311. if (s->data & SmartDataPut) {
  312. if (read(STDIN_FILENO, a.data, 512) == -1) {
  313. perror("reading smart data from stdin");
  314. exit(EXIT_FAILURE);
  315. }
  316. a.h.flags |= Write;
  317. }
  318. if (write(sfd, &a, sizeof a) == -1) {
  319. perror("send ATA identify device");
  320. exit(EXIT_FAILURE);
  321. }
  322. n = aoe_pkt_read(buf, sizeof buf, c, tag);
  323. p = (Ata *) buf;
  324. if (show_smart_regs(p) != 0)
  325. exit(EXIT_FAILURE);
  326. if (s->data & SmartDataRet) {
  327. puts("SMART data:");
  328. hex_print(stdout, p->data, 512, " ");
  329. }
  330. }
  331. void
  332. bad_option(char c)
  333. {
  334. fprintf(stderr, "%s Error: unrecognized option: ", progname);
  335. if (isprint(c))
  336. fprintf(stderr, "%c\n", c);
  337. else
  338. fprintf(stderr, "0x%02x\n", c & 0xff);
  339. }
  340. void
  341. check_timeout(int secs)
  342. {
  343. if (secs < 1) {
  344. fprintf(stderr,
  345. "%s Error: timeout seconds must be one or more\n",
  346. progname);
  347. exit(EXIT_FAILURE);
  348. }
  349. }
  350. void
  351. init_opts(struct progopts *opts, int argc, char *argv[])
  352. {
  353. int c;
  354. while ( (c = getopt(argc, argv, "hviIt:s:S:")) != -1) {
  355. switch (c) {
  356. case 'h':
  357. usage();
  358. exit(EXIT_SUCCESS);
  359. break;
  360. case 'v':
  361. ++opts->verbose;
  362. break;
  363. case 'i':
  364. opts->ata_ident = 1;
  365. break;
  366. case 'I':
  367. opts->ata_ident = 1;
  368. opts->pp_ataid = 1;
  369. break;
  370. case 't':
  371. opts->tag = atoi(optarg);
  372. break;
  373. case 's':
  374. opts->timeout = atoi(optarg);
  375. check_timeout(opts->timeout);
  376. break;
  377. case 'S':
  378. opts->smart = optarg;
  379. break;
  380. case '?':
  381. bad_option(optopt);
  382. usage();
  383. exit(EXIT_FAILURE);
  384. break;
  385. default:
  386. abort(); /* shouldn't happen */
  387. }
  388. }
  389. if (argc - optind != 3) {
  390. usage();
  391. exit(EXIT_FAILURE);
  392. }
  393. opts->shelf = atoi(argv[optind]);
  394. opts->slot = atoi(argv[optind+1]);
  395. opts->netif = argv[optind+2];
  396. }
  397. int
  398. main(int argc, char *argv[])
  399. {
  400. Conf c;
  401. int smartable = 0;
  402. opts = defaults;
  403. progname = strrchr(argv[0], '/');
  404. if (progname)
  405. progname += 1;
  406. else
  407. progname = argv[0];
  408. init_opts(&opts, argc, argv);
  409. opts.tag |= 1UL << 31; /* set high bit for userland AoE */
  410. if (opts.verbose) {
  411. printf("tag: %x\neth: %s\nshelf: %u\nslot: %u\n",
  412. opts.tag, opts.netif, opts.shelf, opts.slot);
  413. fflush(stdout);
  414. }
  415. sfd = dial(opts.netif);
  416. if (!getea(sfd, opts.netif, mac))
  417. exit(EXIT_FAILURE);
  418. alarm(opts.timeout);
  419. find_blade(&c, &opts);
  420. opts.tag += 1;
  421. alarm(0);
  422. if (opts.verbose) {
  423. printf("found e%d.%d with mac ",
  424. ntohs(c.h.maj), c.h.min);
  425. hex_print(stdout, c.h.src, sizeof c.h.src, "");
  426. putchar('\n');
  427. fflush(stdout);
  428. }
  429. if (opts.ata_ident || opts.smart) {
  430. alarm(opts.timeout);
  431. disk_identify(&c, &opts, &smartable);
  432. alarm(0);
  433. opts.tag += 1;
  434. }
  435. if (opts.smart) {
  436. if (!smartable) {
  437. fprintf(stderr,
  438. "Error: e%d.%d does not support SMART\n",
  439. ntohs(c.h.maj), c.h.min);
  440. exit(EXIT_FAILURE);
  441. }
  442. alarm(opts.timeout);
  443. smart(&c, opts.tag, opts.smart);
  444. alarm(0);
  445. opts.tag += 1;
  446. }
  447. return 0;
  448. }