diff options
author | Mike Christie <michaelc@cs.wisc.edu> | 2007-12-13 13:43:29 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-01-11 19:28:33 -0500 |
commit | 2697478903d0ad8bdbf868b1346ae4b891662bb1 (patch) | |
tree | 8b40d774c0a6f33f9320333a20e08fa920f154fc | |
parent | bc436b278776d22eb10e7e75bf3e5257d14550a9 (diff) |
[SCSI] libiscsi: fix shutdown
We were using the device delete sysfs file to remove each device
then logout. Now in 2.6.21 this will not work because
the sysfs delete file returns immediately and does not wait for
the device removal to complete. This causes a hang if a cache sync
is needed during shutdown. Before .21, that approach had other
problems, so this patch fixes the shutdown code so that we remove the target
and unbind the session before logging out and shut down the session
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | drivers/scsi/libiscsi.c | 4 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_init.c | 4 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_os.c | 7 | ||||
-rw-r--r-- | drivers/scsi/scsi_transport_iscsi.c | 289 | ||||
-rw-r--r-- | include/scsi/iscsi_if.h | 7 | ||||
-rw-r--r-- | include/scsi/iscsi_proto.h | 2 | ||||
-rw-r--r-- | include/scsi/scsi_transport_iscsi.h | 7 |
7 files changed, 176 insertions, 144 deletions
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 441e351b4456..5205ef2c29b2 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c | |||
@@ -1662,7 +1662,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) | |||
1662 | struct iscsi_session *session = iscsi_hostdata(shost->hostdata); | 1662 | struct iscsi_session *session = iscsi_hostdata(shost->hostdata); |
1663 | struct module *owner = cls_session->transport->owner; | 1663 | struct module *owner = cls_session->transport->owner; |
1664 | 1664 | ||
1665 | iscsi_unblock_session(cls_session); | 1665 | iscsi_remove_session(cls_session); |
1666 | scsi_remove_host(shost); | 1666 | scsi_remove_host(shost); |
1667 | 1667 | ||
1668 | iscsi_pool_free(&session->mgmtpool); | 1668 | iscsi_pool_free(&session->mgmtpool); |
@@ -1677,7 +1677,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) | |||
1677 | kfree(session->hwaddress); | 1677 | kfree(session->hwaddress); |
1678 | kfree(session->initiatorname); | 1678 | kfree(session->initiatorname); |
1679 | 1679 | ||
1680 | iscsi_destroy_session(cls_session); | 1680 | iscsi_free_session(cls_session); |
1681 | scsi_host_put(shost); | 1681 | scsi_host_put(shost); |
1682 | module_put(owner); | 1682 | module_put(owner); |
1683 | } | 1683 | } |
diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index d692c713416a..cbe0a17ced5f 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * See LICENSE.qla4xxx for copyright and licensing details. | 5 | * See LICENSE.qla4xxx for copyright and licensing details. |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #include <scsi/iscsi_if.h> | ||
8 | #include "ql4_def.h" | 9 | #include "ql4_def.h" |
9 | #include "ql4_glbl.h" | 10 | #include "ql4_glbl.h" |
10 | #include "ql4_dbg.h" | 11 | #include "ql4_dbg.h" |
@@ -1305,7 +1306,8 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, | |||
1305 | atomic_set(&ddb_entry->relogin_timer, 0); | 1306 | atomic_set(&ddb_entry->relogin_timer, 0); |
1306 | clear_bit(DF_RELOGIN, &ddb_entry->flags); | 1307 | clear_bit(DF_RELOGIN, &ddb_entry->flags); |
1307 | clear_bit(DF_NO_RELOGIN, &ddb_entry->flags); | 1308 | clear_bit(DF_NO_RELOGIN, &ddb_entry->flags); |
1308 | iscsi_if_create_session_done(ddb_entry->conn); | 1309 | iscsi_session_event(ddb_entry->sess, |
1310 | ISCSI_KEVENT_CREATE_SESSION); | ||
1309 | /* | 1311 | /* |
1310 | * Change the lun state to READY in case the lun TIMEOUT before | 1312 | * Change the lun state to READY in case the lun TIMEOUT before |
1311 | * the device came back. | 1313 | * the device came back. |
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 89460d27c689..f55b9f7d9396 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c | |||
@@ -298,8 +298,7 @@ void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry) | |||
298 | return; | 298 | return; |
299 | 299 | ||
300 | if (ddb_entry->conn) { | 300 | if (ddb_entry->conn) { |
301 | iscsi_if_destroy_session_done(ddb_entry->conn); | 301 | atomic_set(&ddb_entry->state, DDB_STATE_DEAD); |
302 | iscsi_destroy_conn(ddb_entry->conn); | ||
303 | iscsi_remove_session(ddb_entry->sess); | 302 | iscsi_remove_session(ddb_entry->sess); |
304 | } | 303 | } |
305 | iscsi_free_session(ddb_entry->sess); | 304 | iscsi_free_session(ddb_entry->sess); |
@@ -309,6 +308,7 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry) | |||
309 | { | 308 | { |
310 | int err; | 309 | int err; |
311 | 310 | ||
311 | ddb_entry->sess->recovery_tmo = ddb_entry->ha->port_down_retry_count; | ||
312 | err = iscsi_add_session(ddb_entry->sess, ddb_entry->fw_ddb_index); | 312 | err = iscsi_add_session(ddb_entry->sess, ddb_entry->fw_ddb_index); |
313 | if (err) { | 313 | if (err) { |
314 | DEBUG2(printk(KERN_ERR "Could not add session.\n")); | 314 | DEBUG2(printk(KERN_ERR "Could not add session.\n")); |
@@ -321,9 +321,6 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry) | |||
321 | DEBUG2(printk(KERN_ERR "Could not add connection.\n")); | 321 | DEBUG2(printk(KERN_ERR "Could not add connection.\n")); |
322 | return -ENOMEM; | 322 | return -ENOMEM; |
323 | } | 323 | } |
324 | |||
325 | ddb_entry->sess->recovery_tmo = ddb_entry->ha->port_down_retry_count; | ||
326 | iscsi_if_create_session_done(ddb_entry->conn); | ||
327 | return 0; | 324 | return 0; |
328 | } | 325 | } |
329 | 326 | ||
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 9cc2cc8e87b3..b82139dc4830 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c | |||
@@ -116,6 +116,8 @@ static struct attribute_group iscsi_transport_group = { | |||
116 | .attrs = iscsi_transport_attrs, | 116 | .attrs = iscsi_transport_attrs, |
117 | }; | 117 | }; |
118 | 118 | ||
119 | |||
120 | |||
119 | static int iscsi_setup_host(struct transport_container *tc, struct device *dev, | 121 | static int iscsi_setup_host(struct transport_container *tc, struct device *dev, |
120 | struct class_device *cdev) | 122 | struct class_device *cdev) |
121 | { | 123 | { |
@@ -125,13 +127,30 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev, | |||
125 | memset(ihost, 0, sizeof(*ihost)); | 127 | memset(ihost, 0, sizeof(*ihost)); |
126 | INIT_LIST_HEAD(&ihost->sessions); | 128 | INIT_LIST_HEAD(&ihost->sessions); |
127 | mutex_init(&ihost->mutex); | 129 | mutex_init(&ihost->mutex); |
130 | |||
131 | snprintf(ihost->unbind_workq_name, KOBJ_NAME_LEN, "iscsi_unbind_%d", | ||
132 | shost->host_no); | ||
133 | ihost->unbind_workq = create_singlethread_workqueue( | ||
134 | ihost->unbind_workq_name); | ||
135 | if (!ihost->unbind_workq) | ||
136 | return -ENOMEM; | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int iscsi_remove_host(struct transport_container *tc, struct device *dev, | ||
141 | struct class_device *cdev) | ||
142 | { | ||
143 | struct Scsi_Host *shost = dev_to_shost(dev); | ||
144 | struct iscsi_host *ihost = shost->shost_data; | ||
145 | |||
146 | destroy_workqueue(ihost->unbind_workq); | ||
128 | return 0; | 147 | return 0; |
129 | } | 148 | } |
130 | 149 | ||
131 | static DECLARE_TRANSPORT_CLASS(iscsi_host_class, | 150 | static DECLARE_TRANSPORT_CLASS(iscsi_host_class, |
132 | "iscsi_host", | 151 | "iscsi_host", |
133 | iscsi_setup_host, | 152 | iscsi_setup_host, |
134 | NULL, | 153 | iscsi_remove_host, |
135 | NULL); | 154 | NULL); |
136 | 155 | ||
137 | static DECLARE_TRANSPORT_CLASS(iscsi_session_class, | 156 | static DECLARE_TRANSPORT_CLASS(iscsi_session_class, |
@@ -266,6 +285,35 @@ void iscsi_block_session(struct iscsi_cls_session *session) | |||
266 | } | 285 | } |
267 | EXPORT_SYMBOL_GPL(iscsi_block_session); | 286 | EXPORT_SYMBOL_GPL(iscsi_block_session); |
268 | 287 | ||
288 | static void __iscsi_unbind_session(struct work_struct *work) | ||
289 | { | ||
290 | struct iscsi_cls_session *session = | ||
291 | container_of(work, struct iscsi_cls_session, | ||
292 | unbind_work); | ||
293 | struct Scsi_Host *shost = iscsi_session_to_shost(session); | ||
294 | struct iscsi_host *ihost = shost->shost_data; | ||
295 | |||
296 | /* Prevent new scans and make sure scanning is not in progress */ | ||
297 | mutex_lock(&ihost->mutex); | ||
298 | if (list_empty(&session->host_list)) { | ||
299 | mutex_unlock(&ihost->mutex); | ||
300 | return; | ||
301 | } | ||
302 | list_del_init(&session->host_list); | ||
303 | mutex_unlock(&ihost->mutex); | ||
304 | |||
305 | scsi_remove_target(&session->dev); | ||
306 | iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION); | ||
307 | } | ||
308 | |||
309 | static int iscsi_unbind_session(struct iscsi_cls_session *session) | ||
310 | { | ||
311 | struct Scsi_Host *shost = iscsi_session_to_shost(session); | ||
312 | struct iscsi_host *ihost = shost->shost_data; | ||
313 | |||
314 | return queue_work(ihost->unbind_workq, &session->unbind_work); | ||
315 | } | ||
316 | |||
269 | struct iscsi_cls_session * | 317 | struct iscsi_cls_session * |
270 | iscsi_alloc_session(struct Scsi_Host *shost, | 318 | iscsi_alloc_session(struct Scsi_Host *shost, |
271 | struct iscsi_transport *transport) | 319 | struct iscsi_transport *transport) |
@@ -282,6 +330,7 @@ iscsi_alloc_session(struct Scsi_Host *shost, | |||
282 | INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); | 330 | INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); |
283 | INIT_LIST_HEAD(&session->host_list); | 331 | INIT_LIST_HEAD(&session->host_list); |
284 | INIT_LIST_HEAD(&session->sess_list); | 332 | INIT_LIST_HEAD(&session->sess_list); |
333 | INIT_WORK(&session->unbind_work, __iscsi_unbind_session); | ||
285 | 334 | ||
286 | /* this is released in the dev's release function */ | 335 | /* this is released in the dev's release function */ |
287 | scsi_host_get(shost); | 336 | scsi_host_get(shost); |
@@ -298,6 +347,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) | |||
298 | { | 347 | { |
299 | struct Scsi_Host *shost = iscsi_session_to_shost(session); | 348 | struct Scsi_Host *shost = iscsi_session_to_shost(session); |
300 | struct iscsi_host *ihost; | 349 | struct iscsi_host *ihost; |
350 | unsigned long flags; | ||
301 | int err; | 351 | int err; |
302 | 352 | ||
303 | ihost = shost->shost_data; | 353 | ihost = shost->shost_data; |
@@ -314,9 +364,15 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) | |||
314 | } | 364 | } |
315 | transport_register_device(&session->dev); | 365 | transport_register_device(&session->dev); |
316 | 366 | ||
367 | spin_lock_irqsave(&sesslock, flags); | ||
368 | list_add(&session->sess_list, &sesslist); | ||
369 | spin_unlock_irqrestore(&sesslock, flags); | ||
370 | |||
317 | mutex_lock(&ihost->mutex); | 371 | mutex_lock(&ihost->mutex); |
318 | list_add(&session->host_list, &ihost->sessions); | 372 | list_add(&session->host_list, &ihost->sessions); |
319 | mutex_unlock(&ihost->mutex); | 373 | mutex_unlock(&ihost->mutex); |
374 | |||
375 | iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION); | ||
320 | return 0; | 376 | return 0; |
321 | 377 | ||
322 | release_host: | 378 | release_host: |
@@ -352,19 +408,58 @@ iscsi_create_session(struct Scsi_Host *shost, | |||
352 | } | 408 | } |
353 | EXPORT_SYMBOL_GPL(iscsi_create_session); | 409 | EXPORT_SYMBOL_GPL(iscsi_create_session); |
354 | 410 | ||
411 | static void iscsi_conn_release(struct device *dev) | ||
412 | { | ||
413 | struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev); | ||
414 | struct device *parent = conn->dev.parent; | ||
415 | |||
416 | kfree(conn); | ||
417 | put_device(parent); | ||
418 | } | ||
419 | |||
420 | static int iscsi_is_conn_dev(const struct device *dev) | ||
421 | { | ||
422 | return dev->release == iscsi_conn_release; | ||
423 | } | ||
424 | |||
425 | static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data) | ||
426 | { | ||
427 | if (!iscsi_is_conn_dev(dev)) | ||
428 | return 0; | ||
429 | return iscsi_destroy_conn(iscsi_dev_to_conn(dev)); | ||
430 | } | ||
431 | |||
355 | void iscsi_remove_session(struct iscsi_cls_session *session) | 432 | void iscsi_remove_session(struct iscsi_cls_session *session) |
356 | { | 433 | { |
357 | struct Scsi_Host *shost = iscsi_session_to_shost(session); | 434 | struct Scsi_Host *shost = iscsi_session_to_shost(session); |
358 | struct iscsi_host *ihost = shost->shost_data; | 435 | struct iscsi_host *ihost = shost->shost_data; |
436 | unsigned long flags; | ||
437 | int err; | ||
438 | |||
439 | spin_lock_irqsave(&sesslock, flags); | ||
440 | list_del(&session->sess_list); | ||
441 | spin_unlock_irqrestore(&sesslock, flags); | ||
359 | 442 | ||
443 | /* | ||
444 | * If we are blocked let commands flow again. The lld or iscsi | ||
445 | * layer should set up the queuecommand to fail commands. | ||
446 | */ | ||
447 | iscsi_unblock_session(session); | ||
448 | iscsi_unbind_session(session); | ||
449 | /* | ||
450 | * If the session dropped while removing devices then we need to make | ||
451 | * sure it is not blocked | ||
452 | */ | ||
360 | if (!cancel_delayed_work(&session->recovery_work)) | 453 | if (!cancel_delayed_work(&session->recovery_work)) |
361 | flush_workqueue(iscsi_eh_timer_workq); | 454 | flush_workqueue(iscsi_eh_timer_workq); |
455 | flush_workqueue(ihost->unbind_workq); | ||
362 | 456 | ||
363 | mutex_lock(&ihost->mutex); | 457 | /* hw iscsi may not have removed all connections from session */ |
364 | list_del(&session->host_list); | 458 | err = device_for_each_child(&session->dev, NULL, |
365 | mutex_unlock(&ihost->mutex); | 459 | iscsi_iter_destroy_conn_fn); |
366 | 460 | if (err) | |
367 | scsi_remove_target(&session->dev); | 461 | dev_printk(KERN_ERR, &session->dev, "iscsi: Could not delete " |
462 | "all connections for session. Error %d.\n", err); | ||
368 | 463 | ||
369 | transport_unregister_device(&session->dev); | 464 | transport_unregister_device(&session->dev); |
370 | device_del(&session->dev); | 465 | device_del(&session->dev); |
@@ -373,9 +468,9 @@ EXPORT_SYMBOL_GPL(iscsi_remove_session); | |||
373 | 468 | ||
374 | void iscsi_free_session(struct iscsi_cls_session *session) | 469 | void iscsi_free_session(struct iscsi_cls_session *session) |
375 | { | 470 | { |
471 | iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION); | ||
376 | put_device(&session->dev); | 472 | put_device(&session->dev); |
377 | } | 473 | } |
378 | |||
379 | EXPORT_SYMBOL_GPL(iscsi_free_session); | 474 | EXPORT_SYMBOL_GPL(iscsi_free_session); |
380 | 475 | ||
381 | /** | 476 | /** |
@@ -393,20 +488,6 @@ int iscsi_destroy_session(struct iscsi_cls_session *session) | |||
393 | } | 488 | } |
394 | EXPORT_SYMBOL_GPL(iscsi_destroy_session); | 489 | EXPORT_SYMBOL_GPL(iscsi_destroy_session); |
395 | 490 | ||
396 | static void iscsi_conn_release(struct device *dev) | ||
397 | { | ||
398 | struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev); | ||
399 | struct device *parent = conn->dev.parent; | ||
400 | |||
401 | kfree(conn); | ||
402 | put_device(parent); | ||
403 | } | ||
404 | |||
405 | static int iscsi_is_conn_dev(const struct device *dev) | ||
406 | { | ||
407 | return dev->release == iscsi_conn_release; | ||
408 | } | ||
409 | |||
410 | /** | 491 | /** |
411 | * iscsi_create_conn - create iscsi class connection | 492 | * iscsi_create_conn - create iscsi class connection |
412 | * @session: iscsi cls session | 493 | * @session: iscsi cls session |
@@ -426,6 +507,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid) | |||
426 | { | 507 | { |
427 | struct iscsi_transport *transport = session->transport; | 508 | struct iscsi_transport *transport = session->transport; |
428 | struct iscsi_cls_conn *conn; | 509 | struct iscsi_cls_conn *conn; |
510 | unsigned long flags; | ||
429 | int err; | 511 | int err; |
430 | 512 | ||
431 | conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL); | 513 | conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL); |
@@ -454,6 +536,11 @@ iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid) | |||
454 | goto release_parent_ref; | 536 | goto release_parent_ref; |
455 | } | 537 | } |
456 | transport_register_device(&conn->dev); | 538 | transport_register_device(&conn->dev); |
539 | |||
540 | spin_lock_irqsave(&connlock, flags); | ||
541 | list_add(&conn->conn_list, &connlist); | ||
542 | conn->active = 1; | ||
543 | spin_unlock_irqrestore(&connlock, flags); | ||
457 | return conn; | 544 | return conn; |
458 | 545 | ||
459 | release_parent_ref: | 546 | release_parent_ref: |
@@ -469,15 +556,21 @@ EXPORT_SYMBOL_GPL(iscsi_create_conn); | |||
469 | * iscsi_destroy_conn - destroy iscsi class connection | 556 | * iscsi_destroy_conn - destroy iscsi class connection |
470 | * @conn: iscsi cls session | 557 | * @conn: iscsi cls session |
471 | * | 558 | * |
472 | * This can be called from an LLD or iscsi_transport. | 559 | * This can be called from a LLD or iscsi_transport. |
473 | */ | 560 | */ |
474 | int iscsi_destroy_conn(struct iscsi_cls_conn *conn) | 561 | int iscsi_destroy_conn(struct iscsi_cls_conn *conn) |
475 | { | 562 | { |
563 | unsigned long flags; | ||
564 | |||
565 | spin_lock_irqsave(&connlock, flags); | ||
566 | conn->active = 0; | ||
567 | list_del(&conn->conn_list); | ||
568 | spin_unlock_irqrestore(&connlock, flags); | ||
569 | |||
476 | transport_unregister_device(&conn->dev); | 570 | transport_unregister_device(&conn->dev); |
477 | device_unregister(&conn->dev); | 571 | device_unregister(&conn->dev); |
478 | return 0; | 572 | return 0; |
479 | } | 573 | } |
480 | |||
481 | EXPORT_SYMBOL_GPL(iscsi_destroy_conn); | 574 | EXPORT_SYMBOL_GPL(iscsi_destroy_conn); |
482 | 575 | ||
483 | /* | 576 | /* |
@@ -687,132 +780,74 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh) | |||
687 | } | 780 | } |
688 | 781 | ||
689 | /** | 782 | /** |
690 | * iscsi_if_destroy_session_done - send session destr. completion event | 783 | * iscsi_session_event - send session destr. completion event |
691 | * @conn: last connection for session | 784 | * @session: iscsi class session |
692 | * | 785 | * @event: type of event |
693 | * This is called by HW iscsi LLDs to notify userpsace that its HW has | ||
694 | * removed a session. | ||
695 | */ | 786 | */ |
696 | int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn) | 787 | int iscsi_session_event(struct iscsi_cls_session *session, |
788 | enum iscsi_uevent_e event) | ||
697 | { | 789 | { |
698 | struct iscsi_internal *priv; | 790 | struct iscsi_internal *priv; |
699 | struct iscsi_cls_session *session; | ||
700 | struct Scsi_Host *shost; | 791 | struct Scsi_Host *shost; |
701 | struct iscsi_uevent *ev; | 792 | struct iscsi_uevent *ev; |
702 | struct sk_buff *skb; | 793 | struct sk_buff *skb; |
703 | struct nlmsghdr *nlh; | 794 | struct nlmsghdr *nlh; |
704 | unsigned long flags; | ||
705 | int rc, len = NLMSG_SPACE(sizeof(*ev)); | 795 | int rc, len = NLMSG_SPACE(sizeof(*ev)); |
706 | 796 | ||
707 | priv = iscsi_if_transport_lookup(conn->transport); | 797 | priv = iscsi_if_transport_lookup(session->transport); |
708 | if (!priv) | 798 | if (!priv) |
709 | return -EINVAL; | 799 | return -EINVAL; |
710 | |||
711 | session = iscsi_dev_to_session(conn->dev.parent); | ||
712 | shost = iscsi_session_to_shost(session); | 800 | shost = iscsi_session_to_shost(session); |
713 | 801 | ||
714 | skb = alloc_skb(len, GFP_KERNEL); | 802 | skb = alloc_skb(len, GFP_KERNEL); |
715 | if (!skb) { | 803 | if (!skb) { |
716 | dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " | 804 | dev_printk(KERN_ERR, &session->dev, "Cannot notify userspace " |
717 | "session creation event\n"); | 805 | "of session event %u\n", event); |
718 | return -ENOMEM; | 806 | return -ENOMEM; |
719 | } | 807 | } |
720 | 808 | ||
721 | nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); | 809 | nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); |
722 | ev = NLMSG_DATA(nlh); | 810 | ev = NLMSG_DATA(nlh); |
723 | ev->transport_handle = iscsi_handle(conn->transport); | 811 | ev->transport_handle = iscsi_handle(session->transport); |
724 | ev->type = ISCSI_KEVENT_DESTROY_SESSION; | ||
725 | ev->r.d_session.host_no = shost->host_no; | ||
726 | ev->r.d_session.sid = session->sid; | ||
727 | |||
728 | /* | ||
729 | * this will occur if the daemon is not up, so we just warn | ||
730 | * the user and when the daemon is restarted it will handle it | ||
731 | */ | ||
732 | rc = iscsi_broadcast_skb(skb, GFP_KERNEL); | ||
733 | if (rc < 0) | ||
734 | dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " | ||
735 | "session destruction event. Check iscsi daemon\n"); | ||
736 | |||
737 | spin_lock_irqsave(&sesslock, flags); | ||
738 | list_del(&session->sess_list); | ||
739 | spin_unlock_irqrestore(&sesslock, flags); | ||
740 | |||
741 | spin_lock_irqsave(&connlock, flags); | ||
742 | conn->active = 0; | ||
743 | list_del(&conn->conn_list); | ||
744 | spin_unlock_irqrestore(&connlock, flags); | ||
745 | |||
746 | return rc; | ||
747 | } | ||
748 | EXPORT_SYMBOL_GPL(iscsi_if_destroy_session_done); | ||
749 | |||
750 | /** | ||
751 | * iscsi_if_create_session_done - send session creation completion event | ||
752 | * @conn: leading connection for session | ||
753 | * | ||
754 | * This is called by HW iscsi LLDs to notify userpsace that its HW has | ||
755 | * created a session or a existing session is back in the logged in state. | ||
756 | */ | ||
757 | int iscsi_if_create_session_done(struct iscsi_cls_conn *conn) | ||
758 | { | ||
759 | struct iscsi_internal *priv; | ||
760 | struct iscsi_cls_session *session; | ||
761 | struct Scsi_Host *shost; | ||
762 | struct iscsi_uevent *ev; | ||
763 | struct sk_buff *skb; | ||
764 | struct nlmsghdr *nlh; | ||
765 | unsigned long flags; | ||
766 | int rc, len = NLMSG_SPACE(sizeof(*ev)); | ||
767 | 812 | ||
768 | priv = iscsi_if_transport_lookup(conn->transport); | 813 | ev->type = event; |
769 | if (!priv) | 814 | switch (event) { |
815 | case ISCSI_KEVENT_DESTROY_SESSION: | ||
816 | ev->r.d_session.host_no = shost->host_no; | ||
817 | ev->r.d_session.sid = session->sid; | ||
818 | break; | ||
819 | case ISCSI_KEVENT_CREATE_SESSION: | ||
820 | ev->r.c_session_ret.host_no = shost->host_no; | ||
821 | ev->r.c_session_ret.sid = session->sid; | ||
822 | break; | ||
823 | case ISCSI_KEVENT_UNBIND_SESSION: | ||
824 | ev->r.unbind_session.host_no = shost->host_no; | ||
825 | ev->r.unbind_session.sid = session->sid; | ||
826 | break; | ||
827 | default: | ||
828 | dev_printk(KERN_ERR, &session->dev, "Invalid event %u.\n", | ||
829 | event); | ||
830 | kfree_skb(skb); | ||
770 | return -EINVAL; | 831 | return -EINVAL; |
771 | |||
772 | session = iscsi_dev_to_session(conn->dev.parent); | ||
773 | shost = iscsi_session_to_shost(session); | ||
774 | |||
775 | skb = alloc_skb(len, GFP_KERNEL); | ||
776 | if (!skb) { | ||
777 | dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " | ||
778 | "session creation event\n"); | ||
779 | return -ENOMEM; | ||
780 | } | 832 | } |
781 | 833 | ||
782 | nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); | ||
783 | ev = NLMSG_DATA(nlh); | ||
784 | ev->transport_handle = iscsi_handle(conn->transport); | ||
785 | ev->type = ISCSI_UEVENT_CREATE_SESSION; | ||
786 | ev->r.c_session_ret.host_no = shost->host_no; | ||
787 | ev->r.c_session_ret.sid = session->sid; | ||
788 | |||
789 | /* | 834 | /* |
790 | * this will occur if the daemon is not up, so we just warn | 835 | * this will occur if the daemon is not up, so we just warn |
791 | * the user and when the daemon is restarted it will handle it | 836 | * the user and when the daemon is restarted it will handle it |
792 | */ | 837 | */ |
793 | rc = iscsi_broadcast_skb(skb, GFP_KERNEL); | 838 | rc = iscsi_broadcast_skb(skb, GFP_KERNEL); |
794 | if (rc < 0) | 839 | if (rc < 0) |
795 | dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " | 840 | dev_printk(KERN_ERR, &session->dev, "Cannot notify userspace " |
796 | "session creation event. Check iscsi daemon\n"); | 841 | "of session event %u. Check iscsi daemon\n", event); |
797 | |||
798 | spin_lock_irqsave(&sesslock, flags); | ||
799 | list_add(&session->sess_list, &sesslist); | ||
800 | spin_unlock_irqrestore(&sesslock, flags); | ||
801 | |||
802 | spin_lock_irqsave(&connlock, flags); | ||
803 | list_add(&conn->conn_list, &connlist); | ||
804 | conn->active = 1; | ||
805 | spin_unlock_irqrestore(&connlock, flags); | ||
806 | return rc; | 842 | return rc; |
807 | } | 843 | } |
808 | EXPORT_SYMBOL_GPL(iscsi_if_create_session_done); | 844 | EXPORT_SYMBOL_GPL(iscsi_session_event); |
809 | 845 | ||
810 | static int | 846 | static int |
811 | iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) | 847 | iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) |
812 | { | 848 | { |
813 | struct iscsi_transport *transport = priv->iscsi_transport; | 849 | struct iscsi_transport *transport = priv->iscsi_transport; |
814 | struct iscsi_cls_session *session; | 850 | struct iscsi_cls_session *session; |
815 | unsigned long flags; | ||
816 | uint32_t hostno; | 851 | uint32_t hostno; |
817 | 852 | ||
818 | session = transport->create_session(transport, &priv->t, | 853 | session = transport->create_session(transport, &priv->t, |
@@ -823,10 +858,6 @@ iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) | |||
823 | if (!session) | 858 | if (!session) |
824 | return -ENOMEM; | 859 | return -ENOMEM; |
825 | 860 | ||
826 | spin_lock_irqsave(&sesslock, flags); | ||
827 | list_add(&session->sess_list, &sesslist); | ||
828 | spin_unlock_irqrestore(&sesslock, flags); | ||
829 | |||
830 | ev->r.c_session_ret.host_no = hostno; | 861 | ev->r.c_session_ret.host_no = hostno; |
831 | ev->r.c_session_ret.sid = session->sid; | 862 | ev->r.c_session_ret.sid = session->sid; |
832 | return 0; | 863 | return 0; |
@@ -837,7 +868,6 @@ iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) | |||
837 | { | 868 | { |
838 | struct iscsi_cls_conn *conn; | 869 | struct iscsi_cls_conn *conn; |
839 | struct iscsi_cls_session *session; | 870 | struct iscsi_cls_session *session; |
840 | unsigned long flags; | ||
841 | 871 | ||
842 | session = iscsi_session_lookup(ev->u.c_conn.sid); | 872 | session = iscsi_session_lookup(ev->u.c_conn.sid); |
843 | if (!session) { | 873 | if (!session) { |
@@ -856,28 +886,17 @@ iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) | |||
856 | 886 | ||
857 | ev->r.c_conn_ret.sid = session->sid; | 887 | ev->r.c_conn_ret.sid = session->sid; |
858 | ev->r.c_conn_ret.cid = conn->cid; | 888 | ev->r.c_conn_ret.cid = conn->cid; |
859 | |||
860 | spin_lock_irqsave(&connlock, flags); | ||
861 | list_add(&conn->conn_list, &connlist); | ||
862 | conn->active = 1; | ||
863 | spin_unlock_irqrestore(&connlock, flags); | ||
864 | |||
865 | return 0; | 889 | return 0; |
866 | } | 890 | } |
867 | 891 | ||
868 | static int | 892 | static int |
869 | iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) | 893 | iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
870 | { | 894 | { |
871 | unsigned long flags; | ||
872 | struct iscsi_cls_conn *conn; | 895 | struct iscsi_cls_conn *conn; |
873 | 896 | ||
874 | conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid); | 897 | conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid); |
875 | if (!conn) | 898 | if (!conn) |
876 | return -EINVAL; | 899 | return -EINVAL; |
877 | spin_lock_irqsave(&connlock, flags); | ||
878 | conn->active = 0; | ||
879 | list_del(&conn->conn_list); | ||
880 | spin_unlock_irqrestore(&connlock, flags); | ||
881 | 900 | ||
882 | if (transport->destroy_conn) | 901 | if (transport->destroy_conn) |
883 | transport->destroy_conn(conn); | 902 | transport->destroy_conn(conn); |
@@ -1004,7 +1023,6 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
1004 | struct iscsi_internal *priv; | 1023 | struct iscsi_internal *priv; |
1005 | struct iscsi_cls_session *session; | 1024 | struct iscsi_cls_session *session; |
1006 | struct iscsi_cls_conn *conn; | 1025 | struct iscsi_cls_conn *conn; |
1007 | unsigned long flags; | ||
1008 | 1026 | ||
1009 | priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle)); | 1027 | priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle)); |
1010 | if (!priv) | 1028 | if (!priv) |
@@ -1022,13 +1040,16 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
1022 | break; | 1040 | break; |
1023 | case ISCSI_UEVENT_DESTROY_SESSION: | 1041 | case ISCSI_UEVENT_DESTROY_SESSION: |
1024 | session = iscsi_session_lookup(ev->u.d_session.sid); | 1042 | session = iscsi_session_lookup(ev->u.d_session.sid); |
1025 | if (session) { | 1043 | if (session) |
1026 | spin_lock_irqsave(&sesslock, flags); | ||
1027 | list_del(&session->sess_list); | ||
1028 | spin_unlock_irqrestore(&sesslock, flags); | ||
1029 | |||
1030 | transport->destroy_session(session); | 1044 | transport->destroy_session(session); |
1031 | } else | 1045 | else |
1046 | err = -EINVAL; | ||
1047 | break; | ||
1048 | case ISCSI_UEVENT_UNBIND_SESSION: | ||
1049 | session = iscsi_session_lookup(ev->u.d_session.sid); | ||
1050 | if (session) | ||
1051 | iscsi_unbind_session(session); | ||
1052 | else | ||
1032 | err = -EINVAL; | 1053 | err = -EINVAL; |
1033 | break; | 1054 | break; |
1034 | case ISCSI_UEVENT_CREATE_CONN: | 1055 | case ISCSI_UEVENT_CREATE_CONN: |
diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index bff0b1f7857b..8a4426df6c3a 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h | |||
@@ -49,12 +49,15 @@ enum iscsi_uevent_e { | |||
49 | 49 | ||
50 | ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15, | 50 | ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15, |
51 | ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16, | 51 | ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16, |
52 | ISCSI_UEVENT_UNBIND_SESSION = UEVENT_BASE + 17, | ||
52 | 53 | ||
53 | /* up events */ | 54 | /* up events */ |
54 | ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, | 55 | ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, |
55 | ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2, | 56 | ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2, |
56 | ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3, | 57 | ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3, |
57 | ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4, | 58 | ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4, |
59 | ISCSI_KEVENT_UNBIND_SESSION = KEVENT_BASE + 5, | ||
60 | ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6, | ||
58 | }; | 61 | }; |
59 | 62 | ||
60 | enum iscsi_tgt_dscvr { | 63 | enum iscsi_tgt_dscvr { |
@@ -156,6 +159,10 @@ struct iscsi_uevent { | |||
156 | uint32_t sid; | 159 | uint32_t sid; |
157 | uint32_t cid; | 160 | uint32_t cid; |
158 | } c_conn_ret; | 161 | } c_conn_ret; |
162 | struct msg_unbind_session { | ||
163 | uint32_t sid; | ||
164 | uint32_t host_no; | ||
165 | } unbind_session; | ||
159 | struct msg_recv_req { | 166 | struct msg_recv_req { |
160 | uint32_t sid; | 167 | uint32_t sid; |
161 | uint32_t cid; | 168 | uint32_t cid; |
diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h index 6947082eee6d..318a909e7ae1 100644 --- a/include/scsi/iscsi_proto.h +++ b/include/scsi/iscsi_proto.h | |||
@@ -21,6 +21,8 @@ | |||
21 | #ifndef ISCSI_PROTO_H | 21 | #ifndef ISCSI_PROTO_H |
22 | #define ISCSI_PROTO_H | 22 | #define ISCSI_PROTO_H |
23 | 23 | ||
24 | #include <linux/types.h> | ||
25 | |||
24 | #define ISCSI_DRAFT20_VERSION 0x00 | 26 | #define ISCSI_DRAFT20_VERSION 0x00 |
25 | 27 | ||
26 | /* default iSCSI listen port for incoming connections */ | 28 | /* default iSCSI listen port for incoming connections */ |
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index b8d97bd20f6e..093b4036f8db 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h | |||
@@ -186,6 +186,7 @@ struct iscsi_cls_session { | |||
186 | /* recovery fields */ | 186 | /* recovery fields */ |
187 | int recovery_tmo; | 187 | int recovery_tmo; |
188 | struct delayed_work recovery_work; | 188 | struct delayed_work recovery_work; |
189 | struct work_struct unbind_work; | ||
189 | 190 | ||
190 | int target_id; | 191 | int target_id; |
191 | 192 | ||
@@ -206,6 +207,8 @@ struct iscsi_cls_session { | |||
206 | struct iscsi_host { | 207 | struct iscsi_host { |
207 | struct list_head sessions; | 208 | struct list_head sessions; |
208 | struct mutex mutex; | 209 | struct mutex mutex; |
210 | struct workqueue_struct *unbind_workq; | ||
211 | char unbind_workq_name[KOBJ_NAME_LEN]; | ||
209 | }; | 212 | }; |
210 | 213 | ||
211 | /* | 214 | /* |
@@ -215,8 +218,8 @@ extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost, | |||
215 | struct iscsi_transport *transport); | 218 | struct iscsi_transport *transport); |
216 | extern int iscsi_add_session(struct iscsi_cls_session *session, | 219 | extern int iscsi_add_session(struct iscsi_cls_session *session, |
217 | unsigned int target_id); | 220 | unsigned int target_id); |
218 | extern int iscsi_if_create_session_done(struct iscsi_cls_conn *conn); | 221 | extern int iscsi_session_event(struct iscsi_cls_session *session, |
219 | extern int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn); | 222 | enum iscsi_uevent_e event); |
220 | extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost, | 223 | extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost, |
221 | struct iscsi_transport *t, | 224 | struct iscsi_transport *t, |
222 | unsigned int target_id); | 225 | unsigned int target_id); |