Improve reporting of layout for raid10.
[thirdparty/mdadm.git] / Detail.c
1 /*
2  * mdadm - manage Linux "md" devices aka RAID arrays.
3  *
4  * Copyright (C) 2001-2006 Neil Brown <neilb@suse.de>
5  *
6  *
7  *    This program is free software; you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation; either version 2 of the License, or
10  *    (at your option) any later version.
11  *
12  *    This program is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with this program; if not, write to the Free Software
19  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  *    Author: Neil Brown
22  *    Email: <neilb@cse.unsw.edu.au>
23  *    Paper: Neil Brown
24  *           School of Computer Science and Engineering
25  *           The University of New South Wales
26  *           Sydney, 2052
27  *           Australia
28  */
29
30 #include        "mdadm.h"
31 #include        "md_p.h"
32 #include        "md_u.h"
33
34 int Detail(char *dev, int brief, int export, int test, char *homehost)
35 {
36         /*
37          * Print out details for an md array by using
38          * GET_ARRAY_INFO and GET_DISK_INFO ioctl calls
39          */
40
41         int fd = open(dev, O_RDONLY);
42         int vers;
43         mdu_array_info_t array;
44         mdu_disk_info_t *disks;
45         int next;
46         int d;
47         time_t atime;
48         char *c;
49         char *devices = NULL;
50         int spares = 0;
51         struct stat stb;
52         int is_26 = get_linux_version() >= 2006000;
53         int is_rebuilding = 0;
54         int failed = 0;
55         struct supertype *st = NULL;
56         int max_disks = MD_SB_DISKS; /* just a default */
57         struct mdinfo info;
58         struct mdinfo *sra;
59
60         int rv = test ? 4 : 1;
61         int avail_disks = 0;
62         char *avail;
63
64         if (fd < 0) {
65                 fprintf(stderr, Name ": cannot open %s: %s\n",
66                         dev, strerror(errno));
67                 return rv;
68         }
69         vers = md_get_version(fd);
70         if (vers < 0) {
71                 fprintf(stderr, Name ": %s does not appear to be an md device\n",
72                         dev);
73                 close(fd);
74                 return rv;
75         }
76         if (vers < 9000) {
77                 fprintf(stderr, Name ": cannot get detail for md device %s: driver version too old.\n",
78                         dev);
79                 close(fd);
80                 return rv;
81         }
82         if (ioctl(fd, GET_ARRAY_INFO, &array)<0) {
83                 if (errno == ENODEV)
84                         fprintf(stderr, Name ": md device %s does not appear to be active.\n",
85                                 dev);
86                 else
87                         fprintf(stderr, Name ": cannot get array detail for %s: %s\n",
88                                 dev, strerror(errno));
89                 close(fd);
90                 return rv;
91         }
92         sra = sysfs_read(fd, 0, GET_VERSION);
93         st = super_by_fd(fd);
94
95         if (fstat(fd, &stb) != 0 && !S_ISBLK(stb.st_mode))
96                 stb.st_rdev = 0;
97         rv = 0;
98
99         if (st) max_disks = st->max_devs;
100
101         /* try to load a superblock */
102         for (d= 0; d<max_disks; d++) {
103                 mdu_disk_info_t disk;
104                 char *dv;
105                 disk.number = d;
106                 if (ioctl(fd, GET_DISK_INFO, &disk) < 0)
107                         continue;
108                 if (d >= array.raid_disks &&
109                     disk.major == 0 &&
110                     disk.minor == 0)
111                         continue;
112                 if ((dv=map_dev(disk.major, disk.minor, 1))) {
113                         if ((!st || !st->sb) &&
114                             (disk.state & (1<<MD_DISK_ACTIVE))) {
115                                 /* try to read the superblock from this device
116                                  * to get more info
117                                  */
118                                 int fd2 = dev_open(dv, O_RDONLY);
119                                 if (fd2 >=0 && st &&
120                                     st->ss->load_super(st, fd2, NULL) == 0) {
121                                         st->ss->getinfo_super(st, &info);
122                                         if (info.array.ctime != array.ctime ||
123                                             info.array.level != array.level)
124                                                 st->ss->free_super(st);
125                                 }
126                                 if (fd2 >= 0) close(fd2);
127                         }
128                 }
129         }
130
131         /* Ok, we have some info to print... */
132         c = map_num(pers, array.level);
133
134         if (export) {
135                 if (c)
136                         printf("MD_LEVEL=%s\n", c);
137                 printf("MD_DEVICES=%d\n", array.raid_disks);
138                 if (sra && sra->array.major_version < 0)
139                         printf("MD_METADATA=%s\n", sra->text_version);
140                 else
141                         printf("MD_METADATA=%02d.%02d\n",
142                                array.major_version, array.minor_version);
143
144                 if (st && st->sb)
145                         st->ss->export_detail_super(st);
146                 goto out;
147         }
148
149         if (brief) {
150                 mdu_bitmap_file_t bmf;
151                 printf("ARRAY %s level=%s num-devices=%d", dev,
152                        c?c:"-unknown-",
153                        array.raid_disks );
154                 if (sra && sra->array.major_version < 0)
155                         printf(" metadata=%s", sra->text_version);
156                 else
157                         printf(" metadata=%02d.%02d",
158                                array.major_version, array.minor_version);
159
160                 /* Only try GET_BITMAP_FILE for 0.90.01 and later */
161                 if (vers >= 9001 &&
162                     ioctl(fd, GET_BITMAP_FILE, &bmf) == 0 &&
163                     bmf.pathname[0]) {
164                         printf(" bitmap=%s", bmf.pathname);
165                 }
166         } else {
167                 mdu_bitmap_file_t bmf;
168                 unsigned long long larray_size;
169                 struct mdstat_ent *ms = mdstat_read(0, 0);
170                 struct mdstat_ent *e;
171                 int devnum = array.md_minor;
172                 if (major(stb.st_rdev) != MD_MAJOR)
173                         devnum = -1 - devnum;
174
175                 for (e=ms; e; e=e->next)
176                         if (e->devnum == devnum)
177                                 break;
178                 if (!get_dev_size(fd, NULL, &larray_size))
179                         larray_size = 0;
180
181                 printf("%s:\n", dev);
182
183                 if (sra && sra->array.major_version < 0)
184                         printf("        Version : %s\n", sra->text_version);
185                 else
186                         printf("        Version : %02d.%02d\n",
187                                array.major_version, array.minor_version);
188
189                 atime = array.ctime;
190                 printf("  Creation Time : %.24s\n", ctime(&atime));
191                 if (array.raid_disks == 0) c = "container";
192                 printf("     Raid Level : %s\n", c?c:"-unknown-");
193                 if (larray_size)
194                         printf("     Array Size : %llu%s\n", (larray_size>>10), human_size(larray_size));
195                 if (array.level >= 1) {
196                         if (array.major_version != 0 &&
197                             (larray_size >= 0xFFFFFFFFULL|| array.size == 0)) {
198                                 unsigned long long dsize = get_component_size(fd);
199                                 if (dsize > 0)
200                                         printf("  Used Dev Size : %llu%s\n",
201                                                dsize,
202                                          human_size((long long)dsize<<10));
203                                 else
204                                         printf("  Used Dev Size : unknown\n");
205                         } else
206                                 printf("  Used Dev Size : %d%s\n", array.size,
207                                        human_size((long long)array.size<<10));
208                 }
209                 printf("   Raid Devices : %d\n", array.raid_disks);
210                 printf("  Total Devices : %d\n", array.nr_disks);
211                 printf("Preferred Minor : %d\n", array.md_minor);
212                 if (sra == NULL || sra->array.major_version >= 0)
213                         printf("    Persistence : Superblock is %spersistent\n",
214                                array.not_persistent?"not ":"");
215                 printf("\n");
216                 /* Only try GET_BITMAP_FILE for 0.90.01 and later */
217                 if (vers >= 9001 &&
218                     ioctl(fd, GET_BITMAP_FILE, &bmf) == 0 &&
219                     bmf.pathname[0]) {
220                         printf("  Intent Bitmap : %s\n", bmf.pathname);
221                         printf("\n");
222                 } else if (array.state & (1<<MD_SB_BITMAP_PRESENT))
223                         printf("  Intent Bitmap : Internal\n\n");
224                 atime = array.utime;
225                 printf("    Update Time : %.24s\n", ctime(&atime));
226                 printf("          State : %s%s%s%s\n",
227                        (array.state&(1<<MD_SB_CLEAN))?"clean":"active",
228                        array.active_disks < array.raid_disks? ", degraded":"",
229                        (!e || e->percent < 0) ? "" :
230                         (e->resync) ? ", resyncing": ", recovering",
231                        larray_size ? "": ", Not Started");
232                 printf(" Active Devices : %d\n", array.active_disks);
233                 printf("Working Devices : %d\n", array.working_disks);
234                 printf(" Failed Devices : %d\n", array.failed_disks);
235                 printf("  Spare Devices : %d\n", array.spare_disks);
236                 printf("\n");
237                 if (array.level == 5) {
238                         c = map_num(r5layout, array.layout);
239                         printf("         Layout : %s\n", c?c:"-unknown-");
240                 }
241                 if (array.level == 10) {
242                         printf("         Layout :");
243                         print_r10_layout(array.layout);
244                         printf("\n");
245                 }
246                 switch (array.level) {
247                 case 0:
248                 case 4:
249                 case 5:
250                 case 10:
251                 case 6:
252                         if (array.chunk_size)
253                                 printf("     Chunk Size : %dK\n\n",
254                                        array.chunk_size/1024);
255                         break;
256                 case -1:
257                         printf("       Rounding : %dK\n\n", array.chunk_size/1024);
258                         break;
259                 default: break;
260                 }
261
262                 if (e && e->percent >= 0) {
263                         printf(" Re%s Status : %d%% complete\n",
264                                (st && st->sb && info.reshape_active)?
265                                   "shape":"build",
266                                e->percent);
267                         is_rebuilding = 1;
268                 }
269                 free_mdstat(ms);
270
271                 if (st->sb && info.reshape_active) {
272 #if 0
273 This is pretty boring
274                         printf("  Reshape pos'n : %llu%s\n", (unsigned long long) info.reshape_progress<<9,
275                                human_size(info.reshape_progress<<9));
276 #endif
277                         if (info.delta_disks > 0)
278                                 printf("  Delta Devices : %d, (%d->%d)\n",
279                                        info.delta_disks, array.raid_disks - info.delta_disks, array.raid_disks);
280                         if (info.delta_disks < 0)
281                                 printf("  Delta Devices : %d, (%d->%d)\n",
282                                        info.delta_disks, array.raid_disks, array.raid_disks + info.delta_disks);
283                         if (info.new_level != array.level) {
284                                 char *c = map_num(pers, info.new_level);
285                                 printf("      New Level : %s\n", c?c:"-unknown-");
286                         }
287                         if (info.new_level != array.level ||
288                             info.new_layout != array.layout) {
289                                 if (info.new_level == 5) {
290                                         char *c = map_num(r5layout, info.new_layout);
291                                         printf("     New Layout : %s\n",
292                                                c?c:"-unknown-");
293                                 }
294                                 if (info.new_level == 10) {
295                                         printf("     New Layout : near=%d, %s=%d\n",
296                                                info.new_layout&255,
297                                                (info.new_layout&0x10000)?"offset":"far",
298                                                (info.new_layout>>8)&255);
299                                 }
300                         }
301                         if (info.new_chunk != array.chunk_size)
302                                 printf("  New Chunksize : %dK\n", info.new_chunk/1024);
303                         printf("\n");
304                 } else if (e && e->percent >= 0)
305                         printf("\n");
306                 if (st && st->sb)
307                         st->ss->detail_super(st, homehost);
308
309                 printf("    Number   Major   Minor   RaidDevice State\n");
310         }
311         disks = malloc(max_disks * sizeof(mdu_disk_info_t));
312         for (d=0; d<max_disks; d++) {
313                 disks[d].state = (1<<MD_DISK_REMOVED);
314                 disks[d].major = disks[d].minor = 0;
315                 disks[d].number = disks[d].raid_disk = d;
316         }
317
318         next = array.raid_disks;
319         for (d=0; d < max_disks; d++) {
320                 mdu_disk_info_t disk;
321                 disk.number = d;
322                 if (ioctl(fd, GET_DISK_INFO, &disk) < 0) {
323                         if (d < array.raid_disks)
324                                 fprintf(stderr, Name ": cannot get device detail for device %d: %s\n",
325                                         d, strerror(errno));
326                         continue;
327                 }
328                 if (disk.major == 0 && disk.minor == 0)
329                         continue;
330                 if (disk.raid_disk >= 0 && disk.raid_disk < array.raid_disks)
331                         disks[disk.raid_disk] = disk;
332                 else if (next < max_disks)
333                         disks[next++] = disk;
334         }
335
336         avail = calloc(array.raid_disks, 1);
337         for (d= 0; d < max_disks; d++) {
338                 char *dv;
339                 mdu_disk_info_t disk = disks[d];
340
341                 if (d >= array.raid_disks &&
342                     disk.major == 0 &&
343                     disk.minor == 0)
344                         continue;
345                 if (!brief) {
346                         if (d == array.raid_disks) printf("\n");
347                         if (disk.raid_disk < 0)
348                                 printf("   %5d   %5d    %5d        -     ",
349                                        disk.number, disk.major, disk.minor);
350                         else
351                                 printf("   %5d   %5d    %5d    %5d     ",
352                                        disk.number, disk.major, disk.minor, disk.raid_disk);
353                         if (disk.state & (1<<MD_DISK_FAULTY)) {
354                                 printf(" faulty");
355                                 if (disk.raid_disk < array.raid_disks &&
356                                     disk.raid_disk >= 0)
357                                         failed++;
358                         }
359                         if (disk.state & (1<<MD_DISK_ACTIVE)) printf(" active");
360                         if (disk.state & (1<<MD_DISK_SYNC)) printf(" sync");
361                         if (disk.state & (1<<MD_DISK_REMOVED)) printf(" removed");
362                         if (disk.state & (1<<MD_DISK_WRITEMOSTLY)) printf(" writemostly");
363                         if ((disk.state &
364                              ((1<<MD_DISK_ACTIVE)|(1<<MD_DISK_SYNC)|(1<<MD_DISK_REMOVED)))
365                             == 0) {
366                                 printf(" spare");
367                                 if (is_26) {
368                                         if (disk.raid_disk < array.raid_disks && disk.raid_disk >= 0)
369                                                 printf(" rebuilding");
370                                 } else if (is_rebuilding && failed) {
371                                         /* Taking a bit of a risk here, we remove the
372                                          * device from the array, and then put it back.
373                                          * If this fails, we are rebuilding
374                                          */
375                                         int err = ioctl(fd, HOT_REMOVE_DISK, makedev(disk.major, disk.minor));
376                                         if (err == 0) ioctl(fd, HOT_ADD_DISK, makedev(disk.major, disk.minor));
377                                         if (err && errno ==  EBUSY)
378                                                 printf(" rebuilding");
379                                 }
380                         }
381                 }
382                 if (disk.state == 0) spares++;
383                 if (test && d < array.raid_disks
384                     && !(disk.state & (1<<MD_DISK_SYNC)))
385                         rv |= 1;
386                 if (d < array.raid_disks
387                     && (disk.state & (1<<MD_DISK_SYNC))) {
388                         avail_disks ++;
389                         avail[d] = 1;
390                 }
391                 if ((dv=map_dev(disk.major, disk.minor, 0))) {
392                         if (brief) {
393                                 if (devices) {
394                                         devices = realloc(devices,
395                                                           strlen(devices)+1+strlen(dv)+1);
396                                         strcat(strcat(devices,","),dv);
397                                 } else
398                                         devices = strdup(dv);
399                         } else
400                                 printf("   %s", dv);
401                 }
402                 if (!brief) printf("\n");
403         }
404         if (spares && brief) printf(" spares=%d", spares);
405         if (brief && st && st->sb)
406                 st->ss->brief_detail_super(st);
407         st->ss->free_super(st);
408
409         if (brief > 1 && devices) printf("\n   devices=%s", devices);
410         if (brief) printf("\n");
411         if (test &&
412             !enough(array.level, array.raid_disks, array.layout,
413                     1, avail, avail_disks))
414                 rv = 2;
415
416 out:
417         close(fd);
418         return rv;
419 }