Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3879963
  • 博文数量: 146
  • 博客积分: 3918
  • 博客等级: 少校
  • 技术积分: 8585
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-17 13:52
个人简介

个人微薄: weibo.com/manuscola

文章分类

全部博文(146)

文章存档

2016年(3)

2015年(2)

2014年(5)

2013年(42)

2012年(31)

2011年(58)

2010年(5)

分类: LINUX

2011-10-13 20:43:53

    add_partition,是添加磁盘分区信息的函数,负责向通用磁盘数据结构添加一个新的分区:
    通用磁盘数据结构如下:

  1. struct gendisk {
  2.     int major;            /* major number of driver */
  3.     int first_minor;
  4.     int minors; /* maximum number of minors, =1 for
  5.                                          * disks that can't be partitioned. */
  6.     char disk_name[32];        /* name of major driver */
  7.     struct hd_struct **part;    /* [indexed by minor] */
  8.     struct block_device_operations *fops;
  9.     struct request_queue *queue;
  10.     void *private_data;
  11.     sector_t capacity;

  12.     int flags;
  13.     struct device *driverfs_dev; // FIXME: remove
  14.     struct device dev;
  15.     struct kobject *holder_dir;
  16.     struct kobject *slave_dir;

  17.     struct timer_rand_state *random;
  18.     int policy;

  19.     atomic_t sync_io;        /* RAID */
  20.     unsigned long stamp;
  21.     int in_flight;
  22. #ifdef    CONFIG_SMP
  23.     struct disk_stats *dkstats;
  24. #else
  25.     struct disk_stats dkstats;
  26. #endif
  27.     struct work_struct async_notify;
  28. };
  29. vents *ev;
  30. #ifdef CONFIG_BLK_DEV_INTEGRITY
  31.     struct blk_integrity *integrity;
  32. #endif
  33.     int node_id;
  34. };
    蓝色的struct hd_struct类型 指向分区相关的数据区。看下下面的函数,

    首先是分配一个hd_struct类型的变量作为分区的结构:disk的第part个分区指针指向该结构。
  1. p = kzalloc(sizeof(*p), GFP_KERNEL);
  1. disk->part[part-1] = p;
    然后是将分区的起始扇区赋值为入参start,扇区个数赋值为入参len,partno赋值为入参part。SCSI磁盘的分区个数最多为15个。

  1. void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags)
  2. {
  3.     struct hd_struct *p;
  4.     int err;

  5.     p = kzalloc(sizeof(*p), GFP_KERNEL);
  6.     if (!p)
  7.         return;

  8.     if (!init_part_stats(p)) {
  9.         kfree(p);
  10.         return;
  11.     }
  12.     p->start_sect = start;
  13.     p->nr_sects = len;
  14.     p->partno = part;
  15.     p->policy = disk->policy;

  16.     if (isdigit(disk->dev.bus_id[strlen(disk->dev.bus_id)-1]))
  17.         snprintf(p->dev.bus_id, BUS_ID_SIZE,
  18.         "%sp%d", disk->dev.bus_id, part);
  19.     else
  20.         snprintf(p->dev.bus_id, BUS_ID_SIZE,
  21.              "%s%d", disk->dev.bus_id, part);

  22.     device_initialize(&p->dev);
  23.     p->dev.devt = MKDEV(disk->major, disk->first_minor + part);
  24.     p->dev.class = &block_class;
  25.     p->dev.type = &part_type;
  26.     p->dev.parent = &disk->dev;
  27.     disk->part[part-1] = p;

  28.     /* delay uevent until 'holders' subdir is created */
  29.     p->dev.uevent_suppress = 1;
  30.     device_add(&p->dev);
  31.     partition_sysfs_add_subdir(p);
  32.     p->dev.uevent_suppress = 0;
  33.     if (flags & ADDPART_FLAG_WHOLEDISK)
  34.         err = device_create_file(&p->dev, &dev_attr_whole_disk);

  35.     /* suppress uevent if the disk supresses it */
  36.     if (!disk->dev.uevent_suppress)
  37.         kobject_uevent(&p->dev.kobj, KOBJ_ADD);
  38. }

    接下来的问题是,每个磁盘有几个分区,每个分区的起始扇区是多少,每个分区的扇区个数是多少是怎么来的。

    我们先看下rescan_partitions 中添加分区的流程
  1. struct parsed_partitions *state;
 ...

  1. for (p = 1; p < state->limit; p++) {
  2.         sector_t size = state->parts[p].size;
  3.         sector_t from = state->parts[p].from;
  4.         if (!size)
  5.             continue;
  6.         if (from + size > get_capacity(disk)) {
  7.             printk(" %s: p%d exceeds device capacity\n",
  8.                 disk->disk_name, p);
  9.         }
  10.         add_partition(disk, p, from, size, state->parts[p].flags);
  11. #ifdef CONFIG_BLK_DEV_MD
  12.         if (state->parts[p].flags & ADDPART_FLAG_RAID)
  13.             md_autodetect_dev(bdev->bd_dev+p);
  14. #endif
  15.     }
    我们看到,add_partition的入参比如分区起始扇区,分区扇区的个数等都是从struct parsed_partitions *state 获取到的,那么state指向的数据是从哪里来的呢,这个问题浮出水面,就到了检测分区的函数check_partition闪亮登场的时刻了。

  1. static struct parsed_partitions *
  2. check_partition(struct gendisk *hd, struct block_device *bdev)
  3. {
  4.     struct parsed_partitions *state;
  5.     int i, res, err;

  6.     state = kzalloc(sizeof(struct parsed_partitions), GFP_KERNEL);
  7.     if (!state)
  8.         return NULL;
  9.     state->pp_buf = (char *)__get_free_page(GFP_KERNEL);
  10.     if (!state->pp_buf) {
  11.         kfree(state);
  12.         return NULL;
  13.     }
  14.     state->pp_buf[0] = '\0';

  15.     state->bdev = bdev;
  16.     disk_name(hd, 0, state->name);
  17.     snprintf(state->pp_buf, PAGE_SIZE, " %s:", state->name);
  18.     if (isdigit(state->name[strlen(state->name)-1]))
  19.         sprintf(state->name, "p");

  20.     state->limit = disk_max_parts(hd);
  21.     i = res = err = 0;
  22.     while (!res && check_part[i]) {
  23.         memset(&state->parts, 0, sizeof(state->parts));
  24.         res = check_part[i++](state);
  25.         if (res < 0) {
  26.             /* We have hit an I/O error which we don't report now.
  27.              * But record it, and let the others do their job.
  28.              */
  29.             err = res;
  30.             res = 0;
  31.         }

  32.     }
  33.     if (res > 0) {
  34.         printk(KERN_INFO "%s", state->pp_buf);

  35.         free_page((unsigned long)state->pp_buf);
  36.         return state;
  37.     }
  38.     if (state->access_beyond_eod)
  39.         err = -ENOSPC;
  40.     if (err)
  41.     /* The partition is unrecognized. So report I/O errors if there were any */
  42.         res = err;
  43.     if (!res)
  44.         strlcat(state->pp_buf, " unknown partition table\n", PAGE_SIZE);
  45.     else if (warn_no_part)
  46.         strlcat(state->pp_buf, " unable to read partition table\n", PAGE_SIZE);

  47.     printk(KERN_INFO "%s", state->pp_buf);

  48.     free_page((unsigned long)state->pp_buf);
  49.     kfree(state);
  50.     return ERR_PTR(res);
  51. }
    很吓人是不?原理起始很简单,最关键语句是绿色的那一句。我们知道有很多种类型的磁盘,不可能存在一个处理函数,能够解析出所有磁盘分区信息。那怎么办呢。神农 尝百草,一个一个的实验。我们有个函数数组 check_part,每种函数解析一种磁盘分区信息。那个函数能成功的解析磁盘分区信息,就不需要继续尝试了,可以打完收工了。当然解析出来的数据 存放在struct parsed_partitions 类型的结构体state中。

    那么神农尝百草,那么一共有多少种草呢?
    看下函数数组的定义:
  1. static int (*check_part[])(struct parsed_partitions *, struct block_device *) = {
  2.     /*
  3.      * Probe partition formats with tables at disk address 0
  4.      * that also have an ADFS boot block at 0xdc0.
  5.      */
  6. #ifdef CONFIG_ACORN_PARTITION_ICS
  7.     adfspart_check_ICS,
  8. #endif
  9. #ifdef CONFIG_ACORN_PARTITION_POWERTEC
  10.     adfspart_check_POWERTEC,
  11. #endif
  12. #ifdef CONFIG_ACORN_PARTITION_EESOX
  13.     adfspart_check_EESOX,
  14. #endif

  15.     /*
  16.      * Now move on to formats that only have partition info at
  17.      * disk address 0xdc0. Since these may also have stale
  18.      * PC/BIOS partition tables, they need to come before
  19.      * the msdos entry.
  20.      */
  21. #ifdef CONFIG_ACORN_PARTITION_CUMANA
  22.     adfspart_check_CUMANA,
  23. #endif
  24. #ifdef CONFIG_ACORN_PARTITION_ADFS
  25.     adfspart_check_ADFS,
  26. #endif

  27. #ifdef CONFIG_EFI_PARTITION
  28.     efi_partition,        /* this must come before msdos */
  29. #endif
  30. #ifdef CONFIG_SGI_PARTITION
  31.     sgi_partition,
  32. #endif
  33. #ifdef CONFIG_LDM_PARTITION
  34.     ldm_partition,        /* this must come before msdos */
  35. #endif
  36. #ifdef CONFIG_MSDOS_PARTITION
  37.     msdos_partition,
  38. #endif
  39. #ifdef CONFIG_OSF_PARTITION
  40.     osf_partition,
  41. #endif
  42. #ifdef CONFIG_SUN_PARTITION
  43.     sun_partition,
  44. #endif
  45. #ifdef CONFIG_AMIGA_PARTITION
  46.     amiga_partition,
  47. #endif
  48. #ifdef CONFIG_ATARI_PARTITION
  49.     atari_partition,
  50. #endif
  51. #ifdef CONFIG_MAC_PARTITION
  52.     mac_partition,
  53. #endif
  54. #ifdef CONFIG_ULTRIX_PARTITION
  55.     ultrix_partition,
  56. #endif
  57. #ifdef CONFIG_IBM_PARTITION
  58.     ibm_partition,
  59. #endif
  60. #ifdef CONFIG_KARMA_PARTITION
  61.     karma_partition,
  62. #endif
  63. #ifdef CONFIG_SYSV68_PARTITION
  64.     sysv68_partition,
  65. #endif
  66.     NULL
  67. };
    这么多函数,来自五湖四海,就是为了共同的目的,封装到了一起,就是为了获取分区的信息。把获取到信息存入结构体struct parsed_partitions *state;包含 分区的起始扇区,分区扇区的个数,flag等信息。
    定义如下:
  1. struct parsed_partitions {
  2.     char name[BDEVNAME_SIZE];
  3.     struct {
  4.         sector_t from;
  5.         sector_t size;
  6.         int flags;
  7.     } parts[MAX_PART];
  8.     int next;
  9.     int limit;
  10. };

参考文献:
1 Linux 那些事儿
2 Linux Kernel Source Code



阅读(8426) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~