這一節我們閱讀陣列的創建過程。
按照常理出牌,我們到ioctl中找陣列創建命令,md對應的ioctl函數是md_ioctl,當找對應的cmd命令字時,卻完全沒有類似CREATE_ARRAY的命令,那麼就說明md設備並不是通過ioctl函數來創建的。其實如果我們仔細閱讀一下md_ioctl函數的原型就會發現其實創建md設備根本就不在這個地方,函數原型如下:
6303 static int md_ioctl(struct block_device *bdev, fmode_t mode, 6304 unsigned int cmd, unsigned long arg)
6303行,第一個參數是struct block_device*,就是說對一個塊設備下發的命令,可是我們在創建md設備之前就沒有對應的md塊設備。
到此,線索就斷了,創建設備的入口到底是在哪裡呢?
此路不通,我們換一條路走。創建md設備總是要創建struct mddev結構吧,那就找哪裡申請了struct mddev內存結構不就可以了嗎?這個方法是可行的,可是struct mddev結構體是用kmalloc申請的,這是怎麼知道的呢?因為在函數md_init中根本就沒有申請struct mddev內存池的代碼,只好用kmalloc申請了。我們在md.c文件中搜索kmalloc再根據結果一條條找就能找出struct mddev創建的位置。但這裡我們使用一個更簡便的方法,那就是申請到了struct mddev結構總要進行初始化的吧,初始化函數是mddev_init,搜索這個函數,md.c文件中只有函數mddev_find()一處調用到,很顯然已經找到了struct mddev結構的創建入口了,那就接著往上層調用去找創建md設備的入口函數吧。
我們可以找到這樣的調用關系,箭頭表示調用關系:
mddev_find() <--- md_alloc() <--- md_probe()
md_probe()函數就是在模塊初始化函數md_init()中調用的blk_register_region()函數中的傳入參數,熟悉blk層的同學都知道,只要在用戶態創建了一個md設備,就會相應調用到內核probe()函數,而這裡傳入的probe()函數正是md_probe()。所以創建struct mddev結構體是由用戶態觸發的,而不是由內核態直接進行的。如果到了今天這個時代,還把這種瑣碎的事情放在內核態去做,你都不好意思說你是做linux開發的。做linux開發就是要引導時尚,崇尚簡單才是美。linux內核只提供機制,不提供具體實現策略。跟機制不相關的控制命令就需要從內核搬到用戶態,一方面簡化了內核,突出重點,方便了內核維護,另一方面在用戶態維護策略讓應用程序更加靈活並且方便了調試。
這樣我們就從內核態殺到了用戶態,用戶態程序就是大名鼎鼎的mdadm,網上隨便一搜就是一大堆人雲亦雲的文章,但最好的文章不是在網上,而是用命令man mdadm。用命令mdadm create來創建一個陣列,這裡不去閱讀mdadm的代碼,因為這是用戶態程序不是我們閱讀的重點,其次這些代碼也很簡單基本上學過初中英語的同學都能看得懂。我們需要知道的是mdadm create命令最終會調用mknod()函數來創建一個/dev/md*設備,這樣內核也就相應有了struct mddev結構體,這時這個結構體還是一個空結構體,空的意思就是說這個陣列沒有設置屬性,沒有對應的物理磁盤,沒有運行陣列。
到這個時候既然已經有了md設備,那就輪到md_ioctl上場的時候了,這個函數對應的ioctl命令字在文件include\linux\raid\md_u.h:
36 /* ioctls */ 37 38 /* status */ 39 #define RAID_VERSION _IOR (MD_MAJOR, 0x10, mdu_version_t) 40 #define GET_ARRAY_INFO _IOR (MD_MAJOR, 0x11, mdu_array_info_t) 41 #define GET_DISK_INFO _IOR (MD_MAJOR, 0x12, mdu_disk_info_t) 42 #define PRINT_RAID_DEBUG _IO (MD_MAJOR, 0x13) 43 #define RAID_AUTORUN _IO (MD_MAJOR, 0x14) 44 #define GET_BITMAP_FILE _IOR (MD_MAJOR, 0x15, mdu_bitmap_file_t) 45 46 /* configuration */ 47 #define CLEAR_ARRAY _IO (MD_MAJOR, 0x20) 48 #define ADD_NEW_DISK _IOW (MD_MAJOR, 0x21, mdu_disk_info_t) 49 #define HOT_REMOVE_DISK _IO (MD_MAJOR, 0x22) 50 #define SET_ARRAY_INFO _IOW (MD_MAJOR, 0x23, mdu_array_info_t) 51 #define SET_DISK_INFO _IO (MD_MAJOR, 0x24) 52 #define WRITE_RAID_INFO _IO (MD_MAJOR, 0x25) 53 #define UNPROTECT_ARRAY _IO (MD_MAJOR, 0x26) 54 #define PROTECT_ARRAY _IO (MD_MAJOR, 0x27) 55 #define HOT_ADD_DISK _IO (MD_MAJOR, 0x28) 56 #define SET_DISK_FAULTY _IO (MD_MAJOR, 0x29) 57 #define HOT_GENERATE_ERROR _IO (MD_MAJOR, 0x2a) 58 #define SET_BITMAP_FILE _IOW (MD_MAJOR, 0x2b, int) 59 60 /* usage */ 61 #define RUN_ARRAY _IOW (MD_MAJOR, 0x30, mdu_param_t) 62 /* 0x31 was START_ARRAY */ 63 #define STOP_ARRAY _IO (MD_MAJOR, 0x32) 64 #define STOP_ARRAY_RO _IO (MD_MAJOR, 0x33) 65 #define RESTART_ARRAY_RW _IO (MD_MAJOR, 0x34)
這個文件為什麼不放在md目錄而放在include目錄下?是因為文件裡的內容是用戶態跟內核態共用的,如果是內核態單獨用的就沒有必要放在這裡了。
對於陣列的創建流程,最關心的命令字有:
SET_ARRAY_INFO 設置陣列信息
ADD_NEW_DISK 添加磁盤到陣列
RUN_ARRAY 運行陣列
首先看設置陣列信息,這個函數是這三個函數中最簡單的一個:
6000 /* 6001 * set_array_info is used two different ways 6002 * The original usage is when creating a new array. 6003 * In this usage, raid_disks is > 0 and it together with 6004 * level, size, not_persistent,layout,chunksize determine the 6005 * shape of the array. 6006 * This will always create an array with a type-0.90.0 superblock. 6007 * The newer usage is when assembling an array. 6008 * In this case raid_disks will be 0, and the major_version field is 6009 * use to determine which style super-blocks are to be found on the devices. 6010 * The minor and patch _version numbers are also kept incase the 6011 * super_block handler wishes to interpret them. 6012 */ 6013 static int set_array_info(struct mddev * mddev, mdu_array_info_t *info) 6014 { 6015 6016 if (info->raid_disks == 0) { 6017 /* just setting version number for superblock loading */ 6018 if (info->major_version < 0 || 6019 info->major_version >= ARRAY_SIZE(super_types) || 6020 super_types[info->major_version].name == NULL) { 6021 /* maybe try to auto-load a module? */ 6022 printk(KERN_INFO 6023 "md: superblock version %d not known\n", 6024 info->major_version); 6025 return -EINVAL; 6026 } 6027 mddev->major_version = info->major_version; 6028 mddev->minor_version = info->minor_version; 6029 mddev->patch_version = info->patch_version; 6030 mddev->persistent = !info->not_persistent; 6031 /* ensure mddev_put doesn't delete this now that there 6032 * is some minimal configuration. 6033 */ 6034 mddev->ctime = get_seconds(); 6035 return 0; 6036 } 6037 mddev->major_version = MD_MAJOR_VERSION; 6038 mddev->minor_version = MD_MINOR_VERSION; 6039 mddev->patch_version = MD_PATCHLEVEL_VERSION; 6040 mddev->ctime = get_seconds(); 6041 6042 mddev->level = info->level; 6043 mddev->clevel[0] = 0; 6044 mddev->dev_sectors = 2 * (sector_t)info->size; 6045 mddev->raid_disks = info->raid_disks; 6046 /* don't set md_minor, it is determined by which /dev/md* was 6047 * openned 6048 */ 6049 if (info->state & (1<<MD_SB_CLEAN)) 6050 mddev->recovery_cp = MaxSector; 6051 else 6052 mddev->recovery_cp = 0; 6053 mddev->persistent = ! info->not_persistent; 6054 mddev->external = 0; 6055 6056 mddev->layout = info->layout; 6057 mddev->chunk_sectors = info->chunk_size >> 9; 6058 6059 mddev->max_disks = MD_SB_DISKS; 6060 6061 if (mddev->persistent) 6062 mddev->flags = 0; 6063 set_bit(MD_CHANGE_DEVS, &mddev->flags); 6064 6065 mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9; 6066 mddev->bitmap_info.default_space = 64*2 - (MD_SB_BYTES >> 9); 6067 mddev->bitmap_info.offset = 0; 6068 6069 mddev->reshape_position = MaxSector; 6070 6071 /* 6072 * Generate a 128 bit UUID 6073 */ 6074 get_random_bytes(mddev->uuid, 16); 6075 6076 mddev->new_level = mddev->level; 6077 mddev->new_chunk_sectors = mddev->chunk_sectors; 6078 mddev->new_layout = mddev->layout; 6079 mddev->delta_disks = 0; 6080 mddev->reshape_backwards = 0; 6081 6082 return 0; 6083 }
首先看注釋,這個函數有兩種用途,一是用於創建陣列,當創建陣列時,raid_disk>0,另一種用途是assemble陣列,這時raid_disk==0。
那這裡的raid_disk到底是多少呢?注釋裡又有這樣的一句話,如果raid_disk>0,那麼直接創建0.90陣列超級塊,很顯然,我們要創建的陣列超級塊是1.2的,所以6037-6080只是用於兼容老版本的陣列的,需要閱讀的代碼只有6016行if語句中的那幾行代碼。
6027-6029行,設置陣列超級塊版本號。
6030行,設置persistent屬性,就是說超級塊是保存在磁盤上還是只放在內存中啊,這裡我們都是保存在磁盤上,以後看到這個屬性就永遠為true。
6034行,設置陣列創建時間。
那麼是在什麼時候才開始設置陣列屬性呢?比如說陣列級別?別急,好戲還在後頭。
接著看ADD_NEW_DISK對應的處理函數:
5672 static int add_new_disk(struct mddev * mddev, mdu_disk_info_t *info) 5673 { 5674 char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; 5675 struct md_rdev *rdev; 5676 dev_t dev = MKDEV(info->major,info->minor); 5677 5678 if (info->major != MAJOR(dev) || info->minor != MINOR(dev)) 5679 return -EOVERFLOW; 5680 5681 if (!mddev->raid_disks) { 5682 int err; 5683 /* expecting a device which has a superblock */ 5684 rdev = md_import_device(dev, mddev->major_version, mddev->minor_version); 5685 if (IS_ERR(rdev)) { 5686 printk(KERN_WARNING 5687 "md: md_import_device returned %ld\n", 5688 PTR_ERR(rdev)); 5689 return PTR_ERR(rdev); 5690 } 5691 if (!list_empty(&mddev->disks)) { 5692 struct md_rdev *rdev0 5693 = list_entry(mddev->disks.next, 5694 struct md_rdev, same_set); 5695 err = super_types[mddev->major_version] 5696 .load_super(rdev, rdev0, mddev->minor_version); 5697 if (err < 0) { 5698 printk(KERN_WARNING 5699 "md: %s has different UUID to %s\n", 5700 bdevname(rdev->bdev,b), 5701 bdevname(rdev0->bdev,b2)); 5702 export_rdev(rdev); 5703 return -EINVAL; 5704 } 5705 } 5706 err = bind_rdev_to_array(rdev, mddev); 5707 if (err) 5708 export_rdev(rdev); 5709 return err; 5710 }
這個函數只截取了一部分,因為這一次添加磁盤流程只會走到這一部分代碼,首先注意到函數的參數:第一個參數是struct mddev結構體,這個結構體域比較多,我們會在後面用到具體域時再講,第二個參數是表示一個要加入陣列的磁盤,這裡用到了該結構體的兩個域,major和minor,表示磁盤主設備號和次設備號。
5676行,根據主設備號和次設備號算出dev_t。
5678行,這裡為什麼還要再檢查一下呢?返回的錯誤碼叫溢出,意思是說很久很久以前linux中設備還不是很多的時候dev_t只要用16位來表示就可以了,然而隨著linux服務器單一種類外設數量越來越多,dev_t擴展到32位,所以這裡檢查保證輸入major,minor的正確。
5681行,這裡還未添加磁盤,所以進入這個if分支。
5684行,創建磁盤struct md_rdev結構,繼續跟入到函數中:
3236 /* 3237 * Import a device. If 'super_format' >= 0, then sanity check the superblock 3238 * 3239 * mark the device faulty if: 3240 * 3241 * - the device is nonexistent (zero size) 3242 * - the device has no valid superblock 3243 * 3244 * a faulty rdev _never_ has rdev->sb set. 3245 */ 3246 static struct md_rdev *md_import_device(dev_t newdev, int super_format, int super_minor) 3247 { 3248 char b[BDEVNAME_SIZE]; 3249 int err; 3250 struct md_rdev *rdev; 3251 sector_t size; 3252 3253 rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); 3254 if (!rdev) { 3255 printk(KERN_ERR "md: could not alloc mem for new device!\n"); 3256 return ERR_PTR(-ENOMEM); 3257 } 3258 3259 err = md_rdev_init(rdev); 3260 if (err) 3261 goto abort_free; 3262 err = alloc_disk_sb(rdev); 3263 if (err) 3264 goto abort_free; 3265 3266 err = lock_rdev(rdev, newdev, super_format == -2); 3267 if (err) 3268 goto abort_free; 3269 3270 kobject_init(&rdev->kobj, &rdev_ktype); 3271 3272 size = i_size_read(rdev->bdev->bd_inode) >> BLOCK_SIZE_BITS; 3273 if (!size) { 3274 printk(KERN_WARNING 3275 "md: %s has zero or unknown size, marking faulty!\n", 3276 bdevname(rdev->bdev,b)); 3277 err = -EINVAL; 3278 goto abort_free; 3279 } 3280 3281 if (super_format >= 0) { 3282 err = super_types[super_format]. 3283 load_super(rdev, NULL, super_minor); 3284 if (err == -EINVAL) { 3285 printk(KERN_WARNING 3286 "md: %s does not have a valid v%d.%d " 3287 "superblock, not importing!\n", 3288 bdevname(rdev->bdev,b), 3289 super_format, super_minor); 3290 goto abort_free; 3291 } 3292 if (err < 0) { 3293 printk(KERN_WARNING 3294 "md: could not read %s's sb, not importing!\n", 3295 bdevname(rdev->bdev,b)); 3296 goto abort_free; 3297 } 3298 } 3299 if (super_format == -1) 3300 /* hot-add for 0.90, or non-persistent: so no badblocks */ 3301 rdev->badblocks.shift = -1; 3302 3303 return rdev; 3304 3305 abort_free: 3306 if (rdev->bdev) 3307 unlock_rdev(rdev); 3308 md_rdev_clear(rdev); 3309 kfree(rdev); 3310 return ERR_PTR(err); 3311 }
 
