Commit c877e091 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by James Bottomley

[PATCH] allow registering individual HBAs

Yes, this is the patch every maintainer of a modern HBA waited for the
last years </shameless plug>.

With all my recent changes there's no more reason to call scsi_register_host
except for the intialization it performs to every host found in
scsi_register.  But a driver can aswell do that at the end of it's
per-HBA detection routine (i.e. in ->probe for a modern PCI driver), so
export that code as scsi_add_host to drivers.  Do the same for the
release path (scsi_remove_host).  Such a new-style driver needs neither
->detect or ->release and is in theory hotplug-capable (well, once all
the races in the scsi midlayer are fixed..)
parent 0dc70072
...@@ -90,7 +90,7 @@ int scsi_tp_for_each_host(Scsi_Host_Template *shost_tp, int ...@@ -90,7 +90,7 @@ int scsi_tp_for_each_host(Scsi_Host_Template *shost_tp, int
* This is the default case for the release function. Its completely * This is the default case for the release function. Its completely
* useless for anything but old ISA adapters * useless for anything but old ISA adapters
**/ **/
static void scsi_host_generic_release(struct Scsi_Host *shost) static void scsi_host_legacy_release(struct Scsi_Host *shost)
{ {
if (shost->irq) if (shost->irq)
free_irq(shost->irq, NULL); free_irq(shost->irq, NULL);
...@@ -100,18 +100,35 @@ static void scsi_host_generic_release(struct Scsi_Host *shost) ...@@ -100,18 +100,35 @@ static void scsi_host_generic_release(struct Scsi_Host *shost)
release_region(shost->io_port, shost->n_io_port); release_region(shost->io_port, shost->n_io_port);
} }
static int scsi_remove_legacy_host(struct Scsi_Host *shost)
{
int error, pcount = scsi_hosts_registered;
error = scsi_remove_host(shost);
if (error)
return error;
if (shost->hostt->release)
(*shost->hostt->release)(shost);
else
scsi_host_legacy_release(shost);
if (pcount == scsi_hosts_registered)
scsi_unregister(shost);
return 0;
}
/** /**
* scsi_host_chk_and_release - check a scsi host for release and release * scsi_remove_host - check a scsi host for release and release
* @shost: a pointer to a scsi host to release * @shost: a pointer to a scsi host to release
* *
* Return value: * Return value:
* 0 on Success / 1 on Failure * 0 on Success / 1 on Failure
**/ **/
int scsi_host_chk_and_release(struct Scsi_Host *shost) int scsi_remove_host(struct Scsi_Host *shost)
{ {
int pcount; struct scsi_device *sdev;
Scsi_Device *sdev; struct scsi_cmnd *scmd;
Scsi_Cmnd *scmd;
/* /*
* Current policy is all shosts go away on unregister. * Current policy is all shosts go away on unregister.
...@@ -199,18 +216,30 @@ int scsi_host_chk_and_release(struct Scsi_Host *shost) ...@@ -199,18 +216,30 @@ int scsi_host_chk_and_release(struct Scsi_Host *shost)
kfree(sdev); kfree(sdev);
} }
/* Remove the instance of the individual hosts */ return 0;
pcount = scsi_hosts_registered; }
if (shost->hostt->release)
(*shost->hostt->release) (shost);
else {
scsi_host_generic_release(shost);
}
if (pcount == scsi_hosts_registered) int scsi_add_host(struct Scsi_Host *shost)
scsi_unregister(shost); {
Scsi_Host_Template *sht = shost->hostt;
struct scsi_device *sdev;
int error = 0;
return 0; printk(KERN_INFO "scsi%d : %s\n", shost->host_no,
sht->info ? sht->info(shost) : sht->name);
device_register(&shost->host_driverfs_dev);
scan_scsis(shost, 0, 0, 0, 0);
for (sdev = shost->host_queue; sdev; sdev = sdev->next) {
if (sdev->host->hostt != sht)
continue; /* XXX(hch): can this really happen? */
error = scsi_attach_device(sdev);
if (error)
break;
}
return error;
} }
/** /**
...@@ -451,7 +480,6 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template *shost_tp, int xtr_bytes) ...@@ -451,7 +480,6 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template *shost_tp, int xtr_bytes)
return shost; return shost;
} }
/** /**
* scsi_register_host - register a low level host driver * scsi_register_host - register a low level host driver
* @shost_tp: pointer to a scsi host driver template * @shost_tp: pointer to a scsi host driver template
...@@ -461,10 +489,8 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template *shost_tp, int xtr_bytes) ...@@ -461,10 +489,8 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template *shost_tp, int xtr_bytes)
**/ **/
int scsi_register_host(Scsi_Host_Template *shost_tp) int scsi_register_host(Scsi_Host_Template *shost_tp)
{ {
int cur_cnt;
Scsi_Device *sdev;
struct list_head *lh;
struct Scsi_Host *shost; struct Scsi_Host *shost;
int cur_cnt;
/* /*
* Check no detect routine. * Check no detect routine.
...@@ -492,62 +518,40 @@ int scsi_register_host(Scsi_Host_Template *shost_tp) ...@@ -492,62 +518,40 @@ int scsi_register_host(Scsi_Host_Template *shost_tp)
* registration code below. * registration code below.
*/ */
shost_tp->detect(shost_tp); shost_tp->detect(shost_tp);
if (!shost_tp->present)
if (shost_tp->present) { return 0;
/*
* FIXME Who needs manual registration and why??? if (cur_cnt == scsi_hosts_registered) {
*/ if (shost_tp->present > 1) {
if (cur_cnt == scsi_hosts_registered) { printk(KERN_ERR "scsi: Failure to register"
if (shost_tp->present > 1) { "low-level scsi driver");
printk(KERN_ERR "scsi: Failure to register" scsi_unregister_host(shost_tp);
"low-level scsi driver"); return 1;
scsi_unregister_host(shost_tp);
return 1;
}
/*
* The low-level driver failed to register a driver.
* We can do this now.
*/
if(scsi_register(shost_tp, 0)==NULL) {
printk(KERN_ERR "scsi: register failed.\n");
scsi_unregister_host(shost_tp);
return 1;
}
}
/* The next step is to call scan_scsis here. This generates the
* Scsi_Devices entries
*/
list_for_each(lh, &scsi_host_list) {
shost = list_entry(lh, struct Scsi_Host, sh_list);
if (shost->hostt == shost_tp) {
const char *dm_name;
if (shost_tp->info) {
dm_name = shost_tp->info(shost);
} else {
dm_name = shost_tp->name;
}
printk(KERN_INFO "scsi%d : %s\n",
shost->host_no, dm_name);
/* first register parent with driverfs */
device_register(&shost->host_driverfs_dev);
scan_scsis(shost, 0, 0, 0, 0);
}
} }
/* /*
* Next we create the Scsi_Cmnd structures for this host * The low-level driver failed to register a driver.
* We can do this now.
*
* XXX Who needs manual registration and why???
*/ */
list_for_each(lh, &scsi_host_list) { if (!scsi_register(shost_tp, 0)) {
shost = list_entry(lh, struct Scsi_Host, sh_list); printk(KERN_ERR "scsi: register failed.\n");
for (sdev = shost->host_queue; sdev; sdev = sdev->next) scsi_unregister_host(shost_tp);
if (sdev->host->hostt == shost_tp) return 1;
if (scsi_attach_device(sdev))
goto out_of_space;
} }
} }
/*
* XXX(hch) use scsi_tp_for_each_host() once it propagates
* error returns properly.
*/
list_for_each_entry(shost, &scsi_host_list, sh_list)
if (shost->hostt == shost_tp)
if (scsi_add_host(shost))
goto out_of_space;
return 0; return 0;
out_of_space: out_of_space:
...@@ -579,7 +583,7 @@ int scsi_unregister_host(Scsi_Host_Template *shost_tp) ...@@ -579,7 +583,7 @@ int scsi_unregister_host(Scsi_Host_Template *shost_tp)
pcount = scsi_hosts_registered; pcount = scsi_hosts_registered;
scsi_tp_for_each_host(shost_tp, scsi_host_chk_and_release); scsi_tp_for_each_host(shost_tp, scsi_remove_legacy_host);
if (pcount != scsi_hosts_registered) if (pcount != scsi_hosts_registered)
printk(KERN_INFO "scsi : %d host%s left.\n", scsi_hosts_registered, printk(KERN_INFO "scsi : %d host%s left.\n", scsi_hosts_registered,
......
...@@ -528,8 +528,6 @@ extern void scsi_proc_host_rm(struct Scsi_Host *); ...@@ -528,8 +528,6 @@ extern void scsi_proc_host_rm(struct Scsi_Host *);
extern int next_scsi_host; extern int next_scsi_host;
unsigned int scsi_init(void); unsigned int scsi_init(void);
extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int);
extern void scsi_unregister(struct Scsi_Host *);
extern void scsi_register_blocked_host(struct Scsi_Host *); extern void scsi_register_blocked_host(struct Scsi_Host *);
extern void scsi_deregister_blocked_host(struct Scsi_Host *); extern void scsi_deregister_blocked_host(struct Scsi_Host *);
...@@ -580,10 +578,26 @@ void scsi_initialize_queue(Scsi_Device *, struct Scsi_Host *); ...@@ -580,10 +578,26 @@ void scsi_initialize_queue(Scsi_Device *, struct Scsi_Host *);
/* /*
* Driver registration/unregistration. * Highlevel driver registration/unregistration.
*/ */
extern int scsi_register_device(struct Scsi_Device_Template *); extern int scsi_register_device(struct Scsi_Device_Template *);
extern int scsi_unregister_device(struct Scsi_Device_Template *); extern int scsi_unregister_device(struct Scsi_Device_Template *);
/*
* HBA allocation/freeing.
*/
extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int);
extern void scsi_unregister(struct Scsi_Host *);
/*
* HBA registration/unregistration.
*/
extern int scsi_add_host(struct Scsi_Host *);
extern int scsi_remove_host(struct Scsi_Host *);
/*
* Legacy HBA template registration/unregistration.
*/
extern int scsi_register_host(Scsi_Host_Template *); extern int scsi_register_host(Scsi_Host_Template *);
extern int scsi_unregister_host(Scsi_Host_Template *); extern int scsi_unregister_host(Scsi_Host_Template *);
......
...@@ -32,6 +32,8 @@ EXPORT_SYMBOL(scsi_register_device); ...@@ -32,6 +32,8 @@ EXPORT_SYMBOL(scsi_register_device);
EXPORT_SYMBOL(scsi_unregister_device); EXPORT_SYMBOL(scsi_unregister_device);
EXPORT_SYMBOL(scsi_register_host); EXPORT_SYMBOL(scsi_register_host);
EXPORT_SYMBOL(scsi_unregister_host); EXPORT_SYMBOL(scsi_unregister_host);
EXPORT_SYMBOL(scsi_add_host);
EXPORT_SYMBOL(scsi_remove_host);
EXPORT_SYMBOL(scsi_register); EXPORT_SYMBOL(scsi_register);
EXPORT_SYMBOL(scsi_unregister); EXPORT_SYMBOL(scsi_unregister);
EXPORT_SYMBOL(scsicam_bios_param); EXPORT_SYMBOL(scsicam_bios_param);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment