Linux内核scsi子系统初始化架构

更新时间:2023-12-06 08:28:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

代码布局:

一、读写数据流: drivers/scsi/scsi_lib.c

scsi_execute_req===>scsi_execute===>blk_execute_rq===>blk_execute_rq_nowait

错误恢复:

drivers/scsi/scsi_error.c

内核线程scsi_error_handler===>

二、初始化:(本文!)

-------------------------------------------------------------------------------------------------------------------- scsi高层(scsi磁盘)驱动: 1代码分布

2核心调用:

drivers/scsi/sd.c(这里以scsi disk设备为例) 同步执行部分:sd_probe

异步执行部分:sd_probe_async===>sd_revalidate_disk===>sd_spinup_disk

-------------------------------------------------------------------------------------------------------------------- scsi子系统核心初始化: 1代码分布

2核心调用:

scsi_init_queue(); scsi_init_procfs(); scsi_init_devinfo(); scsi_init_hosts(); scsi_init_sysctl(); scsi_sysfs_register(); scsi_netlink_init();

-------------------------------------------------------------------------------------------------------------------- scsi适配器驱动: 1代码分布

2核心调用:

drivers/scsi/hosts.c 1)、struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) 2)、scsi_add_host(struct Scsi_Host *host,struct device *dev) ===>scsi_add_host_with_dma(host, dev, dev)

int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, struct device *dma_dev)

3)、scsi_scan_host===>do_scsi_scan_host===>scsi_scan_host_selected===>scsi_scan_channel ===>__scsi_scan_target===>scsi_probe_and_add_lun===>scsi_probe_lun

scsi_add_lun

1

代码骨架:

---------------------------------------------------------------------------------------------------------------------- scsi子系统核心初始化: drivers/scsi/scsi.c:

static struct class shost_class = { .name = \ .dev_release = scsi_host_cls_release, };

static struct class sdev_class = { .name = \ .dev_release = scsi_device_cls_release, };

struct bus_type scsi_bus_type = { .name = \ .match = scsi_bus_match, .uevent = scsi_bus_uevent, };

subsys_initcall(init_scsi)===> static int __init init_scsi(void) { scsi_init_queue(); scsi_init_procfs(); scsi_init_devinfo();// scsi_init_hosts();// scsi_init_sysctl(); scsi_sysfs_register();// scsi_netlink_init(); }

int scsi_init_hosts(void) { return class_register(&shost_class); }

int scsi_sysfs_register(void) {

int error; error = bus_register(&scsi_bus_type); if (!error) { class_register(&sdev_class); } }

---------------------------------------------------------------------------------------------------------------------- scsi HBA驱动:(前端以dmx3191d为例)

static struct scsi_host_template dmx3191d_driver_template = { .name = \ .queuecommand = NCR5380_queue_command,

... ..., };

static struct pci_device_id dmx3191d_pci_tbl[] = { {PCI_VENDOR_ID_DOMEX, PCI_DEVICE_ID_DOMEX_DMX3191D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, { } };

2

static struct pci_driver dmx3191d_pci_driver = { .name = DMX3191D_DRIVER_NAME, .id_table = dmx3191d_pci_tbl, .probe = dmx3191d_probe_one, .remove = __devexit_p(dmx3191d_remove_one), };

module_init(dmx3191d_init);

static int __init dmx3191d_init(void) { return pci_register_driver(&dmx3191d_pci_driver);//调dmx3191d_probe_one }

static int __devinit dmx3191d_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { struct Scsi_Host *shost;

... ...; scsi_host_alloc(&dmx3191d_driver_template,sizeof(struct NCR5380_hostdata));//------ shost->irq = pdev->irq;

request_irq(pdev->irq, NCR5380_intr, IRQF_SHARED,NAME, shost); ... ...; scsi_add_host(shost, &pdev->dev); scsi_scan_host(shost); }

---------------------------------------------------------------------------------------------------------- drivers/scsi/hosts.c

static atomic_t scsi_host_next_hn;

struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) { struct Scsi_Host *shost; gfp_t gfp_mask = GFP_KERNEL; shost = kzalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask); shost->host_no = atomic_inc_return(&scsi_host_next_hn) - 1; device_initialize(&shost->shost_gendev); shost->shost_gendev.bus = &scsi_bus_type; shost->shost_gendev.type = &scsi_host_type;

... ...; device_initialize(&shost->shost_dev); shost->shost_dev.parent = &shost->shost_gendev; shost->shost_dev.class = &shost_class; return shost; }

include/scsi/scsi_host.h

static inline int __must_check scsi_add_host(struct Scsi_Host *host, struct device *dev) { return scsi_add_host_with_dma(host, dev, dev); }

drivers/scsi/hosts.c

int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, struct device *dma_dev) { struct scsi_host_template *sht = shost->hostt;

... ...; device_add(&shost->shost_gendev);//------ device_add(&shost->shost_dev);

3

... ...;

scsi_sysfs_add_host(shost); ... ...;

}

