add_partition,是添加磁盘分区信息的函数,负责向通用磁盘数据结构添加一个新的分区:
通用磁盘数据结构如下:
- struct gendisk {
-
int major; /* major number of driver */
-
int first_minor;
-
int minors; /* maximum number of minors, =1 for
-
* disks that can't be partitioned. */
-
char disk_name[32]; /* name of major driver */
-
struct hd_struct **part; /* [indexed by minor] */
-
struct block_device_operations *fops;
-
struct request_queue *queue;
-
void *private_data;
-
sector_t capacity;
-
-
int flags;
-
struct device *driverfs_dev; // FIXME: remove
-
struct device dev;
-
struct kobject *holder_dir;
-
struct kobject *slave_dir;
-
-
struct timer_rand_state *random;
-
int policy;
-
-
atomic_t sync_io; /* RAID */
-
unsigned long stamp;
-
int in_flight;
-
#ifdef CONFIG_SMP
-
struct disk_stats *dkstats;
-
#else
-
struct disk_stats dkstats;
-
#endif
-
struct work_struct async_notify;
-
};
-
vents *ev;
-
#ifdef CONFIG_BLK_DEV_INTEGRITY
-
struct blk_integrity *integrity;
-
#endif
-
int node_id;
-
};
蓝色的struct hd_struct类型 指向分区相关的数据区。看下下面的函数,
首先是分配一个hd_struct类型的变量作为分区的结构:disk的第part个分区指针指向该结构。
- p = kzalloc(sizeof(*p), GFP_KERNEL);
然后是将分区的起始扇区赋值为入参start,扇区个数赋值为入参len,partno赋值为入参part。SCSI磁盘的分区个数最多为15个。
- void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags)
-
{
-
struct hd_struct *p;
-
int err;
-
-
p = kzalloc(sizeof(*p), GFP_KERNEL);
-
if (!p)
-
return;
-
-
if (!init_part_stats(p)) {
-
kfree(p);
-
return;
-
}
-
p->start_sect = start;
-
p->nr_sects = len;
-
p->partno = part;
-
p->policy = disk->policy;
-
-
if (isdigit(disk->dev.bus_id[strlen(disk->dev.bus_id)-1]))
-
snprintf(p->dev.bus_id, BUS_ID_SIZE,
-
"%sp%d", disk->dev.bus_id, part);
-
else
-
snprintf(p->dev.bus_id, BUS_ID_SIZE,
-
"%s%d", disk->dev.bus_id, part);
-
-
device_initialize(&p->dev);
-
p->dev.devt = MKDEV(disk->major, disk->first_minor + part);
-
p->dev.class = &block_class;
-
p->dev.type = &part_type;
-
p->dev.parent = &disk->dev;
-
disk->part[part-1] = p;
-
-
/* delay uevent until 'holders' subdir is created */
-
p->dev.uevent_suppress = 1;
-
device_add(&p->dev);
-
partition_sysfs_add_subdir(p);
-
p->dev.uevent_suppress = 0;
-
if (flags & ADDPART_FLAG_WHOLEDISK)
-
err = device_create_file(&p->dev, &dev_attr_whole_disk);
-
-
/* suppress uevent if the disk supresses it */
-
if (!disk->dev.uevent_suppress)
-
kobject_uevent(&p->dev.kobj, KOBJ_ADD);
-
}
接下来的问题是,每个磁盘有几个分区,每个分区的起始扇区是多少,每个分区的扇区个数是多少是怎么来的。
我们先看下rescan_partitions 中添加分区的流程
- struct parsed_partitions *state;
...
- for (p = 1; p < state->limit; p++) {
-
sector_t size = state->parts[p].size;
-
sector_t from = state->parts[p].from;
-
if (!size)
-
continue;
-
if (from + size > get_capacity(disk)) {
-
printk(" %s: p%d exceeds device capacity\n",
-
disk->disk_name, p);
-
}
-
add_partition(disk, p, from, size, state->parts[p].flags);
-
#ifdef CONFIG_BLK_DEV_MD
-
if (state->parts[p].flags & ADDPART_FLAG_RAID)
-
md_autodetect_dev(bdev->bd_dev+p);
-
#endif
-
}
我们看到,add_partition的入参比如分区起始扇区,分区扇区的个数等都是从struct parsed_partitions *state 获取到的,那么state指向的数据是从哪里来的呢,这个问题浮出水面,就到了检测分区的函数check_partition闪亮登场的时刻了。
- static struct parsed_partitions *
-
check_partition(struct gendisk *hd, struct block_device *bdev)
-
{
-
struct parsed_partitions *state;
-
int i, res, err;
-
-
state = kzalloc(sizeof(struct parsed_partitions), GFP_KERNEL);
-
if (!state)
-
return NULL;
-
state->pp_buf = (char *)__get_free_page(GFP_KERNEL);
-
if (!state->pp_buf) {
-
kfree(state);
-
return NULL;
-
}
-
state->pp_buf[0] = '\0';
-
-
state->bdev = bdev;
-
disk_name(hd, 0, state->name);
-
snprintf(state->pp_buf, PAGE_SIZE, " %s:", state->name);
-
if (isdigit(state->name[strlen(state->name)-1]))
-
sprintf(state->name, "p");
-
-
state->limit = disk_max_parts(hd);
-
i = res = err = 0;
-
while (!res && check_part[i]) {
-
memset(&state->parts, 0, sizeof(state->parts));
-
res = check_part[i++](state);
-
if (res < 0) {
-
/* We have hit an I/O error which we don't report now.
-
* But record it, and let the others do their job.
-
*/
-
err = res;
-
res = 0;
-
}
-
-
}
-
if (res > 0) {
-
printk(KERN_INFO "%s", state->pp_buf);
-
-
free_page((unsigned long)state->pp_buf);
-
return state;
-
}
-
if (state->access_beyond_eod)
-
err = -ENOSPC;
-
if (err)
-
/* The partition is unrecognized. So report I/O errors if there were any */
-
res = err;
-
if (!res)
-
strlcat(state->pp_buf, " unknown partition table\n", PAGE_SIZE);
-
else if (warn_no_part)
-
strlcat(state->pp_buf, " unable to read partition table\n", PAGE_SIZE);
-
-
printk(KERN_INFO "%s", state->pp_buf);
-
-
free_page((unsigned long)state->pp_buf);
-
kfree(state);
-
return ERR_PTR(res);
-
}
很吓人是不?原理起始很简单,最关键语句是绿色的那一句。我们知道有很多种类型的磁盘,不可能存在一个处理函数,能够解析出所有磁盘分区信息。那怎么办呢。神农 尝百草,一个一个的实验。我们有个函数数组 check_part,每种函数解析一种磁盘分区信息。那个函数能成功的解析磁盘分区信息,就不需要继续尝试了,可以打完收工了。当然解析出来的数据 存放在struct parsed_partitions 类型的结构体state中。
那么神农尝百草,那么一共有多少种草呢?
看下函数数组的定义:
- static int (*check_part[])(struct parsed_partitions *, struct block_device *) = {
-
/*
-
* Probe partition formats with tables at disk address 0
-
* that also have an ADFS boot block at 0xdc0.
-
*/
-
#ifdef CONFIG_ACORN_PARTITION_ICS
-
adfspart_check_ICS,
-
#endif
-
#ifdef CONFIG_ACORN_PARTITION_POWERTEC
-
adfspart_check_POWERTEC,
-
#endif
-
#ifdef CONFIG_ACORN_PARTITION_EESOX
-
adfspart_check_EESOX,
-
#endif
-
-
/*
-
* Now move on to formats that only have partition info at
-
* disk address 0xdc0. Since these may also have stale
-
* PC/BIOS partition tables, they need to come before
-
* the msdos entry.
-
*/
-
#ifdef CONFIG_ACORN_PARTITION_CUMANA
-
adfspart_check_CUMANA,
-
#endif
-
#ifdef CONFIG_ACORN_PARTITION_ADFS
-
adfspart_check_ADFS,
-
#endif
-
-
#ifdef CONFIG_EFI_PARTITION
-
efi_partition, /* this must come before msdos */
-
#endif
-
#ifdef CONFIG_SGI_PARTITION
-
sgi_partition,
-
#endif
-
#ifdef CONFIG_LDM_PARTITION
-
ldm_partition, /* this must come before msdos */
-
#endif
-
#ifdef CONFIG_MSDOS_PARTITION
-
msdos_partition,
-
#endif
-
#ifdef CONFIG_OSF_PARTITION
-
osf_partition,
-
#endif
-
#ifdef CONFIG_SUN_PARTITION
-
sun_partition,
-
#endif
-
#ifdef CONFIG_AMIGA_PARTITION
-
amiga_partition,
-
#endif
-
#ifdef CONFIG_ATARI_PARTITION
-
atari_partition,
-
#endif
-
#ifdef CONFIG_MAC_PARTITION
-
mac_partition,
-
#endif
-
#ifdef CONFIG_ULTRIX_PARTITION
-
ultrix_partition,
-
#endif
-
#ifdef CONFIG_IBM_PARTITION
-
ibm_partition,
-
#endif
-
#ifdef CONFIG_KARMA_PARTITION
-
karma_partition,
-
#endif
-
#ifdef CONFIG_SYSV68_PARTITION
-
sysv68_partition,
-
#endif
-
NULL
-
};
这么多函数,来自五湖四海,就是为了共同的目的,封装到了一起,就是为了获取分区的信息。把获取到信息存入结构体struct parsed_partitions *state;包含 分区的起始扇区,分区扇区的个数,flag等信息。
定义如下:
- struct parsed_partitions {
-
char name[BDEVNAME_SIZE];
-
struct {
-
sector_t from;
-
sector_t size;
-
int flags;
-
} parts[MAX_PART];
-
int next;
-
int limit;
-
};
参考文献:
1 Linux 那些事儿
2 Linux Kernel Source Code
阅读(8437) | 评论(0) | 转发(3) |