linux下raid(md)驅動源碼解析

原文鏈接:http://blog.csdn.net/liumangxiong

1. 概述

最近花了一段時間認真地學習了一下md代碼,並且在原代碼的基礎上開發了一系列的新功能,這些新功能讓md更完善、更適合於企業大容量存儲,通過增加陣列緩存和bitmap優化大大提升了存儲速度,提高了數據的可靠性,在任何掉電的情況下保證數據一致性,超級塊異常情況下完全不影響陣列使用,完全控制了踢盤問題,簡化了用戶操作。簡單地概括一下,就是讓存儲不再有門檻。說了這麼多,其實想表達的意思就是md的學習之路並非十分順利,特此寫此博文與所有兄弟姐妹們共享一下我的學習經驗,如果您看完之後能有所收穫,那就不枉費我下功夫寫這些技術文章,同時您的感想和回覆也將是我能夠繼續寫下去的最大動力。

md代碼在內核樹中十多年經久不衰,跟作者neil brown,一位久經考驗的開源戰士,是分不開的。neil brown的博客地址是http://blog.neil.brown.name/,這個網址非常重要,因爲我發現自己遇到疑難問題的時候80%問題都能在這裏找到答案,所以有空的時間從到頭到尾掃一遍,可以解決許多“爲什麼要這樣做”的問題。

最初我看內核代碼都是從module_init開始看的,可是自從學習了kconfig之後,我就改變了一下習慣,從kconfig和Makefile開始看代碼了,如果誰有更好的辦法請分享一下謝謝。

下面就來看一下drivers/md/Kconfig

[cpp] view plain copy

  1. #  
  2. # Block device driver configuration  
  3. #  
  4.   
  5. menuconfig MD  
  6.      bool "Multiple devices driver support (RAID and LVM)"  
  7.      depends on BLOCK  
  8.      help  
  9.        Support multiple physical spindles through a single logical device.  
  10.        Required for RAID and logical volume management.  
  11.   
  12. if MD  
  13. config BLK_DEV_MD  
  14.      tristate "RAID support"  
  15.      ---help---  
  16. #...省略若干  
  17. endif # MD  

menuconfig這個單詞已經很熟悉了,因爲自從開始學習編譯內核的時候就有這樣一個命令make menuconfig,當我們在內核源代碼目錄下敲下這個命令時,就會出現一個文本配置界面,在文本配置界面中可以選擇需要編譯到內核的模塊,那有了這裏的menuconfig MD,文本配置界面中纔會有MD的一項,當選中MD之後,文本配置界面纔會出現config BLK_DEV_MD和之後的選項,這些選項一般有兩種狀態,一個是tristate,表示內建、模塊、移除三種狀態,另一個是bool,表示選中或不選中。depends on表示正向依賴,如果選上了這個模塊,那麼正向依賴的模塊也會自動選上,正向依賴模塊遞歸所依賴的模塊也會選上。一般把這些驅動模塊選擇爲按模塊加載,可以方便修改調試。

知道了Kconfig的基本配置,就可以按需定製內核,把不需要的統統去掉,也明白了爲什麼有時候系統的lib/module/下面爲什麼沒有對應的模塊了。

而對於閱讀源代碼來說,知道了哪些源代碼編譯進了內核,哪些源代碼編譯進了模塊,哪些源代碼沒有編譯,這樣就可以按需閱讀源代碼了。另外在源代碼中有一些編譯選項類似:

#ifdef CONFIG_*
     //code
#endif

那麼*號就是對應這裏Kconfig中config後面的選項,如果這裏選項選上,那麼編譯選項就爲真。這些編譯選項還用於對應的Makefile文件中,如md對應的Makefile文件有:

[cpp] view plain copy

  1. obj-$(CONFIG_MD_RAID0)          += raid0.o  
  2. obj-$(CONFIG_MD_RAID1)          += raid1.o  
  3. obj-$(CONFIG_MD_RAID10)          += raid10.o  
  4. obj-$(CONFIG_MD_RAID456)     += raid456.o  
  5. obj-$(CONFIG_MD_MULTIPATH)     += multipath.o  
  6. obj-$(CONFIG_MD_FAULTY)          += faulty.o  
  7. obj-$(CONFIG_BLK_DEV_MD)     += md-mod.o  

如果在Kconfig中選中了config BLK_DEV_MD,那麼Makefile就要編譯生成md-mod.ko模塊,那這個模塊由哪幾個文件生成的呢?看Makefile中定義:

md-mod-y     += md.o bitmap.o
raid456-y     += raid5.o

就是說在Kconfig中選中了config BLK_DEV_MD,md.c, bitmap.c就會被編譯。同理,config MD_RAID456被選中,raid5.c就會被編譯。這裏,我們也可以知道,哪一個模塊對應着哪幾個源文件。例如要加載md-mod.ko,那麼就需要md.c, bitmap.c。修改raid5代碼時,只需要重新編譯raid5.c一個文件就可以了。

2. md源代碼解析