---------------------------------------------------------------------------------------------------------------------- drivers/scsi/scsi_scan.c

#ifdef CONFIG_SCSI_SCAN_ASYNC //0 #define SCSI_SCAN_TYPE_DEFAULT \#else

#define SCSI_SCAN_TYPE_DEFAULT \#endif

static char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT;

//变量的值可以在加载scsi中间层模块时通过模块参数设定,未设定则用上面的默认值 module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO); # define jiffies raid6_jiffies()

//specify scanning or rescanning of all possible channels, (target) ids, or luns ,on a given shost. #define SCAN_WILD_CARD ~0 #define MAX_COMMAND_SIZE 16

void scsi_scan_host(struct Scsi_Host *shost) { struct task_struct *p; struct async_scan_data *data; if (strncmp(scsi_scan_type, \ return; data = scsi_prep_async_scan(shost);//异步扫描准备与判断 shost->async_scan = 1 if (!data) {//如果data=null, shost->async_scan仍为0 do_scsi_scan_host(shost); return;//同步扫描逻辑,不需要任何准备工作 } p = kthread_run(do_scan_async, data, \异步扫描逻辑 if (IS_ERR(p))//有错误时 do_scan_async(data);//shost->async_scan = 0 }

static void do_scsi_scan_host(struct Scsi_Host *shost) { if (shost->hostt->scan_finished) {

... ...;//自定义的扫描逻辑

} else {

//通配符SCAN_WILD_CARD ~0 , 表示需要尝试所有可能的值

//rescan=0,表示第一次扫描,为1,表示重新扫描,即这个接口还有其他地方调用!

scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD, SCAN_WILD_CARD, 0); } }

int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, unsigned int id, unsigned int lun, int rescan) {

... ...;

//shost->async_scan:是异步扫描标志,为1--异步扫描,0--同步扫描 if (!shost->async_scan) scsi_complete_async_scans();

if (scsi_host_scan_allowed(shost)) {

4

} }

static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, unsigned int id, unsigned int lun, int rescan) { uint order_id; if (id == SCAN_WILD_CARD) for (id = 0; id < shost->max_id; ++id) { if (shost->reverse_ordering) order_id = shost->max_id - id - 1; else order_id = id; __scsi_scan_target(&shost->shost_gendev, channel, order_id, lun, rescan); } else __scsi_scan_target(&shost->shost_gendev, channel, id, lun, rescan);//---- }

static void __scsi_scan_target(struct device *parent, unsigned int channel, unsigned int id, unsigned int lun, int rescan) { struct Scsi_Host *shost = dev_to_shost(parent); int bflags = 0; int res; struct scsi_target *starget;

if (shost->this_id == id) return; starget = scsi_alloc_target(parent, channel, id); if (lun != SCAN_WILD_CARD) { scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, NULL);//--------- goto out_reap; } res = scsi_probe_and_add_lun(starget, 0, &bflags, NULL, rescan, NULL);//-------- if (res == SCSI_SCAN_LUN_PRESENT || res == SCSI_SCAN_TARGET_PRESENT) {

//会调用scsi_probe_and_add_lun

if (scsi_report_lun_scan(starget, bflags, rescan) != 0)

//会调用scsi_probe_and_add_lun

scsi_sequential_lun_scan(starget, bflags, starget->scsi_level, rescan); } out_reap: scsi_target_reap(starget); put_device(&starget->dev); }

