+static inline void sbp2_allow_block(struct sbp2_logical_unit *lu)
+{
+ /*
+ * We may access dont_block without taking card->lock here:
+ * All callers of sbp2_allow_block() and all callers of sbp2_unblock()
+ * are currently serialized against each other.
+ * And a wrong result in sbp2_conditionally_block()'s access of
+ * dont_block is rather harmless, it simply misses its first chance.
+ */
+ --lu->tgt->dont_block;
+}
+
+/*
+ * Blocks lu->tgt if all of the following conditions are met:
+ * - Login, INQUIRY, and high-level SCSI setup of all of the target's
+ * logical units have been finished (indicated by dont_block == 0).
+ * - lu->generation is stale.
+ *
+ * Note, scsi_block_requests() must be called while holding card->lock,
+ * otherwise it might foil sbp2_[conditionally_]unblock()'s attempt to
+ * unblock the target.
+ */
+static void sbp2_conditionally_block(struct sbp2_logical_unit *lu)
+{
+ struct sbp2_target *tgt = lu->tgt;
+ struct fw_card *card = fw_device(tgt->unit->device.parent)->card;
+ struct Scsi_Host *shost =
+ container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ if (!tgt->dont_block && !lu->blocked &&
+ lu->generation != card->generation) {
+ lu->blocked = true;
+ if (++tgt->blocked == 1)
+ scsi_block_requests(shost);
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/*
+ * Unblocks lu->tgt as soon as all its logical units can be unblocked.
+ * Note, it is harmless to run scsi_unblock_requests() outside the
+ * card->lock protected section. On the other hand, running it inside
+ * the section might clash with shost->host_lock.
+ */
+static void sbp2_conditionally_unblock(struct sbp2_logical_unit *lu)
+{
+ struct sbp2_target *tgt = lu->tgt;
+ struct fw_card *card = fw_device(tgt->unit->device.parent)->card;
+ struct Scsi_Host *shost =
+ container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
+ unsigned long flags;
+ bool unblock = false;
+
+ spin_lock_irqsave(&card->lock, flags);
+ if (lu->blocked && lu->generation == card->generation) {
+ lu->blocked = false;
+ unblock = --tgt->blocked == 0;
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (unblock)
+ scsi_unblock_requests(shost);
+}
+
+/*
+ * Prevents future blocking of tgt and unblocks it.
+ * Note, it is harmless to run scsi_unblock_requests() outside the
+ * card->lock protected section. On the other hand, running it inside
+ * the section might clash with shost->host_lock.
+ */
+static void sbp2_unblock(struct sbp2_target *tgt)
+{
+ struct fw_card *card = fw_device(tgt->unit->device.parent)->card;
+ struct Scsi_Host *shost =
+ container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ ++tgt->dont_block;
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ scsi_unblock_requests(shost);
+}
+
+static int sbp2_lun2int(u16 lun)
+{
+ struct scsi_lun eight_bytes_lun;
+
+ memset(&eight_bytes_lun, 0, sizeof(eight_bytes_lun));
+ eight_bytes_lun.scsi_lun[0] = (lun >> 8) & 0xff;
+ eight_bytes_lun.scsi_lun[1] = lun & 0xff;
+
+ return scsilun_to_int(&eight_bytes_lun);
+}
+
+static void sbp2_release_target(struct kref *kref)
+{
+ struct sbp2_target *tgt = container_of(kref, struct sbp2_target, kref);
+ struct sbp2_logical_unit *lu, *next;
+ struct Scsi_Host *shost =
+ container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
+ struct scsi_device *sdev;
+ struct fw_device *device = fw_device(tgt->unit->device.parent);
+
+ /* prevent deadlocks */
+ sbp2_unblock(tgt);
+
+ list_for_each_entry_safe(lu, next, &tgt->lu_list, link) {
+ sdev = scsi_device_lookup(shost, 0, 0, sbp2_lun2int(lu->lun));
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ }
+ sbp2_send_management_orb(lu, tgt->node_id, lu->generation,
+ SBP2_LOGOUT_REQUEST, lu->login_id, NULL);
+
+ fw_core_remove_address_handler(&lu->address_handler);
+ list_del(&lu->link);
+ kfree(lu);
+ }
+ scsi_remove_host(shost);
+ fw_notify("released %s\n", tgt->bus_id);
+
+ fw_unit_put(tgt->unit);
+ scsi_host_put(shost);
+ fw_device_put(device);
+}
+
+static struct workqueue_struct *sbp2_wq;
+
+/*
+ * Always get the target's kref when scheduling work on one its units.
+ * Each workqueue job is responsible to call sbp2_target_put() upon return.
+ */
+static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay)
+{
+ if (queue_delayed_work(sbp2_wq, &lu->work, delay))
+ kref_get(&lu->tgt->kref);
+}