diff options
author | Mike Christie <michaelc@cs.wisc.edu> | 2008-01-31 14:36:43 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-02-07 19:02:34 -0500 |
commit | 6eabafbe6616266e8de61980a7dac5ecc1ba1113 (patch) | |
tree | f5533027fac576acf4db08210ea5815db3a15e50 /drivers/scsi/scsi_transport_iscsi.c | |
parent | 84ac86ca8c6787f9efff28bc04b1b65fe0a5c310 (diff) |
[SCSI] iscsi class, libiscsi: add iscsi sysfs session state file
This adds a iscsi session state file which exports the session
state for both software and hardware iscsi. It also hooks libiscsi
in.
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/scsi_transport_iscsi.c')
-rw-r--r-- | drivers/scsi/scsi_transport_iscsi.c | 107 |
1 files changed, 104 insertions, 3 deletions
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 0d7b4e79415c..f876b0ae521a 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c | |||
@@ -30,7 +30,7 @@ | |||
30 | #include <scsi/scsi_transport_iscsi.h> | 30 | #include <scsi/scsi_transport_iscsi.h> |
31 | #include <scsi/iscsi_if.h> | 31 | #include <scsi/iscsi_if.h> |
32 | 32 | ||
33 | #define ISCSI_SESSION_ATTRS 18 | 33 | #define ISCSI_SESSION_ATTRS 19 |
34 | #define ISCSI_CONN_ATTRS 11 | 34 | #define ISCSI_CONN_ATTRS 11 |
35 | #define ISCSI_HOST_ATTRS 4 | 35 | #define ISCSI_HOST_ATTRS 4 |
36 | #define ISCSI_TRANSPORT_VERSION "2.0-867" | 36 | #define ISCSI_TRANSPORT_VERSION "2.0-867" |
@@ -221,6 +221,54 @@ static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid) | |||
221 | * The following functions can be used by LLDs that allocate | 221 | * The following functions can be used by LLDs that allocate |
222 | * their own scsi_hosts or by software iscsi LLDs | 222 | * their own scsi_hosts or by software iscsi LLDs |
223 | */ | 223 | */ |
224 | static struct { | ||
225 | int value; | ||
226 | char *name; | ||
227 | } iscsi_session_state_names[] = { | ||
228 | { ISCSI_SESSION_LOGGED_IN, "LOGGED_IN" }, | ||
229 | { ISCSI_SESSION_FAILED, "FAILED" }, | ||
230 | { ISCSI_SESSION_FREE, "FREE" }, | ||
231 | }; | ||
232 | |||
233 | const char *iscsi_session_state_name(int state) | ||
234 | { | ||
235 | int i; | ||
236 | char *name = NULL; | ||
237 | |||
238 | for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) { | ||
239 | if (iscsi_session_state_names[i].value == state) { | ||
240 | name = iscsi_session_state_names[i].name; | ||
241 | break; | ||
242 | } | ||
243 | } | ||
244 | return name; | ||
245 | } | ||
246 | |||
247 | int iscsi_session_chkready(struct iscsi_cls_session *session) | ||
248 | { | ||
249 | unsigned long flags; | ||
250 | int err; | ||
251 | |||
252 | spin_lock_irqsave(&session->lock, flags); | ||
253 | switch (session->state) { | ||
254 | case ISCSI_SESSION_LOGGED_IN: | ||
255 | err = 0; | ||
256 | break; | ||
257 | case ISCSI_SESSION_FAILED: | ||
258 | err = DID_IMM_RETRY << 16; | ||
259 | break; | ||
260 | case ISCSI_SESSION_FREE: | ||
261 | err = DID_NO_CONNECT << 16; | ||
262 | break; | ||
263 | default: | ||
264 | err = DID_NO_CONNECT << 16; | ||
265 | break; | ||
266 | } | ||
267 | spin_unlock_irqrestore(&session->lock, flags); | ||
268 | return err; | ||
269 | } | ||
270 | EXPORT_SYMBOL_GPL(iscsi_session_chkready); | ||
271 | |||
224 | static void iscsi_session_release(struct device *dev) | 272 | static void iscsi_session_release(struct device *dev) |
225 | { | 273 | { |
226 | struct iscsi_cls_session *session = iscsi_dev_to_session(dev); | 274 | struct iscsi_cls_session *session = iscsi_dev_to_session(dev); |
@@ -259,26 +307,57 @@ static void session_recovery_timedout(struct work_struct *work) | |||
259 | struct iscsi_cls_session *session = | 307 | struct iscsi_cls_session *session = |
260 | container_of(work, struct iscsi_cls_session, | 308 | container_of(work, struct iscsi_cls_session, |
261 | recovery_work.work); | 309 | recovery_work.work); |
310 | unsigned long flags; | ||
262 | 311 | ||
263 | dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed " | 312 | dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed " |
264 | "out after %d secs\n", session->recovery_tmo); | 313 | "out after %d secs\n", session->recovery_tmo); |
265 | 314 | ||
315 | spin_lock_irqsave(&session->lock, flags); | ||
316 | switch (session->state) { | ||
317 | case ISCSI_SESSION_FAILED: | ||
318 | session->state = ISCSI_SESSION_FREE; | ||
319 | break; | ||
320 | case ISCSI_SESSION_LOGGED_IN: | ||
321 | case ISCSI_SESSION_FREE: | ||
322 | /* we raced with the unblock's flush */ | ||
323 | spin_unlock_irqrestore(&session->lock, flags); | ||
324 | return; | ||
325 | } | ||
326 | spin_unlock_irqrestore(&session->lock, flags); | ||
327 | |||
266 | if (session->transport->session_recovery_timedout) | 328 | if (session->transport->session_recovery_timedout) |
267 | session->transport->session_recovery_timedout(session); | 329 | session->transport->session_recovery_timedout(session); |
268 | 330 | ||
269 | scsi_target_unblock(&session->dev); | 331 | scsi_target_unblock(&session->dev); |
270 | } | 332 | } |
271 | 333 | ||
272 | void iscsi_unblock_session(struct iscsi_cls_session *session) | 334 | void __iscsi_unblock_session(struct iscsi_cls_session *session) |
273 | { | 335 | { |
274 | if (!cancel_delayed_work(&session->recovery_work)) | 336 | if (!cancel_delayed_work(&session->recovery_work)) |
275 | flush_workqueue(iscsi_eh_timer_workq); | 337 | flush_workqueue(iscsi_eh_timer_workq); |
276 | scsi_target_unblock(&session->dev); | 338 | scsi_target_unblock(&session->dev); |
277 | } | 339 | } |
340 | |||
341 | void iscsi_unblock_session(struct iscsi_cls_session *session) | ||
342 | { | ||
343 | unsigned long flags; | ||
344 | |||
345 | spin_lock_irqsave(&session->lock, flags); | ||
346 | session->state = ISCSI_SESSION_LOGGED_IN; | ||
347 | spin_unlock_irqrestore(&session->lock, flags); | ||
348 | |||
349 | __iscsi_unblock_session(session); | ||
350 | } | ||
278 | EXPORT_SYMBOL_GPL(iscsi_unblock_session); | 351 | EXPORT_SYMBOL_GPL(iscsi_unblock_session); |
279 | 352 | ||
280 | void iscsi_block_session(struct iscsi_cls_session *session) | 353 | void iscsi_block_session(struct iscsi_cls_session *session) |
281 | { | 354 | { |
355 | unsigned long flags; | ||
356 | |||
357 | spin_lock_irqsave(&session->lock, flags); | ||
358 | session->state = ISCSI_SESSION_FAILED; | ||
359 | spin_unlock_irqrestore(&session->lock, flags); | ||
360 | |||
282 | scsi_target_block(&session->dev); | 361 | scsi_target_block(&session->dev); |
283 | queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work, | 362 | queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work, |
284 | session->recovery_tmo * HZ); | 363 | session->recovery_tmo * HZ); |
@@ -327,10 +406,12 @@ iscsi_alloc_session(struct Scsi_Host *shost, | |||
327 | 406 | ||
328 | session->transport = transport; | 407 | session->transport = transport; |
329 | session->recovery_tmo = 120; | 408 | session->recovery_tmo = 120; |
409 | session->state = ISCSI_SESSION_FREE; | ||
330 | INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); | 410 | INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); |
331 | INIT_LIST_HEAD(&session->host_list); | 411 | INIT_LIST_HEAD(&session->host_list); |
332 | INIT_LIST_HEAD(&session->sess_list); | 412 | INIT_LIST_HEAD(&session->sess_list); |
333 | INIT_WORK(&session->unbind_work, __iscsi_unbind_session); | 413 | INIT_WORK(&session->unbind_work, __iscsi_unbind_session); |
414 | spin_lock_init(&session->lock); | ||
334 | 415 | ||
335 | /* this is released in the dev's release function */ | 416 | /* this is released in the dev's release function */ |
336 | scsi_host_get(shost); | 417 | scsi_host_get(shost); |
@@ -444,7 +525,10 @@ void iscsi_remove_session(struct iscsi_cls_session *session) | |||
444 | * If we are blocked let commands flow again. The lld or iscsi | 525 | * If we are blocked let commands flow again. The lld or iscsi |
445 | * layer should set up the queuecommand to fail commands. | 526 | * layer should set up the queuecommand to fail commands. |
446 | */ | 527 | */ |
447 | iscsi_unblock_session(session); | 528 | spin_lock_irqsave(&session->lock, flags); |
529 | session->state = ISCSI_SESSION_FREE; | ||
530 | spin_unlock_irqrestore(&session->lock, flags); | ||
531 | __iscsi_unblock_session(session); | ||
448 | iscsi_unbind_session(session); | 532 | iscsi_unbind_session(session); |
449 | /* | 533 | /* |
450 | * If the session dropped while removing devices then we need to make | 534 | * If the session dropped while removing devices then we need to make |
@@ -661,16 +745,23 @@ EXPORT_SYMBOL_GPL(iscsi_recv_pdu); | |||
661 | 745 | ||
662 | void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) | 746 | void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) |
663 | { | 747 | { |
748 | struct iscsi_cls_session *session = iscsi_conn_to_session(conn); | ||
664 | struct nlmsghdr *nlh; | 749 | struct nlmsghdr *nlh; |
665 | struct sk_buff *skb; | 750 | struct sk_buff *skb; |
666 | struct iscsi_uevent *ev; | 751 | struct iscsi_uevent *ev; |
667 | struct iscsi_internal *priv; | 752 | struct iscsi_internal *priv; |
668 | int len = NLMSG_SPACE(sizeof(*ev)); | 753 | int len = NLMSG_SPACE(sizeof(*ev)); |
754 | unsigned long flags; | ||
669 | 755 | ||
670 | priv = iscsi_if_transport_lookup(conn->transport); | 756 | priv = iscsi_if_transport_lookup(conn->transport); |
671 | if (!priv) | 757 | if (!priv) |
672 | return; | 758 | return; |
673 | 759 | ||
760 | spin_lock_irqsave(&session->lock, flags); | ||
761 | if (session->state == ISCSI_SESSION_LOGGED_IN) | ||
762 | session->state = ISCSI_SESSION_FAILED; | ||
763 | spin_unlock_irqrestore(&session->lock, flags); | ||
764 | |||
674 | skb = alloc_skb(len, GFP_ATOMIC); | 765 | skb = alloc_skb(len, GFP_ATOMIC); |
675 | if (!skb) { | 766 | if (!skb) { |
676 | dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored " | 767 | dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored " |
@@ -1246,6 +1337,15 @@ iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0); | |||
1246 | iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0); | 1337 | iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0); |
1247 | iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0); | 1338 | iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0); |
1248 | 1339 | ||
1340 | static ssize_t | ||
1341 | show_priv_session_state(struct class_device *cdev, char *buf) | ||
1342 | { | ||
1343 | struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); | ||
1344 | return sprintf(buf, "%s\n", iscsi_session_state_name(session->state)); | ||
1345 | } | ||
1346 | static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state, | ||
1347 | NULL); | ||
1348 | |||
1249 | #define iscsi_priv_session_attr_show(field, format) \ | 1349 | #define iscsi_priv_session_attr_show(field, format) \ |
1250 | static ssize_t \ | 1350 | static ssize_t \ |
1251 | show_priv_session_##field(struct class_device *cdev, char *buf) \ | 1351 | show_priv_session_##field(struct class_device *cdev, char *buf) \ |
@@ -1472,6 +1572,7 @@ iscsi_register_transport(struct iscsi_transport *tt) | |||
1472 | SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO); | 1572 | SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO); |
1473 | SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO); | 1573 | SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO); |
1474 | SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo); | 1574 | SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo); |
1575 | SETUP_PRIV_SESSION_RD_ATTR(state); | ||
1475 | 1576 | ||
1476 | BUG_ON(count > ISCSI_SESSION_ATTRS); | 1577 | BUG_ON(count > ISCSI_SESSION_ATTRS); |
1477 | priv->session_attrs[count] = NULL; | 1578 | priv->session_attrs[count] = NULL; |