5

if (channel == SCAN_WILD_CARD) for (channel = 0; channel <= shost->max_channel; channel++) scsi_scan_channel(shost, channel, id, lun, rescan); else scsi_scan_channel(shost, channel, id, lun, rescan);

static int scsi_probe_and_add_lun(struct scsi_target *starget, uint lun, int *bflagsp, struct scsi_device **sdevp, int rescan, void *hostdata) { struct scsi_device *sdev; unsigned char *result; int bflags, result_len = 256; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); sdev = scsi_device_lookup_by_target(starget, lun); if (sdev) { if (rescan || !scsi_device_created(sdev)) { if (sdevp) *sdevp = sdev; else scsi_device_put(sdev); if (bflagsp) *bflagsp = scsi_get_device_flags(sdev, sdev->vendor, sdev->model); return SCSI_SCAN_LUN_PRESENT; }

... ...;

} else sdev = scsi_alloc_sdev(starget, lun, hostdata);//----------- result = kmalloc(result_len, GFP_ATOMIC | ((shost->unchecked_isa_dma) ? __GFP_DMA : 0)); scsi_probe_lun(sdev, result, result_len, &bflags);//---------- if (bflagsp) *bflagsp = bflags;

... ...; res = scsi_add_lun(sdev, result, &bflags, shost->async_scan);//---------

return res; }

static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, unsigned int lun, void *hostdata) { struct scsi_device *sdev; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);

... ...; sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size, GFP_ATOMIC);

... ...;

6

}

void scsi_sysfs_device_initialize(struct scsi_device *sdev) { struct Scsi_Host *shost = sdev->host; struct scsi_target *starget = sdev->sdev_target; device_initialize(&sdev->sdev_gendev);// sdev->sdev_gendev.bus = &scsi_bus_type; sdev->sdev_gendev.type = &scsi_dev_type;

... ...; device_initialize(&sdev->sdev_dev);//

... ...; sdev->scsi_level = starget->scsi_level;

list_add_tail(&sdev->same_target_siblings, &starget->devices); list_add_tail(&sdev->siblings, &shost->__devices); }

