add block/unblock to iscsi class
PATCH 1/1 add block/unblock to iscsi class
The attached patch adds session block and unblock functions similar to the rport block and unblock code.
The patch was built against scsi misc (but also pathces against scsi rc fixes) and this patch
http://marc.theaimsgroup.com/?l=linux-scsi&m=111181778109783&w=2
which adds a scsi_flush_work function.
Signed-off-by Mike Chrisite <[email protected]>
drivers/scsi/scsi_transport_iscsi.c +294 -59
@@ -25,7 +25,9 @@#include <scsi/scsi_transport.h> #include <scsi/scsi_transport_iscsi.h> -#define ISCSI_SESSION_ATTRS 20 +#include "scsi_priv.h" + +#define ISCSI_SESSION_ATTRS 21#define ISCSI_HOST_ATTRS 2struct iscsi_internal {
@@ -34,23 +36,13 @@ struct iscsi_internal {/* * We do not have any private or other attrs. */ +struct transport_container session_cont;struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS +1 ];struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS +1 ]; };#define to_iscsi_internal (tmpl) container_of(tmpl, struct iscsi_internal, t) -static DECLARE_TRANSPORT_CLASS(iscsi_transport_class, -"iscsi_transport" , -NULL , -NULL , - NULL); - -static DECLARE_TRANSPORT_CLASS(iscsi_host_class, -"iscsi_host" , -NULL , -NULL , - NULL);/* * iSCSI target and session attrs */
@@ -59,19 +51,40 @@ static DECLARE_TRANSPORT_CLASS(iscsi_hos static ssize_t \ show_session_##field(struct class_device *cdev, char *buf) \ { \ - struct scsi_target *starget = transport_class_to_starget(cdev); \ - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ + struct iscsi_class_session *session = iscsi_cdev_to_session(cdev); \ + struct Scsi_Host *shost = iscsi_session_to_shost(session); \ struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ \ if (i->fnt->get_##field) \ - i->fnt->get_##field(starget); \ - return snprintf(buf, 20, format"\n", iscsi_##field(starget)); \ + i->fnt->get_##field(session); \ + return snprintf(buf, 20, format"\n", session->field); \ +} + +#define iscsi_session_store_fn (field, format_string) \ +static ssize_t \ +store_session_##field(struct class_device *cdev, const char *buf, \ + size_t count) \ +{ \ + struct iscsi_class_session *session = iscsi_cdev_to_session(cdev); \ + struct Scsi_Host *shost = iscsi_session_to_shost(session); \ + struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ + int val; \ + \ + val = simple_strtoul(buf, NULL, 0); \ + i->fnt->set_##field(session, val); \ + return count; \ }#define iscsi_session_rd_attr (field, format) \ iscsi_session_show_fn(field, format) \ static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_##field, NULL); +#define iscsi_session_rw_attr (field, format) \ + iscsi_session_show_fn(field, format) \ + iscsi_session_store_fn(field, format) \ +static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR, \ + show_session_##field, store_session_##field) +iscsi_session_rd_attr (tpgt,"%hu" );iscsi_session_rd_attr (tsih,"%2x" );iscsi_session_rd_attr (max_recv_data_segment_len,"%u" );
@@ -81,21 +94,21 @@ iscsi_session_rd_attr(def_time2wait, "%hiscsi_session_rd_attr (def_time2retain,"%hu" );iscsi_session_rd_attr (max_outstanding_r2t,"%hu" );iscsi_session_rd_attr (erl,"%d" ); - +iscsi_session_rw_attr (session_recovery_tmo,"%d" );#define iscsi_session_show_bool_fn (field) \ \ static ssize_t \ show_session_bool_##field(struct class_device *cdev, char *buf) \ { \ - struct scsi_target *starget = transport_class_to_starget(cdev); \ - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ + struct iscsi_class_session *session = iscsi_cdev_to_session(cdev); \ + struct Scsi_Host *shost = iscsi_session_to_shost(session); \ struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ \ if (i->fnt->get_##field) \ - i->fnt->get_##field(starget); \ + i->fnt->get_##field(session); \ \ - if (iscsi_##field(starget)) \ + if (session->field) \ return sprintf(buf, "Yes\n"); \ return sprintf(buf, "No\n"); \ }
@@ -114,14 +127,14 @@ iscsi_session_rd_bool_attr(data_sequence static ssize_t \ show_##field(struct class_device *cdev, char *buf) \ { \ - struct scsi_target *starget = transport_class_to_starget(cdev); \ - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ + struct iscsi_class_session *session = iscsi_cdev_to_session(cdev); \ + struct Scsi_Host *shost = iscsi_session_to_shost(session); \ struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ \ if (i->fnt->get_##field) \ - i->fnt->get_##field(starget); \ + i->fnt->get_##field(session); \ \ - if (iscsi_##field(starget)) \ + if (session->field) \ return sprintf(buf, "CRC32C\n"); \ return sprintf(buf, "None\n"); \ }
@@ -136,33 +149,33 @@ iscsi_session_rd_digest_attr(data_digeststatic ssize_t show_port (struct class_device *cdev,char *buf) { -struct scsi_target *starget =transport_class_to_starget (cdev); -struct Scsi_Host *shost =dev_to_shost (starget->dev.parent); +struct iscsi_class_session *session =iscsi_cdev_to_session (cdev); +struct Scsi_Host *shost =iscsi_session_to_shost (session);struct iscsi_internal *i =to_iscsi_internal (shost->transportt);if (i->fnt->get_port) - i->fnt->get_port (starget); + i->fnt->get_port (session); -return snprintf (buf,20 ,"%hu\n" ,ntohs (iscsi_port (starget))); +return snprintf (buf,20 ,"%hu\n" ,ntohs (session->port)); } static CLASS_DEVICE_ATTR(port, S_IRUGO, show_port, NULL);static ssize_t show_ip_address (struct class_device *cdev,char *buf) { -struct scsi_target *starget =transport_class_to_starget (cdev); -struct Scsi_Host *shost =dev_to_shost (starget->dev.parent); +struct iscsi_class_session *session =iscsi_cdev_to_session (cdev); +struct Scsi_Host *shost =iscsi_session_to_shost (session);struct iscsi_internal *i =to_iscsi_internal (shost->transportt);if (i->fnt->get_ip_address) - i->fnt->get_ip_address (starget); + i->fnt->get_ip_address (session); -if (iscsi_addr_type (starget) ==AF_INET ) +if (session->addr_type ==AF_INET )return sprintf (buf,"%u.%u.%u.%u\n" , -NIPQUAD (iscsi_sin_addr (starget))); -else if (iscsi_addr_type (starget) ==AF_INET6 ) +NIPQUAD (session->u.sin_addr)); +else if (session->addr_type ==AF_INET6 )return sprintf (buf,"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n" , -NIP6 (iscsi_sin6_addr (starget))); +NIP6 (session->u.sin6_addr));return -EINVAL ; } static CLASS_DEVICE_ATTR(ip_address, S_IRUGO, show_ip_address, NULL);
@@ -170,17 +183,17 @@ static CLASS_DEVICE_ATTR(ip_address, S_Istatic ssize_t show_isid (struct class_device *cdev,char *buf) { -struct scsi_target *starget =transport_class_to_starget (cdev); -struct Scsi_Host *shost =dev_to_shost (starget->dev.parent); +struct iscsi_class_session *session =iscsi_cdev_to_session (cdev); +struct Scsi_Host *shost =iscsi_session_to_shost (session);struct iscsi_internal *i =to_iscsi_internal (shost->transportt);if (i->fnt->get_isid) - i->fnt->get_isid (starget); + i->fnt->get_isid (session);return sprintf (buf,"%02x%02x%02x%02x%02x%02x\n" , -iscsi_isid (starget)[0 ],iscsi_isid (starget)[1 ], -iscsi_isid (starget)[2 ],iscsi_isid (starget)[3 ], -iscsi_isid (starget)[4 ],iscsi_isid (starget)[5 ]); + session->isid[0 ], session->isid[1 ], + session->isid[2 ], session->isid[3 ], + session->isid[4 ], session->isid[5 ]); } static CLASS_DEVICE_ATTR(isid, S_IRUGO, show_isid, NULL);
@@ -196,12 +209,12 @@ static ssize_t \ show_session_str_##field(struct class_device *cdev, char *buf) \ { \ ssize_t ret = 0; \ - struct scsi_target *starget = transport_class_to_starget(cdev); \ - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ + struct iscsi_class_session *session = iscsi_cdev_to_session(cdev); \ + struct Scsi_Host *shost = iscsi_session_to_shost(session); \ struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ \ if (i->fnt->get_##field) \ - ret = i->fnt->get_##field(starget, buf, PAGE_SIZE); \ + ret = i->fnt->get_##field(session, buf, PAGE_SIZE); \ return ret; \ }
@@ -242,12 +255,227 @@ static CLASS_DEVICE_ATTR(field, S_IRUGO,iscsi_host_rd_str_attr (initiator_name);iscsi_host_rd_str_attr (initiator_alias); +/** +* iscsi_block_session - block IO to session. +* @session: iscsi_session +* +* scsi lldd's with a iSCSI transport call this routine to temporarily stop all +* scsi commands to all devices managed by this scsi target. Called +* from interrupt or normal process context. +* +* Returns zero if successful or error if not +* +* Notes: +* This routine assumes no locks are held on entry. +**/ +int iscsi_block_session (struct iscsi_class_session *session) +{ +struct work_struct *work = &session->session_recovery_work; +struct Scsi_Host *shost =iscsi_session_to_shost (session); +struct iscsi_internal *i =to_iscsi_internal (shost->transportt); +int timeout; + + i->fnt->get_session_recovery_tmo (session); + timeout = session->session_recovery_tmo; +if (timeout <0 ) +return -EINVAL ; + +scsi_target_block (&session->dev); + +/* The scsi lld blocks this target for the timeout period only. */ +schedule_delayed_work (work, timeout); +return 0 ; +} +EXPORT_SYMBOL_GPL (iscsi_block_session); + +/** +* iscsi_unblocksession - unblock IO to a session. +* @session: scsi target managed by this iscsi scsi lldd. +* +* scsi lld's with a iSCSI transport call this routine to restart IO to all +* devices associated with the caller's scsi target following a +* iscsi_target_block request. Called from normal process context. +* +* Notes: +* This routine assumes no locks are held on entry. +**/ +void iscsi_unblock_session (struct iscsi_class_session *session) +{ +/* +* Stop the target timer first. Take no action on the del_timer +* failure as the state machine state change will validate the +* transaction. +*/ +if (!cancel_delayed_work (&session->session_recovery_work)) +flush_scheduled_work (); + +scsi_target_unblock (&session->dev); +} +EXPORT_SYMBOL_GPL (iscsi_unblock_session); + +/** +* iscsi_session_block_timedout - Timeout handler for blocked scsi targets +* that fail to recover in the alloted time. +* @data: iscsi session that failed to reappear in the alloted time. +**/ +static void iscsi_session_block_timedout (void *data) +{ +struct iscsi_class_session *session = data; +struct Scsi_Host *shost =iscsi_session_to_shost (session); +struct iscsi_internal *i =to_iscsi_internal (shost->transportt); + +dev_printk (KERN_ERR , &session->dev, +"blocked session time out: target resuming\n" ); + +if (i->fnt->session_recovery_timedout) + i->fnt->session_recovery_timedout (session); +/* +* set the device going again ... if the scsi lld didn't +* unblock this device, then IO errors will probably +* result if the host still isn't ready. +*/ +scsi_target_unblock (&session->dev); +} + +/** +* iscsi_scan_session - called to perform a scsi scan on a session's target. +* @data: session to be scanned. +**/ +static void +iscsi_scan_session (void *data) +{ +struct iscsi_class_session *session = data; + +scsi_scan_target (&session->dev, session->channel, session->target_id, +SCAN_WILD_CARD ,1 ); +} + +static void iscsi_session_dev_release (struct device *dev) +{ +struct iscsi_class_session *session =iscsi_dev_to_session (dev); +struct Scsi_Host *shost =iscsi_session_to_shost (session); + +kfree (session); +scsi_host_put (shost); +} + +/** +* iscsi_add_session - add a session +* @shost: scsi host session is attached to +* @channel: channel on host session is attached to +* +* Allocate and add a iscsi session. After this call it will be accessable +* through sysfs. The LLD must set the create_workqueue bit in it's +* transport so iscsi_add_session can scan the sessions's target from +* the workqueue. +**/ +struct iscsi_class_session * +iscsi_add_session (struct Scsi_Host *shost,unsigned int channel) +{ +struct iscsi_class_session *session; +struct iscsi_host *iscsi_host; +struct device *dev; + +/* +* get a handle for the session since it references the +* template's internal data (this is released in the class's +* release function) +*/ + shost =scsi_host_get (shost); +if (!shost) { +printk (KERN_ERR "iSCSI: could not get reference to shost\n" ); +return NULL ; + } + iscsi_host = shost->shost_data; + + session =kmalloc (sizeof (*session),GFP_KERNEL ); +if (!session) { +printk (KERN_ERR "iSCSI: could not allocate session\n" ); + goto put_host; + } + +memset (session,0 ,sizeof (*session)); +INIT_WORK (&session->session_recovery_work, + iscsi_session_block_timedout, session); +INIT_WORK (&session->scan_work, iscsi_scan_session, session); + session->session_recovery_tmo =15 ; + session->channel = channel; + session->target_id = iscsi_host->next_target_id++; + + dev = &session->dev; +snprintf (dev->bus_id,BUS_ID_SIZE ,"session%u:%u:%u" , shost->host_no, + session->channel, session->target_id); + dev->parent = &shost->shost_gendev; + dev->release = iscsi_session_dev_release; +if (device_register (dev)) { +printk (KERN_ERR "iSCSI: could not register session dev\n" ); + goto free_session; + } + +transport_register_device (dev); +scsi_queue_work (shost, &session->scan_work); + +return session; + + free_session: +kfree (session); + put_host: +scsi_host_put (shost); +return NULL ; +} +EXPORT_SYMBOL_GPL (iscsi_add_session); + +void +iscsi_remove_session (struct iscsi_class_session *session) +{ +struct Scsi_Host *shost =iscsi_session_to_shost (session); + +if (!cancel_delayed_work (&session->session_recovery_work)) +flush_scheduled_work (); +scsi_flush_work (shost); + +transport_unregister_device (&session->dev); +device_unregister (&session->dev); +} +EXPORT_SYMBOL_GPL (iscsi_remove_session); + +static DECLARE_TRANSPORT_CLASS(iscsi_session_class, +"iscsi_session" , +NULL , +NULL , + NULL); + +static int iscsi_setup_host (struct device *dev) +{ +struct Scsi_Host *shost =dev_to_shost (dev); +struct iscsi_host *ihost = shost->shost_data; + + ihost->next_target_id =0 ; +return 0 ; +} + +static DECLARE_TRANSPORT_CLASS(iscsi_host_class, +"iscsi_host" , + iscsi_setup_host, +NULL , + NULL); +#define SETUP_SESSION_RD_ATTR (field) \ if (i->fnt->show_##field) { \ i->session_attrs[count] = &class_device_attr_##field; \ count++; \ } +#define SETUP_SESSION_RW_ATTR (field) \ + if (i->fnt->show_##field) { \ + i->session_attrs[count] = &class_device_attr_##field; \ + if (!i->fnt->set_##field) { \ + i->session_attrs[count]->attr.mode = S_IRUGO; \ + i->session_attrs[count]->store = NULL; \ + } \ + count++; \ + } +#define SETUP_HOST_RD_ATTR (field) \ if (i->fnt->show_##field) { \ i->host_attrs[count] = &class_device_attr_##field; \
@@ -273,13 +501,18 @@ static int iscsi_host_match(struct attrireturn &i->t.host_attrs.ac == cont; } -static int iscsi_target_match (struct attribute_container *cont, -struct device *dev) +static int iscsi_dev_is_session (const struct device *dev) +{ +return dev->release == iscsi_session_dev_release; +} + +static int iscsi_session_match (struct attribute_container *cont, +struct device *dev) {struct Scsi_Host *shost;struct iscsi_internal *i; -if (!scsi_is_target_device (dev)) +if (!iscsi_dev_is_session (dev))return 0 ; shost =dev_to_shost (dev->parent);
@@ -288,8 +521,8 @@ static int iscsi_target_match(struct attreturn 0 ; i =to_iscsi_internal (shost->transportt); - -return &i->t.target_attrs.ac == cont; + +return &i->session_cont.ac == cont; } struct scsi_transport_template *
@@ -304,12 +537,12 @@ iscsi_attach_transport(struct iscsi_funcmemset (i,0 ,sizeof (struct iscsi_internal )); i->fnt = fnt; + i->t.create_work_queue =1 ; - i->t.target_attrs.ac.attrs = &i->session_attrs[0 ]; - i->t.target_attrs.ac.class = &iscsi_transport_class.class; - i->t.target_attrs.ac.match = iscsi_target_match; -transport_container_register (&i->t.target_attrs); - i->t.target_size =sizeof (struct iscsi_class_session ); + i->session_cont.ac.attrs = &i->session_attrs[0 ]; + i->session_cont.ac.class = &iscsi_session_class.class; + i->session_cont.ac.match = iscsi_session_match; +transport_container_register (&i->session_cont);SETUP_SESSION_RD_ATTR (tsih);SETUP_SESSION_RD_ATTR (isid);
@@ -331,6 +564,7 @@ iscsi_attach_transport(struct iscsi_funcSETUP_SESSION_RD_ATTR (data_pdu_in_order);SETUP_SESSION_RD_ATTR (data_sequence_in_order);SETUP_SESSION_RD_ATTR (erl); +SETUP_SESSION_RW_ATTR (session_recovery_tmo);BUG_ON (count >ISCSI_SESSION_ATTRS ); i->session_attrs[count] =NULL ;
@@ -339,7 +573,7 @@ iscsi_attach_transport(struct iscsi_func i->t.host_attrs.ac.class = &iscsi_host_class.class; i->t.host_attrs.ac.match = iscsi_host_match;transport_container_register (&i->t.host_attrs); - i->t.host_size =0 ; + i->t.host_size =sizeof (struct iscsi_host ); count =0 ;SETUP_HOST_RD_ATTR (initiator_name);
@@ -357,7 +591,7 @@ void iscsi_release_transport(struct scsi {struct iscsi_internal *i =to_iscsi_internal (t); -transport_container_unregister (&i->t.target_attrs); +transport_container_unregister (&i->session_cont);transport_container_unregister (&i->t.host_attrs);kfree (i);
@@ -367,17 +601,18 @@ EXPORT_SYMBOL(iscsi_release_transport);static __init int iscsi_transport_init (void ) { -int err =transport_class_register (&iscsi_transport_class); +int err =transport_class_register (&iscsi_session_class);if (err)return err; +return transport_class_register (&iscsi_host_class); } static void __exit iscsi_transport_exit(void) {transport_class_unregister (&iscsi_host_class); -transport_class_unregister (&iscsi_transport_class); +transport_class_unregister (&iscsi_session_class); }module_init (iscsi_transport_init);
include/scsi/scsi_transport_iscsi.h +73 -84
190 lines not on this page · raw message