diff options
-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); |