`
dato0123
  • 浏览: 914566 次
文章分类
社区版块
存档分类
最新评论

Linux那些事儿之我是SCSI硬盘(2)依然probe

 
阅读更多

虽然scsi disk不难,但是如果你以为scsi disk这个模块每个函数都像init_sd()一样简单,那么我只能说你属于那种被蜘蛛咬了就以为自己是蜘蛛侠,被雷电劈了就以为自己是闪电侠,摸了一次高压电就以为自己是沈殿霞.你不服?咱们来看sd_probe,这个函数就不是那么简单.

1566 /**

1567 * sd_probe - called during driver initialization and whenever a

1568 * new scsi device is attached to the system. It is called once

1569 * for each scsi device (not just disks) present.

1570 * @dev: pointer to device object

1571 *

1572 * Returns 0 if successful (or not interested in this scsi device

1573 * (e.g. scanner)); 1 when there is an error.

1574 *

1575 * Note: this function is invoked from the scsi mid-level.

1576 * This function sets up the mapping between a given

1577 * <host,channel,id,lun> (found in sdp) and new device name

1578 * (e.g. /dev/sda). More precisely it is the block device major

1579 * and minor number that is chosen here.

1580 *

1581 * Assume sd_attach is not re-entrant (for time being)

1582 * Also think about sd_attach() and sd_remove() running coincidentally.

1583 **/

1584 static int sd_probe(struct device *dev)

1585 {

1586 struct scsi_device *sdp = to_scsi_device(dev);

1587 struct scsi_disk *sdkp;

1588 struct gendisk *gd;

1589 u32 index;

1590 int error;

1591

1592 error = -ENODEV;

1593 if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD && sdp->type != TYPE_RBC)

1594 goto out;

1595

1596 SCSI_LOG_HLQUEUE(3, sdev_printk(KERN_INFO, sdp,

1597 "sd_attach/n"));

1598

1599 error = -ENOMEM;

1600 sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL);

1601 if (!sdkp)

1602 goto out;

1603

1604 gd = alloc_disk(16);

1605 if (!gd)

1606 goto out_free;

1607

1608 if (!idr_pre_get(&sd_index_idr, GFP_KERNEL))

1609 goto out_put;

1610

1611 spin_lock(&sd_index_lock);

1612 error = idr_get_new(&sd_index_idr, NULL, &index);

1613 spin_unlock(&sd_index_lock);

1614

1615 if (index >= SD_MAX_DISKS)

1616 error = -EBUSY;

1617 if (error)

1618 goto out_put;

1619

1620 sdkp->device = sdp;

1621 sdkp->driver = &sd_template;

1622 sdkp->disk = gd;

1623 sdkp->index = index;

1624 sdkp->openers = 0;

1625

1626 if (!sdp->timeout) {

1627 if (sdp->type != TYPE_MOD)

1628 sdp->timeout = SD_TIMEOUT;

1629 else

1630 sdp->timeout = SD_MOD_TIMEOUT;

1631 }

1632

1633 class_device_initialize(&sdkp->cdev);

1634 sdkp->cdev.dev = &sdp->sdev_gendev;

1635 sdkp->cdev.class = &sd_disk_class;

1636 strncpy(sdkp->cdev.class_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE);

1637

1638 if (class_device_add(&sdkp->cdev))

1639 goto out_put;

1640

1641 get_device(&sdp->sdev_gendev);

1642

1643 gd->major = sd_major((index & 0xf0) >> 4);

1644 gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);

1645 gd->minors = 16;

1646 gd->fops = &sd_fops;

1647

1648 if (index < 26) {

1649 sprintf(gd->disk_name, "sd%c", 'a' + index % 26);

1650 } else if (index < (26 + 1) * 26) {

1651 sprintf(gd->disk_name, "sd%c%c",

1652 'a' + index / 26 - 1,'a' + index % 26);

1653 } else {

1654 const unsigned int m1 = (index / 26 - 1) / 26 - 1;

1655 const unsigned int m2 = (index / 26 - 1) % 26;

1656 const unsigned int m3 = index % 26;

1657 sprintf(gd->disk_name, "sd%c%c%c",

1658 'a' + m1, 'a' + m2, 'a' + m3);

1659 }

1660

1661 gd->private_data = &sdkp->driver;

1662 gd->queue = sdkp->device->request_queue;

1663

1664 sd_revalidate_disk(gd);

1665

1666 gd->driverfs_dev = &sdp->sdev_gendev;

1667 gd->flags = GENHD_FL_DRIVERFS;

1668 if (sdp->removable)

1669 gd->flags |= GENHD_FL_REMOVABLE;

1670

1671 dev_set_drvdata(dev, sdkp);

1672 add_disk(gd);

1673

1674 sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk/n",

1675 sdp->removable ? "removable " : "");

1676

1677 return 0;

1678

1679 out_put:

1680 put_disk(gd);

1681 out_free:

1682 kfree(sdkp);

1683 out:

1684 return error;

1685 }

如果我们不看新闻联播,我们又怎么知道自己生活在幸福中呢?如果我们不看probe,我们又怎么知道设备驱动的故事是如何展开的呢?

首先,我们为scsi device准备一个指针,struct scsi_device *sdp,scsi disk准备一个指针,struct scsi_disk *sdkp,此外,甭管是scsi硬盘还是ide硬盘,都少不了一个结构体struct gendisk,这里咱们准备了一个指针struct gendisk *gd.

一路走来的兄弟们一定知道,sd_probe将会由scsi核心层调用,或者也叫scsi mid-level来调用.scsi mid-level在调用sd_probe之前,已经为这个scsi设备准备好了struct device,struct scsi_device,已经为它们做好了初始化,所以这里struct device *dev作为参数传递进来咱们就可以直接引用它的成员了.

这不,1593,就开始判断sdp->type,这是struct scsi_device结构体中的成员char type,它用来表征这个scsi设备是哪种类型的,scsi设备五花八门,而只有这里列出来的这三种是sd_mod所支持的.这其中我们最熟悉的当属TYPE_DISK,它就是普通的scsi磁盘,TYPE_MOD表示的是磁光盘(Magneto-Optical disk),一种采用激光和磁场共同作用的磁光方式存储技术实现的介质,外观和3.5英寸软盘相似,量你也不知道,所以不多说了.另外,TYPE_RBC也算在咱们名下,RBC表示Reduced Block Commands,中文叫命令集,这个也不必多说.

1600,sdkp申请内存.struct scsi_disk定义于include/scsi/sd.h:

34 struct scsi_disk {

35 struct scsi_driver *driver; /* always &sd_template */

36 struct scsi_device *device;

37 struct class_device cdev;

38 struct gendisk *disk;

39 unsigned int openers; /* protected by BKL for now, yuck */

40 sector_t capacity; /* size in 512-byte sectors */

41 u32 index;

42 u8 media_present;

43 u8 write_prot;

44 unsigned WCE : 1; /* state of disk WCE bit */

45 unsigned RCD : 1; /* state of disk RCD bit, unused */

46 unsigned DPOFUA : 1; /* state of disk DPOFUA bit */

47 };

看起来,似乎描述一个scsi disk很简单,其实你不要忘了,前面我们还提到另一个结构体struct gendisk,这个结构体来自一个神秘的地方,include/linux/genhd.h:

113 struct gendisk {

114 int major; /* major number of driver */

115 int first_minor;

116 int minors; /* maximum number of minors, =1 for

117 * disks that can't be partitioned. */

118 char disk_name[32]; /* name of major driver */

119 struct hd_struct **part; /* [indexed by minor] */

120 int part_uevent_suppress;

121 struct block_device_operations *fops;

122 struct request_queue *queue;

123 void *private_data;

124 sector_t capacity;

125

126 int flags;

127 struct device *driverfs_dev;

128 struct kobject kobj;

129 struct kobject *holder_dir;

130 struct kobject *slave_dir;

131

132 struct timer_rand_state *random;

133 int policy;

134

135 atomic_t sync_io; /* RAID */

136 unsigned long stamp;

137 int in_flight;

138 #ifdef CONFIG_SMP

139 struct disk_stats *dkstats;

140 #else

141 struct disk_stats dkstats;

142 #endif

143 struct work_struct async_notify;

144 };

于是,struct scsi_diskstruct gendisk联手来为我们描述一块磁盘,scsi_diskscsi专用,gendisk中的gen表示general,过了英语四级的都知道,这表示通用,scsi,ide,大家伙都能利用的.

于是1604,alloc_disk就是为我们分配一个gendisk.

但是接下来1608行的sd_index_idr就有些学问了.

下面我们必须用专门一段文字来描述idr.首先在89行我们看到下面这么一句,

89 static DEFINE_IDR(sd_index_idr);

这被叫做定义一个IDR.印象中大四上刚开学的时候,江湖中开始流传一篇文章叫做”idr”- integer ID management,专门对idr进行了一些介绍,这篇文章最早是发表在LWN(Linux Weekly News)上面.怎奈少不更事的我一直沉迷于上网,聊天,灌水,玩游戏,所以直到今天,依然不知道为什么这玩意儿叫做idr,只是懵懵懂懂的感觉它是一个用来管理一些小整数的工具,具体来说,就是内核中定义了一些函数,几乎所有的函数都被定义在一个文件中,lib/idr.c,关于它的实现咱们自然不必多说,说多了就未免喧宾夺主了,我们只看它的实际效果.

实际上我们一共调用了三个来自lib/idr.c的函数,或者更确切的说是四个,因为上面这个宏DEFINE_IDR也是一个函数的包装,总的来说,如果我们需要使用idr工具,我们就需要首先调用idr_init函数,或者使用它的马夹DEFINE_IDR,这算是初始化,也叫做创建一个idr对象,其实就是申请一个struct idr结构体变量.然后使用两个函数,一个是idr_pre_get(),一个是idr_get_new(),当我们日后觉得这个idr已经没有利用价值了,我们则可以调用另一个函数,idr_remove()来完成过河拆桥的工作.

我们看到1608行调用idr_pre_get(),其第一个参数就是我们之前初始化的&sd_index_idr,第二个参数是一个掩码,和我们以往每一次申请内存时一样,通常传递的就是GFP_KERNEL.这个函数有点与众不同的是,它返回0表示出错,返回非0才表示正常,典型的抽疯式函数.

1612,idr_get_new(),就是获得下一个availableID,保存在第三个参数中,即我们这里的index,第二个参数不是太常用,传递个NULL就可以了.一切正常将返回0.

index必须小于SD_MAX_DISKS,这个宏定义于include/scsi/sd.h:

12 * This is limited by the naming scheme enforced in sd_probe,

13 * add another character to it if you really need more disks.

14 */

15 #define SD_MAX_DISKS (((26 * 26) + 26 + 1) * 26)

比这个宏还大就肯定出错了.关于这个宏,曾几何时,我也和你一样,丈二和尚摸不着头脑,我也曾彷徨,也曾犹豫,也曾困惑,后来有一天我终于明白了,26代表的是英文字母的个数,而下面我们马上就能看到,Linux中对scsi disk的命名规则正是利用了26个英文字母.

不信你就看16431659,这一段同时也正是idr的精华.最终你会发现,gd->disk_name一定是在sda-sdz之间,或者是在sdaasdzz之间,或者是在sdaaasdzzz之间.算一下,是不是正好数量为SD_MAX_DISKS.index的取值范围则是[0,SD_MAX_DISKS)之间,只取整数.举例来说,如果你只有一块硬盘,那么你能看到的是,/dev/sda,如果你有多块硬盘,比如像我下面这个例子中的一样,

localhost:~ # fdisk -l

Disk /dev/sda: 146.1 GB, 146163105792 bytes

255 heads, 63 sectors/track, 17769 cylinders

Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System

/dev/sda1 1 266 2136613+ 83 Linux

/dev/sda2 2879 17769 119611957+ 83 Linux

/dev/sda3 * 267 1572 10490445 83 Linux

/dev/sda4 1573 2878 10490445 82 Linux swap / Solaris

Partition table entries are not in disk order

Disk /dev/sdb: 5368 MB, 5368709120 bytes

166 heads, 62 sectors/track, 1018 cylinders

Units = cylinders of 10292 * 512 = 5269504 bytes

Device Boot Start End Blocks Id System

Disk /dev/sdc: 5368 MB, 5368709120 bytes

166 heads, 62 sectors/track, 1018 cylinders

Units = cylinders of 10292 * 512 = 5269504 bytes

Device Boot Start End Blocks Id System

Disk /dev/sdd: 5368 MB, 5368709120 bytes

166 heads, 62 sectors/track, 1018 cylinders

Units = cylinders of 10292 * 512 = 5269504 bytes

Device Boot Start End Blocks Id System

Disk /dev/sde: 5368 MB, 5368709120 bytes

166 heads, 62 sectors/track, 1018 cylinders

Units = cylinders of 10292 * 512 = 5269504 bytes

Device Boot Start End Blocks Id System

这个例子中我的机器里是5scsi硬盘,那么它们的名字分别是sda,sdb,sdc,sdd,sde,但是如果你他妈的比较变态,一台机器里接了30块硬盘,那么没啥说的,它们就会依次被命名为sda,sdb,…,sdx,sdy,sdz,这还不够,只有26,接下来的硬盘名称就叫做sdaa,sdab,sdac,sdad,总共凑满30.但如果你觉得这还不够变态,你非要挑战极限,你非要play zhuangbility,那么在用完了sdaasdzz之后,Linux还允许你用sdaaa,sdaab,…,一直到sdzzz.总之,Linux,你最多可以使用的硬盘数撑死就是SD_MAX_DISKS.当然,我还是奉劝你,别这么干,毕竟孔子曾经曰过:”莫装B,B遭雷劈!”

算了我们言归正传,1643,gd->major被赋了值,自从在张江软件园某不知名的小公司里笔试过那道不用临时变量交换两个变量的值之后,我曾深刻的反省自己为何当初没有好好学习谭浩强老师那本<<C程序设计>>中的位运算.痛定思痛之后,我终于能看懂眼前这代码了,当然首先我们得明白这个sd_major为何物?

247 /*

248 * Device no to disk mapping:

249 *

250 * major disc2 disc p1

251 * |............|.............|....|....| <- dev_t

252 * 31 20 19 8 7 4 3 0

253 *

254 * Inside a major, we have 16k disks, however mapped non-

255 * contiguously. The first 16 disks are for major0, the next

256 * ones with major1, ... Disk 256 is for major0 again, disk 272

257 * for major1, ...

258 * As we stay compatible with our numbering scheme, we can reuse

259 * the well-know SCSI majors 8, 65--71, 136--143.

260 */

261 static int sd_major(int major_idx)

262 {

263 switch (major_idx) {

264 case 0:

265 return SCSI_DISK0_MAJOR;

266 case 1 ... 7:

267 return SCSI_DISK1_MAJOR + major_idx - 1;

268 case 8 ... 15:

269 return SCSI_DISK8_MAJOR + major_idx - 8;

270 default:

271 BUG();

272 return 0; /* shut up gcc */

273 }

274 }

看起来挺复杂,其实不然,我们前面说过,scsi disk的主设备号是已经固定好了的,它就是瓜分了8,65-71,128-135这几个号,这里SCSI_DISK0_MAJOR就是8,SCSI_DISK1_MAJOR就是65,SCSI_DISK8_MAJOR就是128.sd_major()接受的参数就是indexbit4bit7,而它取值范围自然就是015,这也正是sd_major()switch/case语句判断的范围,即实际上major_idx就是主设备号的一个索引,就说是在这个16个主设备号中它算老几.first_minor就是对应于本index的第一个次设备号,我们可以用代入法得到,index0,first_minor0,index1,first_minor16,index2,first_minor32.另一方面,minor本身表示本index下有多少个次设备号,这个大家都是一样的,都是16.我们通过下面这个例子也能看到:

[root@localhost ~]# cat /proc/partitions

major minor #blocks name

8 0 285474816 sda

8 1 2104483 sda1

8 2 16779892 sda2

8 3 1 sda3

8 5 20972826 sda5

8 6 20482843 sda6

8 7 20482843 sda7

8 8 10241406 sda8

8 9 20482843 sda9

8 10 20482843 sda10

8 11 20482843 sda11

8 12 20482843 sda12

8 13 20482843 sda13

8 14 20482843 sda14

8 15 20482843 sda15

8 16 5242880 sdb

8 32 5242880 sdc

8 48 5242880 sdd

8 64 5242880 sde

很显然,对于sda,其次设备是从0开始,对于sdb,次设备号从16开始,对于sdc,则从32开始,sdd则从48开始,每个index或者说每个disk_name下面有16个次设备号.也因此一块SCSI硬盘就是最多15个分区.

除此之外,sd_probe中主要就是些简单的赋值了.当然也不全是,比如1633行到1639行这一段,它的效果就是让这个设备出现在了sysfsclass子目录下面,比如下面这个例子:

localhost:~ # cat /sys/class/scsi_device/

0:0:8:0/ 0:2:0:0/ 1:0:0:0/ 1:0:0:1/ 1:0:0:2/ 1:0:0:3/

每一个scsi设备都在这个占了一个子目录.

1641行这个get_device不用多说,访问一个struct device的第一步,增加引用计数.以后不用的时候自然会有一个相对的函数put_device被调用.

1671,dev_set_drvdata,就是设置dev->driver_data等于sdkp,即让struct device的指针devstruct scsi_disk的指针sdkp给联系起来.这就是所谓的朋友多了路好走,关系网建立得越大,日后使用起来就越方便.

最后,特别提醒两个赋值.第一个是,1621,sdkp->driver等于&sd_template,另一个是1646,gd->fops等于&sd_fops.这两行赋值对咱们整个故事的意义,不亚于1979年那个春天有一位老人在中国的南海边划了一个圈对中国的重大意义.在咱们讲完sd_probe之后,这两个赋值将引领我们展开下面的故事.

关于sd_probe,眼瞅着就要完了,但是很显然,有两个函数我们还没有提到,它们就是1664行的sd_revalidate_disk()以及1672行的add_disk(),这两个函数是如此的重要,以至于我们有必要在下一节专门来讲述它们.

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics