1 /* $NetBSD: dev-md.c,v 1.1.1.1 2008/12/22 00:17:57 haad Exp $ */ 2 3 /* 4 * Copyright (C) 2004 Luca Berra 5 * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. 6 * 7 * This file is part of LVM2. 8 * 9 * This copyrighted material is made available to anyone wishing to use, 10 * modify, copy, or redistribute it subject to the terms and conditions 11 * of the GNU Lesser General Public License v.2.1. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this program; if not, write to the Free Software Foundation, 15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 */ 17 18 #include "lib.h" 19 #include "metadata.h" 20 #include "xlate.h" 21 #include "filter.h" 22 23 #ifdef linux 24 25 /* Lifted from <linux/raid/md_p.h> because of difficulty including it */ 26 27 #define MD_SB_MAGIC 0xa92b4efc 28 #define MD_RESERVED_BYTES (64 * 1024ULL) 29 #define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) 30 #define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) \ 31 - MD_RESERVED_SECTORS) 32 33 static int _dev_has_md_magic(struct device *dev, uint64_t sb_offset) 34 { 35 uint32_t md_magic; 36 37 /* Version 1 is little endian; version 0.90.0 is machine endian */ 38 if (dev_read(dev, sb_offset, sizeof(uint32_t), &md_magic) && 39 ((md_magic == xlate32(MD_SB_MAGIC)) || 40 (md_magic == MD_SB_MAGIC))) 41 return 1; 42 43 return 0; 44 } 45 46 /* 47 * Calculate the position of the superblock. 48 * It is always aligned to a 4K boundary and 49 * depending on minor_version, it can be: 50 * 0: At least 8K, but less than 12K, from end of device 51 * 1: At start of device 52 * 2: 4K from start of device. 53 */ 54 typedef enum { 55 MD_MINOR_VERSION_MIN, 56 MD_MINOR_V0 = MD_MINOR_VERSION_MIN, 57 MD_MINOR_V1, 58 MD_MINOR_V2, 59 MD_MINOR_VERSION_MAX = MD_MINOR_V2 60 } md_minor_version_t; 61 62 static uint64_t _v1_sb_offset(uint64_t size, md_minor_version_t minor_version) 63 { 64 uint64_t uninitialized_var(sb_offset); 65 66 switch(minor_version) { 67 case MD_MINOR_V0: 68 sb_offset = (size - 8 * 2) & ~(4 * 2 - 1ULL); 69 break; 70 case MD_MINOR_V1: 71 sb_offset = 0; 72 break; 73 case MD_MINOR_V2: 74 sb_offset = 4 * 2; 75 break; 76 } 77 sb_offset <<= SECTOR_SHIFT; 78 79 return sb_offset; 80 } 81 82 /* 83 * Returns -1 on error 84 */ 85 int dev_is_md(struct device *dev, uint64_t *sb) 86 { 87 int ret = 1; 88 md_minor_version_t minor; 89 uint64_t size, sb_offset; 90 91 if (!dev_get_size(dev, &size)) { 92 stack; 93 return -1; 94 } 95 96 if (size < MD_RESERVED_SECTORS * 2) 97 return 0; 98 99 if (!dev_open(dev)) { 100 stack; 101 return -1; 102 } 103 104 /* Check if it is an md component device. */ 105 /* Version 0.90.0 */ 106 sb_offset = MD_NEW_SIZE_SECTORS(size) << SECTOR_SHIFT; 107 if (_dev_has_md_magic(dev, sb_offset)) 108 goto out; 109 110 minor = MD_MINOR_VERSION_MIN; 111 /* Version 1, try v1.0 -> v1.2 */ 112 do { 113 sb_offset = _v1_sb_offset(size, minor); 114 if (_dev_has_md_magic(dev, sb_offset)) 115 goto out; 116 } while (++minor <= MD_MINOR_VERSION_MAX); 117 118 ret = 0; 119 120 out: 121 if (!dev_close(dev)) 122 stack; 123 124 if (ret && sb) 125 *sb = sb_offset; 126 127 return ret; 128 } 129 130 /* 131 * Retrieve chunk size from md device using sysfs. 132 */ 133 unsigned long dev_md_chunk_size(const char *sysfs_dir, struct device *dev) 134 { 135 char path[PATH_MAX+1], buffer[64]; 136 FILE *fp; 137 struct stat info; 138 unsigned long chunk_size_bytes = 0UL; 139 140 if (MAJOR(dev->dev) != md_major()) 141 return 0; 142 143 if (!sysfs_dir || !*sysfs_dir) 144 return_0; 145 146 if (dm_snprintf(path, PATH_MAX, "%s/dev/block/%d:%d/md/chunk_size", 147 sysfs_dir, MAJOR(dev->dev), MINOR(dev->dev)) < 0) { 148 log_error("dm_snprintf md chunk_size failed"); 149 return 0; 150 } 151 152 /* old sysfs structure */ 153 if (stat(path, &info) && 154 dm_snprintf(path, PATH_MAX, "%s/block/md%d/md/chunk_size", 155 sysfs_dir, MINOR(dev->dev)) < 0) { 156 log_error("dm_snprintf old md chunk size failed"); 157 return 0; 158 } 159 160 if (!(fp = fopen(path, "r"))) { 161 log_sys_error("fopen", path); 162 return 0; 163 } 164 165 if (!fgets(buffer, sizeof(buffer), fp)) { 166 log_sys_error("fgets", path); 167 goto out; 168 } 169 170 if (sscanf(buffer, "%lu", &chunk_size_bytes) != 1) { 171 log_error("sysfs file %s not in expected format: %s", path, 172 buffer); 173 goto out; 174 } 175 176 log_very_verbose("Device %s md chunk size is %lu bytes.", 177 dev_name(dev), chunk_size_bytes); 178 179 out: 180 if (fclose(fp)) 181 log_sys_error("fclose", path); 182 183 return chunk_size_bytes >> SECTOR_SHIFT; 184 } 185 186 #else 187 188 int dev_is_md(struct device *dev __attribute((unused)), 189 uint64_t *sb __attribute((unused))) 190 { 191 return 0; 192 } 193 194 unsigned long dev_md_chunk_size(const char *sysfs_dir __attribute((unused)), 195 struct device *dev __attribute((unused))) 196 { 197 return 0UL; 198 } 199 200 #endif 201