};
#define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000
-#define SBP2_MAX_SECTORS 255 /* Max sectors supported */
#define SBP2_ORB_TIMEOUT 2000 /* Timeout in ms */
-
#define SBP2_ORB_NULL 0x80000000
#define SBP2_DIRECTION_TO_MEDIA 0x0
orb->request.status_fifo.low = lu->address_handler.offset;
if (function == SBP2_LOGIN_REQUEST) {
+ /* Ask for 2^2 == 4 seconds reconnect grace period */
orb->request.misc |=
- MANAGEMENT_ORB_EXCLUSIVE(sbp2_param_exclusive_login) |
- MANAGEMENT_ORB_RECONNECT(0);
+ MANAGEMENT_ORB_RECONNECT(2) |
+ MANAGEMENT_ORB_EXCLUSIVE(sbp2_param_exclusive_login);
}
fw_memcpy_to_be32(&orb->request, &orb->request, sizeof(orb->request));
struct sbp2_logical_unit *lu, *next;
struct Scsi_Host *shost =
container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
+ struct fw_device *device = fw_device(tgt->unit->device.parent);
list_for_each_entry_safe(lu, next, &tgt->lu_list, link) {
if (lu->sdev)
scsi_remove_device(lu->sdev);
- sbp2_send_management_orb(lu, tgt->node_id, lu->generation,
- SBP2_LOGOUT_REQUEST, lu->login_id, NULL);
+ if (!fw_device_is_shutdown(device))
+ 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);
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);
+}
+
+static void sbp2_target_put(struct sbp2_target *tgt)
+{
+ kref_put(&tgt->kref, sbp2_release_target);
+}
+
static void sbp2_reconnect(struct work_struct *work);
static void sbp2_login(struct work_struct *work)
if (sbp2_send_management_orb(lu, node_id, generation,
SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) {
- if (lu->retries++ < 5) {
- if (queue_delayed_work(sbp2_wq, &lu->work,
- DIV_ROUND_UP(HZ, 5)))
- kref_get(&lu->tgt->kref);
- } else {
+ if (lu->retries++ < 5)
+ sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
+ else
fw_error("failed to login to %s LUN %04x\n",
unit->device.bus_id, lu->lun);
- }
- kref_put(&lu->tgt->kref, sbp2_release_target);
- return;
+ goto out;
}
lu->generation = generation;
lu->sdev = sdev;
scsi_device_put(sdev);
}
- kref_put(&lu->tgt->kref, sbp2_release_target);
+ out:
+ sbp2_target_put(lu->tgt);
}
static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry)
get_device(&unit->device);
- /*
- * We schedule work to do the login so we can easily
- * reschedule retries. Always get the ref before scheduling
- * work.
- */
+ /* Do the login in a workqueue so we can easily reschedule retries. */
list_for_each_entry(lu, &tgt->lu_list, link)
- if (queue_delayed_work(sbp2_wq, &lu->work, 0))
- kref_get(&tgt->kref);
+ sbp2_queue_work(lu, 0);
return 0;
fail_tgt_put:
- kref_put(&tgt->kref, sbp2_release_target);
+ sbp2_target_put(tgt);
return -ENOMEM;
fail_shost_put:
struct fw_unit *unit = fw_unit(dev);
struct sbp2_target *tgt = unit->device.driver_data;
- kref_put(&tgt->kref, sbp2_release_target);
+ sbp2_target_put(tgt);
return 0;
}
lu->retries = 0;
PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
}
- if (queue_delayed_work(sbp2_wq, &lu->work, DIV_ROUND_UP(HZ, 5)))
- kref_get(&lu->tgt->kref);
- kref_put(&lu->tgt->kref, sbp2_release_target);
- return;
+ sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
+ goto out;
}
lu->generation = generation;
sbp2_agent_reset(lu);
sbp2_cancel_orbs(lu);
-
- kref_put(&lu->tgt->kref, sbp2_release_target);
+ out:
+ sbp2_target_put(lu->tgt);
}
static void sbp2_update(struct fw_unit *unit)
*/
list_for_each_entry(lu, &tgt->lu_list, link) {
lu->retries = 0;
- if (queue_delayed_work(sbp2_wq, &lu->work, 0))
- kref_get(&tgt->kref);
+ sbp2_queue_work(lu, 0);
}
}
* elements larger than 65535 bytes, some IOMMUs may merge sg elements
* during DMA mapping, and Linux currently doesn't prevent this.
*/
- for (i = 0, j = 0; i < count; i++) {
- sg_len = sg_dma_len(sg + i);
- sg_addr = sg_dma_address(sg + i);
+ for (i = 0, j = 0; i < count; i++, sg = sg_next(sg)) {
+ sg_len = sg_dma_len(sg);
+ sg_addr = sg_dma_address(sg);
while (sg_len) {
/* FIXME: This won't get us out of the pinch. */
if (unlikely(j >= ARRAY_SIZE(orb->page_table))) {
sdev->allow_restart = 1;
+ /*
+ * Update the dma alignment (minimum alignment requirements for
+ * start and end of DMA transfers) to be a sector
+ */
+ blk_queue_update_dma_alignment(sdev->request_queue, 511);
+
if (lu->tgt->workarounds & SBP2_WORKAROUND_INQUIRY_36)
sdev->inquiry_len = 36;