Linux libata initialization Analysis

Source: Internet
Author: User

From: http://zhangwenxin82.blog.163.com/blog/static/114595956201071934831530/

I am quite touched by the libata module. The record is as follows. I hope it will be helpful to everyone and further deepen my understanding.
Linux: linux-2.6.24.3
Note: For personal understanding, improper understanding is inevitable. Please correct your criticism !!!!

Everyone knows the driverProgramAfter the SATA controller is initialized and the ata_host struct is initialized, the function is called.Ata_host_activateStart libata initialization.

The following is the SATA driver of The Freescale mpc8315 platform.Code.

Linux/driver/ATA/sata_fsl.c

Static int sata_fsl_probe (struct of_device * ofdev,
Const struct of_device_id * match)
{
Host_priv = kzarloc (sizeof (struct sata_fsl_host_priv), gfp_kernel );
If (! Host_priv)
Goto error_exit_with_cleanup;

IRQ = irq_of_parse_and_map (ofdev-> node, 0 );
If (IRQ <0 ){
Dev_printk (kern_err, & ofdev-> Dev, "invalid IRQ from platform \ n ");
Goto error_exit_with_cleanup;
}
Host_priv-> IRQ = IRQ;

/* Allocate host structure */
Host = ata_host_alloc_pinfo (& ofdev-> Dev, PPI, sata_fsl_max_ports );

/* Host-> iomap is not used currently */
Host-> private_data = host_priv;

/* Initialize host controller */
Sata_fsl_init_controller (host );

/*
* Now, register with libata core, this will also initiate
* Device discovery process, invoking our port_start () handler &
* Error_handler () to execute a dummy softreset eh session
*/
Ata_host_activate (host, IRQ, sata_fsl_interrupt, sata_fsl_irq_flag,
& Sata_fsl_sht );

Dev_set_drvdata (& ofdev-> Dev, host );

Return 0;
}

The ata_host_activate function requests an interruption and calls the ata_host_register function to register the host.

Linux/driver/ATA/libata-core.c