static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, int result_len, int *bflags) { unsigned char scsi_cmd[MAX_COMMAND_SIZE]; int first_inquiry_len, try_inquiry_len, next_inquiry_len; int response_len = 0; int pass, count, result; struct scsi_sense_hdr sshdr; *bflags = 0; first_inquiry_len = sdev->inquiry_len ? sdev->inquiry_len : 36; try_inquiry_len = first_inquiry_len; pass = 1;

next_pass: for (count = 0; count < 3; ++count) { int resid; memset(scsi_cmd, 0, 6); scsi_cmd[0] = INQUIRY;//--- scsi_cmd[4] = (unsigned char) try_inquiry_len;

... ...;

result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, inq_result,

try_inquiry_len, &sshdr, HZ / 2 + HZ * scsi_inq_timeout, 3, &resid);//-----

if (result) { if ((driver_byte(result) & DRIVER_SENSE) && scsi_sense_valid(&sshdr)) { if ((sshdr.sense_key == UNIT_ATTENTION) && ((sshdr.asc == 0x28) ||(sshdr.asc == 0x29)) && (sshdr.ascq == 0)) continue; } } else { if (resid == try_inquiry_len) continue; } break;//其他错误 }

7

sdev->request_queue = scsi_alloc_queue(sdev); ... ...;

scsi_sysfs_device_initialize(sdev);//--------- ... ...;

return sdev;

if (result == 0) { response_len = inq_result[4] + 5; if (response_len > 255) response_len = first_inquiry_len; *bflags = scsi_get_device_flags(sdev, &inq_result[8], &inq_result[16]); if (pass == 1) { if (BLIST_INQUIRY_36 & *bflags) next_inquiry_len = 36; else if (BLIST_INQUIRY_58 & *bflags) next_inquiry_len = 58; else if (sdev->inquiry_len) next_inquiry_len = sdev->inquiry_len; else next_inquiry_len = response_len; if (next_inquiry_len > try_inquiry_len) { try_inquiry_len = next_inquiry_len; pass = 2; goto next_pass;// } } } else if (pass == 2) { try_inquiry_len = first_inquiry_len; pass = 3; goto next_pass; }

if (result) return -EIO; ... ...; return 0;

}

static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, int *bflags, int async) { sdev->inquiry= kmemdup(inq_result,max_t(size_t, sdev->inquiry_len, 36),GFP_ATOMIC); if (sdev->inquiry == NULL) return SCSI_SCAN_NO_RESPONSE;

... ...;

if (*bflags & BLIST_NO_ULD_ATTACH) sdev->no_uld_attach = 1; ... ...; if (!async && scsi_sysfs_add_sdev(sdev) != 0) return SCSI_SCAN_NO_RESPONSE; return SCSI_SCAN_LUN_PRESENT; }

int scsi_sysfs_add_sdev(struct scsi_device *sdev) { struct request_queue *rq = sdev->request_queue; struct scsi_target *starget = sdev->sdev_target; scsi_device_set_state(sdev, SDEV_RUNNING); scsi_target_add(starget);

device_add(&sdev->sdev_gendev);//--------将lun挂入到总线的设备链表 device_add(&sdev->sdev_dev);//-------- transport_add_device(&sdev->sdev_gendev);

8

bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL); }

====================================================================== scsi磁盘驱动 drivers/scsi/sd.c

#define SD_MINORS 16

static DEFINE_IDA(sd_index_ida);// 定义ida{} sd_index_ida 变量

static const struct block_device_operations sd_fops = { .owner = THIS_MODULE, .open = sd_open, .locked_ioctl = sd_ioctl, .getgeo = sd_getgeo, .media_changed = sd_media_changed, .revalidate_disk = sd_revalidate_disk,

... ...; };

static struct class sd_disk_class = { .name = \ .owner = THIS_MODULE, .dev_release = scsi_disk_release, .dev_attrs = sd_disk_attrs, };

static struct scsi_driver sd_template = { .owner = THIS_MODULE, .gendrv = { .name = \ .probe = sd_probe, }, .rescan = sd_rescan, .done = sd_done, };

module_init(init_sd)===> static int __init init_sd(void) { int majors = 0, i; for (i = 0; i < SD_MAJORS; i++) if (register_blkdev(sd_major(i), \ majors++; class_register(&sd_disk_class); scsi_register_driver(&sd_template.gendrv);

... ...; }

static int sd_major(int major_idx) { switch (major_idx) { case 0: return SCSI_DISK0_MAJOR; case 1 ... 7:

9

return SCSI_DISK1_MAJOR + major_idx - 1; case 8 ... 15: return SCSI_DISK8_MAJOR + major_idx - 8; default: return 0; } }

int scsi_register_driver(struct device_driver *drv) { drv->bus = &scsi_bus_type; return driver_register(drv); }

driver_register()-->bus_add_driver()-->driver_attach()-->bus_for_each_dev()-->__driver_attach()--> driver_match_device()====>scsi_bus_match()

driver_probe_device()====>really_probe()====>sd_probe()

static int scsi_bus_match(struct device *dev, struct device_driver *gendrv) { struct scsi_device *sdp; if (dev->type != &scsi_dev_type) return 0; sdp = to_scsi_device(dev); if (sdp->no_uld_attach) return 0; return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0; }

//同步部分

static int sd_probe(struct device *dev) { struct scsi_device *sdp = to_scsi_device(dev); struct scsi_disk *sdkp; struct gendisk *gd; u32 index; int error; error = -ENODEV; if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD && sdp->type != TYPE_RBC) goto out; error = -ENOMEM; sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL); gd = alloc_disk(SD_MINORS);// do { if (!ida_pre_get(&sd_index_ida, GFP_KERNEL)) goto out_put;

... ...;

error = ida_get_new(&sd_index_ida, &index); } while (error == -EAGAIN); sd_format_disk_name(\index, gd->disk_name, DISK_NAME_LEN);

... ...;

10

}