在編譯完成linux內核源代碼的時候,drivers/md目錄下會生成多個ko文件,那麼這些內核模塊哪一個先加載,哪一個後加載的呢?例如md-mod.ko, raid5.ko, raid10.ko,這些模塊是一起加載的呢,還是有先後順序呢?如果熟悉linux內核編程的話,知道有一個request_module函數,這個函數用於請求加載一個模塊,但這個函數並不能說明一個模塊對另一個模塊的依賴關係。準確的信息還是來自於Kconfig,這裏只抽取Kconfig中相關的部分:
config BLK_DEV_MD
     tristate "RAID support"
config MD_RAID10
     tristate "RAID-10 (mirrored striping) mode"
     depends on BLK_DEV_MD
config MD_RAID456
     tristate "RAID-4/RAID-5/RAID-6 mode"
     depends on BLK_DEV_MD

從這裏我們可以看出,raid5.ko, raid10.ko都是依賴於md-mod.ko的(參見如上標紅部分的關聯),這就決定了我們的閱讀方向是從md-mod.ko中開始的。

那麼md-mod.ko中又是從哪個文件開始的呢?這就要找module_init函數,這個函數在md.c中定義的,那麼就從這裏入手。
8416 static int __init md_init(void)
8417 {
8418         int ret = -ENOMEM;
8419 
8420         md_wq = alloc_workqueue("md", WQ_MEM_RECLAIM, 0);
8421         if (!md_wq)
8422                 goto err_wq;
8423 
8424         md_misc_wq = alloc_workqueue("md_misc", 0, 0);
8425         if (!md_misc_wq)
8426                 goto err_misc_wq;
8427 
8428         if ((ret = register_blkdev(MD_MAJOR, "md")) < 0)
8429                 goto err_md;
8430 
8431         if ((ret = register_blkdev(0, "mdp")) < 0)
8432                 goto err_mdp;
8433         mdp_major = ret;
8434 
8435         blk_register_region(MKDEV(MD_MAJOR, 0), 1UL<<MINORBITS, THIS_MODULE,
8436                             md_probe, NULL, NULL);
8437         blk_register_region(MKDEV(mdp_major, 0), 1UL<<MINORBITS, THIS_MODULE,
8438                             md_probe, NULL, NULL);
8439 
8440         register_reboot_notifier(&md_notifier);
8441         raid_table_header = register_sysctl_table(raid_root_table);
8442 
8443         md_geninit();
8444         return 0;
8445 
8446 err_mdp:
8447         unregister_blkdev(MD_MAJOR, "md");
8448 err_md:
8449         destroy_workqueue(md_misc_wq);
8450 err_misc_wq:
8451         destroy_workqueue(md_wq);
8452 err_wq:
8453         return ret;
8454 }

模塊的初始化過程看起來異常地簡單,這就像有些人表面看起來十分普通,內心裏卻無比地強大,所以不要只看外表,還要聽其言觀其行。內在的美麗比外表的榮華更具吸引力和持久性。

8420和8424行,分別創建了工作隊列,md_wq是用於flush命令的,另一個md_misc_wq,misc是miscellaneous的簡寫,是雜項的意思,用於處理一些零零碎碎的事情。

8428和8431行,創建了兩個塊設備,剛開始我只注意md的設備,壓根沒在意mdp,搜索變量mdp_major,在函數autorun_devices中使用了這個變量:
5474                 if (part) {
5475                         dev = MKDEV(mdp_major,
5476                                     rdev0->preferred_minor << MdpMinorShift);
5477                         unit = MINOR(dev) >> MdpMinorShift;
5478                 } else {
5479                         dev = MKDEV(MD_MAJOR, rdev0->preferred_minor);
5480                         unit = MINOR(dev);
5481                 }

5474行,變量part是函數傳入參數,表示磁盤第幾個分區,那麼就知道mdp_major中字母p是表示part的意思,而mdp_major就表示用磁盤分區創建的陣列。

8435和8437行,創建了兩個region,這兩個函數的作用是在用戶態創建了一個/dev/md*設備時,內核態就會對應調用md_probe創建一個mddev結構體,之後在用戶態對/dev/md*的操作到內核態就相應地對mddev的操作了。

8440行,註冊關機回調函數,主要作用是停止陣列線程,刷數據操作。

8441行,註冊sysctl函數,用於控制陣列最小和最大的sync速度。

8443行,註冊proc函數,於是有了目錄/proc/mdstat,該目錄的顯示由函數md_seq_show控制。
md初始化代碼就這樣輕鬆地完成了,可是回到我們的初衷,仍然對md設備一無所知。那麼md設備是如何創建的呢?創建的過程又是怎麼的呢?一個陣列擁有哪些資源?下一節我們直入核心,開始閱讀陣列創建的過程。

3. md驅動陣列創建過程

按照常理出牌,我們到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的處理過程具有重要的意義,並且其過程不是三言兩語能夠說完的,我們把該命令字處理流程放到下一個小節單獨來講。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章