diff options
-rw-r--r-- | drivers/firewire/fw-sbp2.c | 42 |
1 files changed, 30 insertions, 12 deletions
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 0c87724cec3f..d7940db30a5b 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c | |||
@@ -51,6 +51,7 @@ typedef void (*scsi_done_fn_t) (struct scsi_cmnd *); | |||
51 | static const char sbp2_driver_name[] = "sbp2"; | 51 | static const char sbp2_driver_name[] = "sbp2"; |
52 | 52 | ||
53 | struct sbp2_device { | 53 | struct sbp2_device { |
54 | struct kref kref; | ||
54 | struct fw_unit *unit; | 55 | struct fw_unit *unit; |
55 | struct fw_address_handler address_handler; | 56 | struct fw_address_handler address_handler; |
56 | struct list_head orb_list; | 57 | struct list_head orb_list; |
@@ -513,6 +514,22 @@ static int add_scsi_devices(struct fw_unit *unit); | |||
513 | static void remove_scsi_devices(struct fw_unit *unit); | 514 | static void remove_scsi_devices(struct fw_unit *unit); |
514 | static void sbp2_reconnect(struct work_struct *work); | 515 | static void sbp2_reconnect(struct work_struct *work); |
515 | 516 | ||
517 | static void | ||
518 | release_sbp2_device(struct kref *kref) | ||
519 | { | ||
520 | struct sbp2_device *sd = container_of(kref, struct sbp2_device, kref); | ||
521 | |||
522 | sbp2_send_management_orb(sd->unit, sd->node_id, sd->generation, | ||
523 | SBP2_LOGOUT_REQUEST, sd->login_id, NULL); | ||
524 | |||
525 | remove_scsi_devices(sd->unit); | ||
526 | |||
527 | fw_core_remove_address_handler(&sd->address_handler); | ||
528 | fw_notify("removed sbp2 unit %s\n", sd->unit->device.bus_id); | ||
529 | put_device(&sd->unit->device); | ||
530 | kfree(sd); | ||
531 | } | ||
532 | |||
516 | static void sbp2_login(struct work_struct *work) | 533 | static void sbp2_login(struct work_struct *work) |
517 | { | 534 | { |
518 | struct sbp2_device *sd = | 535 | struct sbp2_device *sd = |
@@ -537,6 +554,7 @@ static void sbp2_login(struct work_struct *work) | |||
537 | fw_error("failed to login to %s\n", | 554 | fw_error("failed to login to %s\n", |
538 | unit->device.bus_id); | 555 | unit->device.bus_id); |
539 | remove_scsi_devices(unit); | 556 | remove_scsi_devices(unit); |
557 | kref_put(&sd->kref, release_sbp2_device); | ||
540 | } | 558 | } |
541 | return; | 559 | return; |
542 | } | 560 | } |
@@ -577,6 +595,7 @@ static void sbp2_login(struct work_struct *work) | |||
577 | * retry login on bus reset. */ | 595 | * retry login on bus reset. */ |
578 | PREPARE_DELAYED_WORK(&sd->work, sbp2_login); | 596 | PREPARE_DELAYED_WORK(&sd->work, sbp2_login); |
579 | } | 597 | } |
598 | kref_put(&sd->kref, release_sbp2_device); | ||
580 | } | 599 | } |
581 | 600 | ||
582 | static int sbp2_probe(struct device *dev) | 601 | static int sbp2_probe(struct device *dev) |
@@ -595,6 +614,7 @@ static int sbp2_probe(struct device *dev) | |||
595 | unit->device.driver_data = sd; | 614 | unit->device.driver_data = sd; |
596 | sd->unit = unit; | 615 | sd->unit = unit; |
597 | INIT_LIST_HEAD(&sd->orb_list); | 616 | INIT_LIST_HEAD(&sd->orb_list); |
617 | kref_init(&sd->kref); | ||
598 | 618 | ||
599 | sd->address_handler.length = 0x100; | 619 | sd->address_handler.length = 0x100; |
600 | sd->address_handler.address_callback = sbp2_status_write; | 620 | sd->address_handler.address_callback = sbp2_status_write; |
@@ -650,10 +670,14 @@ static int sbp2_probe(struct device *dev) | |||
650 | unit->device.bus_id, | 670 | unit->device.bus_id, |
651 | sd->workarounds, firmware_revision, model); | 671 | sd->workarounds, firmware_revision, model); |
652 | 672 | ||
673 | get_device(&unit->device); | ||
674 | |||
653 | /* We schedule work to do the login so we can easily | 675 | /* We schedule work to do the login so we can easily |
654 | * reschedule retries. */ | 676 | * reschedule retries. Always get the ref before scheduling |
677 | * work.*/ | ||
655 | INIT_DELAYED_WORK(&sd->work, sbp2_login); | 678 | INIT_DELAYED_WORK(&sd->work, sbp2_login); |
656 | schedule_delayed_work(&sd->work, 0); | 679 | if (schedule_delayed_work(&sd->work, 0)) |
680 | kref_get(&sd->kref); | ||
657 | 681 | ||
658 | return 0; | 682 | return 0; |
659 | } | 683 | } |
@@ -663,15 +687,7 @@ static int sbp2_remove(struct device *dev) | |||
663 | struct fw_unit *unit = fw_unit(dev); | 687 | struct fw_unit *unit = fw_unit(dev); |
664 | struct sbp2_device *sd = unit->device.driver_data; | 688 | struct sbp2_device *sd = unit->device.driver_data; |
665 | 689 | ||
666 | sbp2_send_management_orb(unit, sd->node_id, sd->generation, | 690 | kref_put(&sd->kref, release_sbp2_device); |
667 | SBP2_LOGOUT_REQUEST, sd->login_id, NULL); | ||
668 | |||
669 | remove_scsi_devices(unit); | ||
670 | |||
671 | fw_core_remove_address_handler(&sd->address_handler); | ||
672 | kfree(sd); | ||
673 | |||
674 | fw_notify("removed sbp2 unit %s\n", dev->bus_id); | ||
675 | 691 | ||
676 | return 0; | 692 | return 0; |
677 | } | 693 | } |
@@ -710,6 +726,7 @@ static void sbp2_reconnect(struct work_struct *work) | |||
710 | unit->device.bus_id, sd->retries); | 726 | unit->device.bus_id, sd->retries); |
711 | sbp2_agent_reset(unit); | 727 | sbp2_agent_reset(unit); |
712 | sbp2_cancel_orbs(unit); | 728 | sbp2_cancel_orbs(unit); |
729 | kref_put(&sd->kref, release_sbp2_device); | ||
713 | } | 730 | } |
714 | 731 | ||
715 | static void sbp2_update(struct fw_unit *unit) | 732 | static void sbp2_update(struct fw_unit *unit) |
@@ -719,7 +736,8 @@ static void sbp2_update(struct fw_unit *unit) | |||
719 | 736 | ||
720 | sd->retries = 0; | 737 | sd->retries = 0; |
721 | fw_device_enable_phys_dma(device); | 738 | fw_device_enable_phys_dma(device); |
722 | schedule_delayed_work(&sd->work, 0); | 739 | if (schedule_delayed_work(&sd->work, 0)) |
740 | kref_get(&sd->kref); | ||
723 | } | 741 | } |
724 | 742 | ||
725 | #define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e | 743 | #define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e |