//异步部分:

static void sd_probe_async(void *data, async_cookie_t cookie) { struct scsi_disk *sdkp = data; struct scsi_device *sdp; struct gendisk *gd; u32 index;

... ...; index = sdkp->index; if (index < SD_MAX_DISKS) { gd->major = sd_major((index & 0xf0) >> 4); gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); gd->minors = SD_MINORS; }

... ...; gd->fops = &sd_fops; gd->queue = sdkp->device->request_queue;

... ...;

sdkp->media_present = 1; sdkp->first_scan = 1; sd_revalidate_disk(gd); blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);

... ...; add_disk(gd);

... ...; sd_revalidate_disk(gd); }

static int sd_revalidate_disk(struct gendisk *disk) { struct scsi_disk *sdkp = scsi_disk(disk); struct scsi_device *sdp = sdkp->device;

... ...; if (!scsi_device_online(sdp)) goto out;

... ...; sd_spinup_disk(sdkp); if (sdkp->media_present) {

... ...; } sdkp->first_scan = 0;

out: return 0; }

static void sd_spinup_disk(struct scsi_disk *sdkp)

11

sdkp->index = index; ... ...;

device_initialize(&sdkp->dev); ... ...;

device_add(&sdkp->dev); ... ...;

async_schedule(sd_probe_async, sdkp);//见异步部分

{

unsigned char cmd[10];

unsigned long spintime_expire = 0; int retries, spintime; unsigned int the_result; struct scsi_sense_hdr sshdr; int sense_valid = 0; spintime = 0; do {

retries = 0; do { cmd[0] = TEST_UNIT_READY; memset((void *) &cmd[1], 0, 9); the_result = scsi_execute_req(sdkp->device, cmd,DMA_NONE, NULL, 0,&sshdr, SD_TIMEOUT, SD_MAX_RETRIES, NULL);

if (media_not_present(sdkp, &sshdr)) return; if (the_result) sense_valid = scsi_sense_valid(&sshdr); retries++;

} while (retries < 3 && (!scsi_status_is_good(the_result) || ((driver_byte(the_result) & DRIVER_SENSE) && sense_valid && sshdr.sense_key == UNIT_ATTENTION))); ... ...;

if (sense_valid && sshdr.sense_key == NOT_READY) {

... ...;

if (!spintime) { cmd[0] = START_STOP; cmd[1] = 1; memset((void *) &cmd[2], 0, 8); cmd[4] = 1; if (sdkp->device->start_stop_pwr_cond) cmd[4] |= 1 << 4; scsi_execute_req(sdkp->device, cmd, DMA_NONE,NULL, 0, &sshdr,SD_TIMEOUT, SD_MAX_RETRIES,NULL); spintime_expire = jiffies + 100 * HZ; spintime = 1; } msleep(1000); } else if (sense_valid && sshdr.sense_key == UNIT_ATTENTION && sshdr.asc == 0x28) { if (!spintime) { spintime_expire = jiffies + 5 * HZ; spintime = 1; } msleep(1000); } else {

... ...;

break; }

12

}

} while (spintime && time_before_eq(jiffies, spintime_expire)); if (spintime) { if (scsi_status_is_good(the_result)) printk(\ else printk(\}

13

本文来源:https://www.bwwdw.com/article/06rt.html

Top