The SCSI low-level driver is for the host Adapter. When the low-level driver is loaded, you must first add the host adapter. Host adapters can be added when the PCI subsystem completes ID matching, or manually added. All host adapters Based on the hardware PCI interface adopt the previous method, while the unh iSCSI initiator adopts the latter method.
Adding a host adapter includes two parts: assigning a data structure to the host adapter and adding the host adapter to the system.
The SCSI middle layer provides two common functions:Scsi_host_alloc and scsi_add_host.
/**
* Scsi_host_alloc-register a SCSI host adapter instance.
* @ Sht: pointer to SCSI host Template
* @ Privsize: extra bytes to allocate for driver
*
* Note:
* Allocate a new scsi_host and perform basic initialization.
* The host is not published to the SCSI midlayer until scsi_add_host
* Is called.
*
* Return value:
* Pointer to a new scsi_host
**/
Struct scsi_host *Scsi_host_alloc (Struct scsi_host_template * sht, int privsize)
{
Struct scsi_host * shost;
Gfp_t gfp_mask = gfp_kernel;
Int rval;
If (SHT-> unchecked_isa_dma & privsize)
Gfp_mask | = __Gfp_dma;// Ram's low 16 m is used as the DMA address space
Shost = kzarloc (sizeof (struct scsi_host) + privsize, gfp_mask );
// One-time space allocation for scsi_host, including public and private parts.
If (! Shost)
Return NULL;
Shost-> host_lock = & shost-> default_lock;
Spin_lock_init (shost-> host_lock );
Shost-> shost_state = shost_created;
Init_list_head (& shost->__ devices );
Init_list_head (& shost->__ targets );
Init_list_head (& shost-> eh_1__q );
Init_list_head (& shost-> starved_list );
Init_waitqueue_head (& shost-> host_wait );
Mutex_init (& shost-> scan_mutex );
/*
* Subtract one because we increment first then return, but we need
* Know what the next host number was before Increment
*/
Shost-> host_no = atomic_inc_return (& scsi_host_next_hn)-1;// Host_no host adapter number. scsi_host_next_hn indicates the number of the next host adapter. Its initial value is 0. Each time a new host adapter is found, the value of the global variable scsi_host_next_hn is assigned to the host_no field, and then increments.
Shost-> dma_channel = 0xff;
/* These three are default values which can be overridden */
Shost-> max_channel = 0;
Shost-> max_id = 8;
Shost-> max_lun = 8;
/* Give each shost a default transportt */
Shost-> transportt = & blank_transport_template;
/*
* All drivers right now shocould be able to handle 12 byte
* Commands. Every so often there are requests for 16 bytes
* Commands, but individual low-level drivers need to certify that
* They actually do something sensible with such commands.
*/
Shost-> max_cmd_len = 12;
Shost-> hostt = SHT;
Shost-> this_id = sht-> this_id;
Shost-> can_queue = sht-> can_queue;
Shost-> sg_tablesize = sht-> sg_tablesize;
Shost-> pai_per_lun = sht-> pai_per_lun;
Shost-> unchecked_isa_dma = sht-> unchecked_isa_dma;
Shost-> use_clustering = sht-> use_clustering;
Shost-> ordered_tag = sht-> ordered_tag;
If (SHT-> supported_mode = mode_unknown)
/* Means we didn't set it... default to initiator */
Shost-> active_mode = mode_initiator;
Else
Shost-> active_mode = sht-> supported_mode;
If (SHT-> max_host_blocked)
Shost-> max_host_blocked = sht-> max_host_blocked;
Else
Shost-> max_host_blocked = scsi_default_host_blocked;
/*
* If the driver imposes no hard sector transfer limit, start
* Machine infinity initially.
*/
If (SHT-> max_sectors)
Shost-> max_sectors = sht-> max_sectors;
Else
Shost-> max_sectors = scsi_default_max_sectors;
/*
* Assume a 4 GB boundary, if not set
*/
If (SHT-> dma_boundary)
Shost-> dma_boundary = sht-> dma_boundary;
Else
Shost-> dma_boundary = 0 xffffffff;
Device_initialize (& shost-> shost_gendev );
// The shost_gendev domain is embedded with a general-purpose device and initialized. It is a secondary device of an embedded class device.
Dev_set_name (& shost-> shost_gendev, "host % d", shost-> host_no );
# Ifndef config_sysfs_deprecated
Shost-> shost_gendev.bus = & scsi_bus_type;
# Endif
Shost-> shost_gendev.type = & scsi_host_type;
Device_initialize (& shost-> shost_dev );// The shost_dev domain is an embedded device,Initialize this type of device,
Shost-> shost_dev.parent = & shost-> shost_gendev;
Shost-> shost_dev.class = & shost_class;
Dev_set_name (& shost-> shost_dev, "host % d", shost-> host_no );
Shost-> shost_dev.groups = scsi_sysfs_shost_attr_groups;
Shost-> ehandler = kthread_run (scsi_error_handler, shost,
// SCSI error recovery
"Scsi_eh _ % d", shost-> host_no );
If (is_err (shost-> ehandler )){
Rval = ptr_err (shost-> ehandler );
Goto fail_kfree;
}
Scsi_proc_hostdir_add (shost-> hostt );// Add a directory for the host adapter in the proc file system
Return shost;
Fail_kfree:
Kfree (shost );
Return NULL;
}
After the scsi_host_alloc function is called, the host adapter is not disclosed to the SCSI middle layer until the scsi_add_host function is called.
Static inline int _ must_checkScsi_add_host(Struct scsi_host * host,
Struct device * Dev)
{
Return scsi_add_host_with_dma (host, Dev, Dev );
}
IntScsi_add_host_with_dma(Struct scsi_host * shost, struct device * Dev,
// The first is the pointer to the host adapter descriptor, and the second is the device descriptor of the parent device in the SCSI host Adapter Driver Model.
Struct device * dma_dev)
{
Struct scsi_host_template * sht = shost-> hostt;
Int error =-einval;
Printk (kern_info "SCSI % d: % s \ n", shost-> host_no,
Sht-> info? Sht-> Info (shost): sht-> name );
If (! Shost-> can_queue ){// Can_queue indicates the number of commands that can be accepted by the host adapter at the same time.
Printk (kern_err "% s: can_queue = 0 no longer supported \ n ",
Sht-> name );
Goto fail;
}
Error = scsi_setup_command_freelist (shost );
If (error)
Goto fail;
If (! Shost-> shost_gendev.parent)
// Determine the host adapter position in sysfs
Shost-> shost_gendev.parent = Dev? Dev: & platform_bus;
Shost-> dma_dev = dma_dev;
Error = device_add (& shost-> shost_gendev );
If (error)
Goto out;
Scsi_host_set_state (shost, shost_running );
Get_device (shost-> shost_gendev.parent );
Error = device_add (& shost-> shost_dev );
If (error)
Goto out_del_gendev;
Get_device (& shost-> shost_gendev );
If (shost-> transportt-> host_size ){
Shost-> shost_data = kzarloc (shost-> transportt-> host_size,
Gfp_kernel );
If (shost-> shost_data = NULL ){
Error =-enomem;
Goto out_del_dev;
}
}
If (shost-> transportt-> create_work_queue ){
Snprintf (shost-> work_q_name, sizeof (shost-> work_q_name ),
"Scsi_wq _ % d", shost-> host_no );
Shost-> work_q = create_singlethread_workqueue (
Shost-> work_q_name );
If (! Shost-> work_q ){
Error =-einval;
Goto out_free_shost_data;
}
}
Error = scsi_sysfs_add_host (shost );
If (error)
Goto out_destroy_host;
Scsi_proc_host_add (shost );
Return Error;
Out_destroy_host:
If (shost-> work_q)
Destroy_workqueue (shost-> work_q );
Out_free_shost_data:
Kfree (shost-> shost_data );
Out_del_dev:
Device_del (& shost-> shost_dev );
Out_del_gendev:
Device_del (& shost-> shost_gendev );
Out:
Scsi_destroy_command_freelist (shost );
Fail:
Return Error;
}
Supplement:
Sysfs is a new virtual memory-based file system in the Linux kernel. Its role is similar to that of Proc, in addition to the same features as proc, Proc has the ability to view and set kernel parameters. It also provides a Linux unified device model for management. Compared with the proc file system, sysfs is used to export kernel data in a more unified and better organized manner. Its Design draws many lessons from Proc. This article describes the mount point/sys directory structure of sysfs, its relationship with the Linux unified device model, and usage of common attribute files.
This document introduces sysfs and describes how to add sysfs support in kernel programming.