/**
* Ata_host_activate-start host, request IRQ and register it
* @ Host: Target ata host
* @ IRQ: IRQ to request
* @ Irq_handler: irq_handler used when requesting IRQ
* @ Irq_flags: irq_flags used when requesting IRQ
* @ Sht: scsi_host_template to use when registering the host
*
* After allocating an ATA host and initializing it, most libata
* Llds perform three steps to activate the host-start host,
* Request IRQ and register it. This helper takes necessasry
* Arguments and performs the three steps in one go.
*
* An invalid IRQ skips the IRQ registration and expects the host
* Have set polling mode on the port. In this case, @ irq_handler
* Shocould be null.
*
* Locking:
* Inherited from calling layer (may sleep ).
*
* Returns:
* 0 on success,-errno otherwise.
*/
Int ata_host_activate (struct ata_host * Host, int IRQ,
Irq_handler_t irq_handler, unsigned long irq_flags,
Struct scsi_host_template * ShT)
{
Int I, RC;

Rc = ata_host_start (host );
If (RC)
Return RC;

/* Special case for polling mode */
If (! IRQ ){
Warn_on (irq_handler );
Return ata_host_register (host, ShT );
}

Rc = devm_request_irq (host-> Dev, IRQ, irq_handler, irq_flags,
Dev_driver_string (host-> Dev), host );
If (RC)
Return RC;

For (I = 0; I Ata_port_desc (host-> ports [I], "IRQ % d", IRQ );

Rc = ata_host_register (host, ShT );
/* If failed, just free the IRQ and leave ports alone */
If (RC)
Devm_free_irq (host-> Dev, IRQ, host );

Return RC;
}

Linux/driver/ATA/libata-core.c

/**
* ata_host_register-register initialized ata host
* @ host: ata host to register
* @ sht: template for SCSI host
* Register initialized ata host. @ host is allocated using
* ata_host_alloc () and fully initialized by LLD. this function
* starts ports, registers @ host with ATA and SCSI layers and
* probe registered devices.
* locking:
* inherited from calling layer (may sleep ).
* returns:
* 0 on success,-errno otherwise.
*/
int ata_host_register (struct ata_host * Host, struct scsi_host_template * ShT)
{< br> int I, RC;

/* Host must have been started */
If (! (Host-> flags & ata_host_started )){
Dev_printk (kern_err, host-> Dev,
"Bug: trying to register unstarted host \ n ");
Warn_on (1 );
Return-einval;
}

/* Blow away unused ports. This happens when LLD can't
* Determine the exact number of ports to allocate
* Allocation time.
*/
For (I = Host-> n_ports; host-> ports [I]; I ++)
Kfree (host-> ports [I]);

/* Give ports names and add SCSI hosts */
For (I = 0; I Host-> ports [I]-> print_id = ata_print_id ++;

Rc = ata_scsi_add_hosts (host, ShT );
If (RC)
Return RC;

/* Associate with ACPI nodes */
Ata_acpi_associate (host );

/* Set cable, sata_spd_limit and report */
For (I = 0; I Struct ata_port * ap = Host-> ports [I];
Unsigned long xfer_mask;

/* Set SATA cable type if still unset */
If (ap-> CBl = ata_cbl_none & (ap-> flags & ata_flag_sata ))
AP-> CBl = ata_cbl_sata;

/* Init sata_spd_limit to the current value */
Sata_link_init_spd (& AP-> link );

/* Print per-port info to dmesg */
Xfer_mask = ata_pack_xfermask (ap-> pio_mask, ap-> mwdma_mask,
AP-> udma_mask );

If (! Ata_port_is_dummy (AP) {
ata_port_printk (AP, kern_info,
"% CATA max % S % s \ n ",
(ap-> flags & ata_flag_sata )? 'S ': 'P',
ata_mode_string (xfer_mask),
aP-> link. eh_info.desc);
ata_ehi_clear_desc (& AP-> link. eh_info);
}else
ata_port_printk (AP, kern_info, "dummy \ n");
}

/* Perform each probe synchronously */
Dprintk ("probe begin \ n ");
For (I = 0; I Struct ata_port * ap = Host-> ports [I];
Int RC;

/* Probe */
If (ap-> OPS-> error_handler ){
Struct ata_eh_info * Ehi = & AP-> link. eh_info;
Unsigned long flags;

Ata_port_probe (AP );

/* Kick eh for boot probing */
Spin_lock_irqsave (ap-> lock, flags );

Ehi-> probe_mask =
(1 <ata_link_max_devices (& AP-> link)-1;
Ehi-> action | = ata_eh_softreset;
Ehi-> flags | = ata_ehi_no_autopsy | ata_ehi_quiet;

AP-> pflags & = ~ Ata_pflag_initializing;
AP-> pflags | = ata_pflag_loading;
Ata_port_schedule_eh (AP );

Spin_unlock_irqrestore (ap-> lock, flags );

/* Wait for eh to finish */
ata_port_wait_eh (AP);
}else {
dprintk ("ata % u: bus probe begin \ n ", ap-> print_id);
rc = ata_bus_probe (AP);
dprintk (" ata % u: Bus probe end \ n ", AP-> print_id);

If (RC) {
/* fixme: Do something useful here?
* Current libata behavior will
* tear down everything when
* the module is removed
* or the H/W is unplugged.
*/
}< BR >}

/* Probes are done, now Scan each port's disk (s )*/
For (I = 0; I Struct ata_port * ap = Host-> ports [I];

Ata_scsi_scan_host (AP, 1 );
Ata_lpm_schedule (AP, ap-> pm_policy );
}

Return 0;
}
After the error_handler kernel thread is started in the ata_scsi_add_hosts function, the thread will be executed in the second part of the red code until the initialization is completed, the third part initializes each hard disk device (including allocating hard disk device nodes ).

Linux/driver/ATA/libata-scsi.c
Int ata_scsi_add_hosts (struct ata_host * Host, struct scsi_host_template * ShT)
{
Int I, RC;

For (I = 0; I Struct ata_port * ap = Host-> ports [I];
Struct scsi_host * shost;

Rc =-enomem;
Shost = scsi_host_alloc (SHT, sizeof (struct ata_port *));
If (! Shost)
Goto err_alloc;

* (Struct ata_port **) & shost-> hostdata [0] = ap;
AP-> scsi_host = shost;

Shost-> transportt = & ata_scsi_transport_template;
Shost-> unique_id = ap-> print_id;
Shost-> max_id = 16;
Shost-> max_lun = 1;
Shost-> max_channel = 1;
Shost-> max_cmd_len = 16;

/* Schedule policy is determined by-> qc_defer ()
* Callback and it needs to see every deferred QC.
* Set host_blocked to 1 to prevent SCSI midlayer from
* Automatically deferring requests.
*/
Shost-> max_host_blocked = 1;

Rc = scsi_add_host (ap-> scsi_host, ap-> host-> Dev );
If (RC)
Goto err_add;
}

Return 0;

Err_add:
Scsi_host_put (host-> ports [I]-> scsi_host );
Err_alloc:
While (-- I> = 0 ){
Struct scsi_host * shost = Host-> ports [I]-> scsi_host;

Scsi_remove_host (shost );
Scsi_host_put (shost );
}
Return RC;
}
Ata_scsi_add_hosts initializes the structure required by the SCSI layer and registers it with the SCSI module to connect the SCSI to the ATA layer.

Linux/driver/SCSI/hosts. c

/**
* 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;

Shost = kzarloc (sizeof (struct scsi_host) + privsize, gfp_mask );
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 );

Shost-> host_no = scsi_host_next_hn ++;/* XXX (HCH): Still racy */
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;
Shost-> active_mode = sht-> supported_mode;
Shost-> use_sg_chaining = sht-> use_sg_chaining;

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;

Rval = scsi_setup_command_freelist (shost );
If (rval)
Goto fail_kfree;

Device_initialize (& shost-> shost_gendev );
Snprintf (shost-> shost_gendev.bus_id, bus_id_size, "host % d ",
Shost-> host_no );
Shost-> shost_gendev.release = scsi_host_dev_release;

Class_device_initialize (& shost-> shost_classdev );
Shost-> shost_classdev.dev = & shost-> shost_gendev;
Shost-> shost_classdev.class = & shost_class;
Snprintf (shost-> shost_classdev.class_id, bus_id_size, "host % d ",
Shost-> host_no );

Shost-> ehandler = kthread_run (scsi_error_handler, shost,
"Scsi_eh _ % d", shost-> host_no );
If (is_err (shost-> ehandler )){
Rval = ptr_err (shost-> ehandler );
Goto fail_destroy_freelist;
}

Scsi_proc_hostdir_add (shost-> hostt );
Return shost;

fail_destroy_freelist:
scsi_destroy_command_freelist (shost);
fail_kfree:
kfree (shost);
return NULL;
}< br> export_symbol (scsi_host_alloc);
after scsi_alloc_hosts is executed, the kernel has one more thread to execute scsi_error_handler, And the handler continues the initial structure of scsi_host:
shost-> transportt = & ata_scsi_transport_template; is called in scsi_error_handler.

/**
* scsi_error_handler-SCSI error handler thread
* @ data: host for which we are running.
* Notes:
* this is the main error handling loop. this is run as a kernel thread
* for every SCSI host and handles all error handling activity.
**/
int scsi_error_handler (void * Data)
{< br> struct scsi_host * shost = data;

/*
* We use task_interruptible so that the thread is not
* Counted against the load average as a running process.
* We never actually get interrupted because kthread_run
* Disables singal delivery for the created thread.
*/
Set_current_state (task_interruptible );
While (! Kthread_should_stop ()){
If (shost-> host_failed = 0 & shost-> host_eh_scheduled = 0) |
Shost-> host_failed! = Shost-> host_busy ){
Scsi_log_error_recovery (1,
Printk ("error handler scsi_eh _ % d sleeping \ n ",
Shost-> host_no ));
Schedule ();
Set_current_state (task_interruptible );
Continue;
}

_ set_current_state (task_running);
scsi_log_error_recovery (1,
printk ("error handler scsi_eh _ % d Waking up \ n ",
shost-> host_no);

/*
* We Have A host that is failing for some reason. figure out
* what we need to do to get it up and online again (if we can ).
* If we fail, we end up taking the thing offline.
*/
If (shost-> transportt-> eh_strategy_handler)
shost-> transportt-> eh_strategy_handler (shost);
else
scsi_unjam_host (shost);

/*
* Note-if the above fails completely, the action is to take
* individual devices offline and flush the queue of any
* outstanding requests that may have been pending. when we
* restart, we restart any I/O to any other devices on the bus
* which are still online.
*/
scsi_restart_operations (shost);
set_current_state (task_interruptible);
}< br> _ set_current_state (task_running);

scsi_log_error_recovery (1,
printk ("error handler scsi_eh _ % d exiting \ n", shost-> host_no ));
shost-> ehandler = NULL;
return 0;
}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.