3252行,創建一個struct md_rdev結構體。
3259行,初始化struct md_rdev結構體。
3262行,申請一個page頁,用於存放磁盤超級塊信息。
3266行,對磁盤加鎖,防止被其他程序操作如mount, 分區等。
3270行,初始化struct md_rdev磁盤kobject結構。
3272行,讀磁盤大小,判斷是否合法。
3281行,陣列超級塊是1.2版本的,進入if分支。
3282行,讀入陣列超級塊信息,具體調用的函數是:
1450 static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_version)
這個函數很簡單,根據超級塊版本從磁盤上讀入陣列超級塊信息並保存到md_rdev->sb_page中,做一些基本的校驗和檢查,並將超級塊信息保存到struct md_rdev結構中。到這裡就返回到add_new_disk函數,5684行返回的rdev就含有從磁盤上加載的超級塊信息。
5691行,由於陣列中還沒有磁盤,所以list_empty(&mddev->disks)成立,不會進入if分支。
5706行,建立陣列struct mddev和磁盤struct md_rdev結構之間的聯系,進函數:
2077 static int bind_rdev_to_array(struct md_rdev * rdev, struct mddev * mddev) 2078 { 2079 char b[BDEVNAME_SIZE]; 2080 struct kobject *ko; 2081 char *s; 2082 int err; 2083 2084 if (rdev->mddev) { 2085 MD_BUG(); 2086 return -EINVAL; 2087 } 2088 2089 /* prevent duplicates */ 2090 if (find_rdev(mddev, rdev->bdev->bd_dev)) 2091 return -EEXIST; 2092 2093 /* make sure rdev->sectors exceeds mddev->dev_sectors */ 2094 if (rdev->sectors && (mddev->dev_sectors == 0 || 2095 rdev->sectors < mddev->dev_sectors)) { 2096 if (mddev->pers) { 2097 /* Cannot change size, so fail 2098 * If mddev->level <= 0, then we don't care 2099 * about aligning sizes (e.g. linear) 2100 */ 2101 if (mddev->level > 0) 2102 return -ENOSPC; 2103 } else 2104 mddev->dev_sectors = rdev->sectors; 2105 } 2106 2107 /* Verify rdev->desc_nr is unique. 2108 * If it is -1, assign a free number, else 2109 * check number is not in use 2110 */ 2111 if (rdev->desc_nr < 0) { 2112 int choice = 0; 2113 if (mddev->pers) choice = mddev->raid_disks; 2114 while (find_rdev_nr(mddev, choice)) 2115 choice++; 2116 rdev->desc_nr = choice; 2117 } else { 2118 if (find_rdev_nr(mddev, rdev->desc_nr)) 2119 return -EBUSY; 2120 } 2121 if (mddev->max_disks && rdev->desc_nr >= mddev->max_disks) { 2122 printk(KERN_WARNING "md: %s: array is limited to %d devices\n", 2123 mdname(mddev), mddev->max_disks); 2124 return -EBUSY; 2125 } 2126 bdevname(rdev->bdev,b); 2127 while ( (s=strchr(b, '/')) != NULL) 2128 *s = '!'; 2129 2130 rdev->mddev = mddev; 2131 printk(KERN_INFO "md: bind<%s>\n", b); 2132 2133 if ((err = kobject_add(&rdev->kobj, &mddev->kobj, "dev-%s", b))) 2134 goto fail; 2135 2136 ko = &part_to_dev(rdev->bdev->bd_part)->kobj; 2137 if (sysfs_create_link(&rdev->kobj, ko, "block")) 2138 /* failure here is OK */; 2139 rdev->sysfs_state = sysfs_get_dirent_safe(rdev->kobj.sd, "state"); 2140 2141 list_add_rcu(&rdev->same_set, &mddev->disks); 2142 bd_link_disk_holder(rdev->bdev, mddev->gendisk); 2143 2144 /* May as well allow recovery to be retried once */ 2145 mddev->recovery_disabled++; 2146 2147 return 0; 2148 2149 fail: 2150 printk(KERN_WARNING "md: failed to register dev-%s for %s\n", 2151 b, mdname(mddev)); 2152 return err; 2153 }
2090行,檢查是否磁盤已經加入陣列了,加過就不必重復添加。2094-2105行,比較磁盤大小,記錄最小的磁盤空間。2111行,desc_nr分配,這個號只描述加入陣列的早晚。2130行,建立struct md_rdev到mddev的關聯。2133-2139行,建立sysfs相關狀態和鏈接。2141行,建立mddev到struct md_rdev的關聯。add_new_disk就這麼快結束了,簡單地說就是創建struct md_rdev結構並與struct mddev結構之間創建聯系。第三個命令字RUN_ARRAY的處理過程具有重要的意義,並且其過程不是三言兩語能夠說完的,我們把該命令字處理流程放到下一個小節單獨來講。
出處:http://blog.csdn.net/liumangxiong