diff options
Diffstat (limited to 'drivers/firewire/fw-sbp2.c')
-rw-r--r-- | drivers/firewire/fw-sbp2.c | 560 |
1 files changed, 396 insertions, 164 deletions
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 19ece9b6d74..2a999373863 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -28,14 +28,15 @@ * and many others. */ +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/mod_devicetable.h> -#include <linux/device.h> #include <linux/scatterlist.h> -#include <linux/dma-mapping.h> -#include <linux/blkdev.h> #include <linux/string.h> #include <linux/stringify.h> #include <linux/timer.h> @@ -47,9 +48,9 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> -#include "fw-transaction.h" -#include "fw-topology.h" #include "fw-device.h" +#include "fw-topology.h" +#include "fw-transaction.h" /* * So far only bridges from Oxford Semiconductor are known to support @@ -82,6 +83,9 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device " * Avoids access beyond actual disk limits on devices with an off-by-one bug. * Don't use this with devices which don't have this bug. * + * - delay inquiry + * Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry. + * * - override internal blacklist * Instead of adding to the built-in blacklist, use only the workarounds * specified in the module load parameter. @@ -91,6 +95,8 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device " #define SBP2_WORKAROUND_INQUIRY_36 0x2 #define SBP2_WORKAROUND_MODE_SENSE_8 0x4 #define SBP2_WORKAROUND_FIX_CAPACITY 0x8 +#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10 +#define SBP2_INQUIRY_DELAY 12 #define SBP2_WORKAROUND_OVERRIDE 0x100 static int sbp2_param_workarounds; @@ -100,6 +106,7 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0" ", 36 byte inquiry = " __stringify(SBP2_WORKAROUND_INQUIRY_36) ", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8) ", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY) + ", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY) ", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE) ", or a combination)"); @@ -115,7 +122,6 @@ static const char sbp2_driver_name[] = "sbp2"; struct sbp2_logical_unit { struct sbp2_target *tgt; struct list_head link; - struct scsi_device *sdev; struct fw_address_handler address_handler; struct list_head orb_list; @@ -132,6 +138,8 @@ struct sbp2_logical_unit { int generation; int retries; struct delayed_work work; + bool has_sdev; + bool blocked; }; /* @@ -141,16 +149,19 @@ struct sbp2_logical_unit { struct sbp2_target { struct kref kref; struct fw_unit *unit; + const char *bus_id; + struct list_head lu_list; u64 management_agent_address; + u64 guid; int directory_id; int node_id; int address_high; - - unsigned workarounds; - struct list_head lu_list; - + unsigned int workarounds; unsigned int mgt_orb_timeout; + + int dont_block; /* counter for each logical unit */ + int blocked; /* ditto */ }; /* @@ -160,12 +171,11 @@ struct sbp2_target { */ #define SBP2_MIN_LOGIN_ORB_TIMEOUT 5000U /* Timeout in ms */ #define SBP2_MAX_LOGIN_ORB_TIMEOUT 40000U /* Timeout in ms */ -#define SBP2_ORB_TIMEOUT 2000 /* Timeout in ms */ +#define SBP2_ORB_TIMEOUT 2000U /* Timeout in ms */ #define SBP2_ORB_NULL 0x80000000 #define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000 - -#define SBP2_DIRECTION_TO_MEDIA 0x0 -#define SBP2_DIRECTION_FROM_MEDIA 0x1 +#define SBP2_RETRY_LIMIT 0xf /* 15 retries */ +#define SBP2_CYCLE_LIMIT (0xc8 << 12) /* 200 125us cycles */ /* Unit directory keys */ #define SBP2_CSR_UNIT_CHARACTERISTICS 0x3a @@ -213,8 +223,8 @@ struct sbp2_status { }; struct sbp2_pointer { - u32 high; - u32 low; + __be32 high; + __be32 low; }; struct sbp2_orb { @@ -242,8 +252,8 @@ struct sbp2_management_orb { struct { struct sbp2_pointer password; struct sbp2_pointer response; - u32 misc; - u32 length; + __be32 misc; + __be32 length; struct sbp2_pointer status_fifo; } request; __be32 response[4]; @@ -252,20 +262,17 @@ struct sbp2_management_orb { struct sbp2_status status; }; -#define LOGIN_RESPONSE_GET_LOGIN_ID(v) ((v).misc & 0xffff) -#define LOGIN_RESPONSE_GET_LENGTH(v) (((v).misc >> 16) & 0xffff) - struct sbp2_login_response { - u32 misc; + __be32 misc; struct sbp2_pointer command_block_agent; - u32 reconnect_hold; + __be32 reconnect_hold; }; #define COMMAND_ORB_DATA_SIZE(v) ((v)) #define COMMAND_ORB_PAGE_SIZE(v) ((v) << 16) #define COMMAND_ORB_PAGE_TABLE_PRESENT ((1) << 19) #define COMMAND_ORB_MAX_PAYLOAD(v) ((v) << 20) #define COMMAND_ORB_SPEED(v) ((v) << 24) -#define COMMAND_ORB_DIRECTION(v) ((v) << 27) +#define COMMAND_ORB_DIRECTION ((1) << 27) #define COMMAND_ORB_REQUEST_FORMAT(v) ((v) << 29) #define COMMAND_ORB_NOTIFY ((1) << 31) @@ -274,7 +281,7 @@ struct sbp2_command_orb { struct { struct sbp2_pointer next; struct sbp2_pointer data_descriptor; - u32 misc; + __be32 misc; u8 command_block[12]; } request; struct scsi_cmnd *cmd; @@ -297,7 +304,7 @@ struct sbp2_command_orb { static const struct { u32 firmware_revision; u32 model; - unsigned workarounds; + unsigned int workarounds; } sbp2_workarounds_table[] = { /* DViCO Momobay CX-1 with TSB42AA9 bridge */ { .firmware_revision = 0x002800, @@ -305,6 +312,11 @@ static const struct { .workarounds = SBP2_WORKAROUND_INQUIRY_36 | SBP2_WORKAROUND_MODE_SENSE_8, }, + /* DViCO Momobay FX-3A with TSB42AA9A bridge */ { + .firmware_revision = 0x002800, + .model = 0x000000, + .workarounds = SBP2_WORKAROUND_DELAY_INQUIRY, + }, /* Initio bridges, actually only needed for some older ones */ { .firmware_revision = 0x000200, .model = ~0, @@ -315,6 +327,11 @@ static const struct { .model = ~0, .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, }, + /* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ { + .firmware_revision = 0x002600, + .model = ~0, + .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, + }, /* * There are iPods (2nd gen, 3rd gen) with model_id == 0, but @@ -438,8 +455,7 @@ sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu, unsigned long flags; orb->pointer.high = 0; - orb->pointer.low = orb->request_bus; - fw_memcpy_to_be32(&orb->pointer, &orb->pointer, sizeof(orb->pointer)); + orb->pointer.low = cpu_to_be32(orb->request_bus); spin_lock_irqsave(&device->card->lock, flags); list_add_tail(&orb->link, &lu->orb_list); @@ -501,6 +517,9 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, unsigned int timeout; int retval = -ENOMEM; + if (function == SBP2_LOGOUT_REQUEST && fw_device_is_shutdown(device)) + return 0; + orb = kzalloc(sizeof(*orb), GFP_ATOMIC); if (orb == NULL) return -ENOMEM; @@ -512,31 +531,31 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, if (dma_mapping_error(orb->response_bus)) goto fail_mapping_response; - orb->request.response.high = 0; - orb->request.response.low = orb->response_bus; + orb->request.response.high = 0; + orb->request.response.low = cpu_to_be32(orb->response_bus); - orb->request.misc = + orb->request.misc = cpu_to_be32( MANAGEMENT_ORB_NOTIFY | MANAGEMENT_ORB_FUNCTION(function) | - MANAGEMENT_ORB_LUN(lun_or_login_id); - orb->request.length = - MANAGEMENT_ORB_RESPONSE_LENGTH(sizeof(orb->response)); + MANAGEMENT_ORB_LUN(lun_or_login_id)); + orb->request.length = cpu_to_be32( + MANAGEMENT_ORB_RESPONSE_LENGTH(sizeof(orb->response))); - orb->request.status_fifo.high = lu->address_handler.offset >> 32; - orb->request.status_fifo.low = lu->address_handler.offset; + orb->request.status_fifo.high = + cpu_to_be32(lu->address_handler.offset >> 32); + orb->request.status_fifo.low = + cpu_to_be32(lu->address_handler.offset); if (function == SBP2_LOGIN_REQUEST) { /* Ask for 2^2 == 4 seconds reconnect grace period */ - orb->request.misc |= + orb->request.misc |= cpu_to_be32( MANAGEMENT_ORB_RECONNECT(2) | - MANAGEMENT_ORB_EXCLUSIVE(sbp2_param_exclusive_login); + MANAGEMENT_ORB_EXCLUSIVE(sbp2_param_exclusive_login)); timeout = lu->tgt->mgt_orb_timeout; } else { timeout = SBP2_ORB_TIMEOUT; } - fw_memcpy_to_be32(&orb->request, &orb->request, sizeof(orb->request)); - init_completion(&orb->done); orb->base.callback = complete_management_orb; @@ -553,20 +572,20 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, retval = -EIO; if (sbp2_cancel_orbs(lu) == 0) { - fw_error("orb reply timed out, rcode=0x%02x\n", - orb->base.rcode); + fw_error("%s: orb reply timed out, rcode=0x%02x\n", + lu->tgt->bus_id, orb->base.rcode); goto out; } if (orb->base.rcode != RCODE_COMPLETE) { - fw_error("management write failed, rcode 0x%02x\n", - orb->base.rcode); + fw_error("%s: management write failed, rcode 0x%02x\n", + lu->tgt->bus_id, orb->base.rcode); goto out; } if (STATUS_GET_RESPONSE(orb->status) != 0 || STATUS_GET_SBP_STATUS(orb->status) != 0) { - fw_error("error status: %d:%d\n", + fw_error("%s: error status: %d:%d\n", lu->tgt->bus_id, STATUS_GET_RESPONSE(orb->status), STATUS_GET_SBP_STATUS(orb->status)); goto out; @@ -581,8 +600,7 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, sizeof(orb->response), DMA_FROM_DEVICE); fail_mapping_response: if (response) - fw_memcpy_from_be32(response, - orb->response, sizeof(orb->response)); + memcpy(response, orb->response, sizeof(orb->response)); kref_put(&orb->base.kref, free_orb); return retval; @@ -590,29 +608,154 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, static void complete_agent_reset_write(struct fw_card *card, int rcode, - void *payload, size_t length, void *data) + void *payload, size_t length, void *done) { - struct fw_transaction *t = data; + complete(done); +} - kfree(t); +static void sbp2_agent_reset(struct sbp2_logical_unit *lu) +{ + struct fw_device *device = fw_device(lu->tgt->unit->device.parent); + DECLARE_COMPLETION_ONSTACK(done); + struct fw_transaction t; + static u32 z; + + fw_send_request(device->card, &t, TCODE_WRITE_QUADLET_REQUEST, + lu->tgt->node_id, lu->generation, device->max_speed, + lu->command_block_agent_address + SBP2_AGENT_RESET, + &z, sizeof(z), complete_agent_reset_write, &done); + wait_for_completion(&done); } -static int sbp2_agent_reset(struct sbp2_logical_unit *lu) +static void +complete_agent_reset_write_no_wait(struct fw_card *card, int rcode, + void *payload, size_t length, void *data) +{ + kfree(data); +} + +static void sbp2_agent_reset_no_wait(struct sbp2_logical_unit *lu) { struct fw_device *device = fw_device(lu->tgt->unit->device.parent); struct fw_transaction *t; - static u32 zero; + static u32 z; - t = kzalloc(sizeof(*t), GFP_ATOMIC); + t = kmalloc(sizeof(*t), GFP_ATOMIC); if (t == NULL) - return -ENOMEM; + return; fw_send_request(device->card, t, TCODE_WRITE_QUADLET_REQUEST, lu->tgt->node_id, lu->generation, device->max_speed, lu->command_block_agent_address + SBP2_AGENT_RESET, - &zero, sizeof(zero), complete_agent_reset_write, t); + &z, sizeof(z), complete_agent_reset_write_no_wait, t); +} - return 0; +static void sbp2_set_generation(struct sbp2_logical_unit *lu, int generation) +{ + struct fw_card *card = fw_device(lu->tgt->unit->device.parent)->card; + unsigned long flags; + + /* serialize with comparisons of lu->generation and card->generation */ + spin_lock_irqsave(&card->lock, flags); + lu->generation = generation; + spin_unlock_irqrestore(&card->lock, flags); +} + +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) @@ -621,26 +764,31 @@ static void sbp2_release_target(struct kref *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); - list_for_each_entry_safe(lu, next, &tgt->lu_list, link) { - if (lu->sdev) - scsi_remove_device(lu->sdev); + /* prevent deadlocks */ + sbp2_unblock(tgt); - if (!fw_device_is_shutdown(device)) - sbp2_send_management_orb(lu, tgt->node_id, - lu->generation, SBP2_LOGOUT_REQUEST, - lu->login_id, NULL); + 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->unit->device.bus_id); + fw_notify("released %s\n", tgt->bus_id); - put_device(&tgt->unit->device); + fw_unit_put(tgt->unit); scsi_host_put(shost); + fw_device_put(device); } static struct workqueue_struct *sbp2_wq; @@ -660,77 +808,154 @@ static void sbp2_target_put(struct sbp2_target *tgt) kref_put(&tgt->kref, sbp2_release_target); } +static void +complete_set_busy_timeout(struct fw_card *card, int rcode, + void *payload, size_t length, void *done) +{ + complete(done); +} + +/* + * Write retransmit retry values into the BUSY_TIMEOUT register. + * - The single-phase retry protocol is supported by all SBP-2 devices, but the + * default retry_limit value is 0 (i.e. never retry transmission). We write a + * saner value after logging into the device. + * - The dual-phase retry protocol is optional to implement, and if not + * supported, writes to the dual-phase portion of the register will be + * ignored. We try to write the original 1394-1995 default here. + * - In the case of devices that are also SBP-3-compliant, all writes are + * ignored, as the register is read-only, but contains single-phase retry of + * 15, which is what we're trying to set for all SBP-2 device anyway, so this + * write attempt is safe and yields more consistent behavior for all devices. + * + * See section 8.3.2.3.5 of the 1394-1995 spec, section 6.2 of the SBP-2 spec, + * and section 6.4 of the SBP-3 spec for further details. + */ +static void sbp2_set_busy_timeout(struct sbp2_logical_unit *lu) +{ + struct fw_device *device = fw_device(lu->tgt->unit->device.parent); + DECLARE_COMPLETION_ONSTACK(done); + struct fw_transaction t; + static __be32 busy_timeout; + + busy_timeout = cpu_to_be32(SBP2_CYCLE_LIMIT | SBP2_RETRY_LIMIT); + + fw_send_request(device->card, &t, TCODE_WRITE_QUADLET_REQUEST, + lu->tgt->node_id, lu->generation, device->max_speed, + CSR_REGISTER_BASE + CSR_BUSY_TIMEOUT, &busy_timeout, + sizeof(busy_timeout), complete_set_busy_timeout, &done); + wait_for_completion(&done); +} + static void sbp2_reconnect(struct work_struct *work); static void sbp2_login(struct work_struct *work) { struct sbp2_logical_unit *lu = container_of(work, struct sbp2_logical_unit, work.work); - struct Scsi_Host *shost = - container_of((void *)lu->tgt, struct Scsi_Host, hostdata[0]); + struct sbp2_target *tgt = lu->tgt; + struct fw_device *device = fw_device(tgt->unit->device.parent); + struct Scsi_Host *shost; struct scsi_device *sdev; - struct scsi_lun eight_bytes_lun; - struct fw_unit *unit = lu->tgt->unit; - struct fw_device *device = fw_device(unit->device.parent); struct sbp2_login_response response; int generation, node_id, local_node_id; + if (fw_device_is_shutdown(device)) + goto out; + generation = device->generation; smp_rmb(); /* node_id must not be older than generation */ node_id = device->node_id; local_node_id = device->card->node_id; + /* If this is a re-login attempt, log out, or we might be rejected. */ + if (lu->has_sdev) + sbp2_send_management_orb(lu, device->node_id, generation, + SBP2_LOGOUT_REQUEST, lu->login_id, NULL); + if (sbp2_send_management_orb(lu, node_id, generation, SBP2_LOGIN_REQUEST, lu->lun, &response) < 0) { - if (lu->retries++ < 5) + 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); + } else { + fw_error("%s: failed to login to LUN %04x\n", + tgt->bus_id, lu->lun); + /* Let any waiting I/O fail from now on. */ + sbp2_unblock(lu->tgt); + } goto out; } - lu->generation = generation; - lu->tgt->node_id = node_id; - lu->tgt->address_high = local_node_id << 16; + tgt->node_id = node_id; + tgt->address_high = local_node_id << 16; + sbp2_set_generation(lu, generation); - /* Get command block agent offset and login id. */ lu->command_block_agent_address = - ((u64) (response.command_block_agent.high & 0xffff) << 32) | - response.command_block_agent.low; - lu->login_id = LOGIN_RESPONSE_GET_LOGIN_ID(response); + ((u64)(be32_to_cpu(response.command_block_agent.high) & 0xffff) + << 32) | be32_to_cpu(response.command_block_agent.low); + lu->login_id = be32_to_cpu(response.misc) & 0xffff; - fw_notify("logged in to %s LUN %04x (%d retries)\n", - unit->device.bus_id, lu->lun, lu->retries); + fw_notify("%s: logged in to LUN %04x (%d retries)\n", + tgt->bus_id, lu->lun, lu->retries); -#if 0 - /* FIXME: The linux1394 sbp2 does this last step. */ - sbp2_set_busy_timeout(scsi_id); -#endif + /* set appropriate retry limit(s) in BUSY_TIMEOUT register */ + sbp2_set_busy_timeout(lu); PREPARE_DELAYED_WORK(&lu->work, sbp2_reconnect); sbp2_agent_reset(lu); - memset(&eight_bytes_lun, 0, sizeof(eight_bytes_lun)); - eight_bytes_lun.scsi_lun[0] = (lu->lun >> 8) & 0xff; - eight_bytes_lun.scsi_lun[1] = lu->lun & 0xff; + /* This was a re-login. */ + if (lu->has_sdev) { + sbp2_cancel_orbs(lu); + sbp2_conditionally_unblock(lu); + goto out; + } - sdev = __scsi_add_device(shost, 0, 0, - scsilun_to_int(&eight_bytes_lun), lu); - if (IS_ERR(sdev)) { - sbp2_send_management_orb(lu, node_id, generation, - SBP2_LOGOUT_REQUEST, lu->login_id, NULL); - /* - * Set this back to sbp2_login so we fall back and - * retry login on bus reset. - */ - PREPARE_DELAYED_WORK(&lu->work, sbp2_login); - } else { - lu->sdev = sdev; + if (lu->tgt->workarounds & SBP2_WORKAROUND_DELAY_INQUIRY) + ssleep(SBP2_INQUIRY_DELAY); + + shost = container_of((void *)tgt, struct Scsi_Host, hostdata[0]); + sdev = __scsi_add_device(shost, 0, 0, sbp2_lun2int(lu->lun), lu); + /* + * FIXME: We are unable to perform reconnects while in sbp2_login(). + * Therefore __scsi_add_device() will get into trouble if a bus reset + * happens in parallel. It will either fail or leave us with an + * unusable sdev. As a workaround we check for this and retry the + * whole login and SCSI probing. + */ + + /* Reported error during __scsi_add_device() */ + if (IS_ERR(sdev)) + goto out_logout_login; + + /* Unreported error during __scsi_add_device() */ + smp_rmb(); /* get current card generation */ + if (generation != device->card->generation) { + scsi_remove_device(sdev); scsi_device_put(sdev); + goto out_logout_login; } + + /* No error during __scsi_add_device() */ + lu->has_sdev = true; + scsi_device_put(sdev); + sbp2_allow_block(lu); + goto out; + + out_logout_login: + smp_rmb(); /* generation may have changed */ + generation = device->generation; + smp_rmb(); /* node_id must not be older than generation */ + + sbp2_send_management_orb(lu, device->node_id, generation, + SBP2_LOGOUT_REQUEST, lu->login_id, NULL); + /* + * If a bus reset happened, sbp2_update will have requeued + * lu->work already. Reset the work from reconnect to login. + */ + PREPARE_DELAYED_WORK(&lu->work, sbp2_login); out: - sbp2_target_put(lu->tgt); + sbp2_target_put(tgt); } static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) @@ -751,10 +976,12 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) return -ENOMEM; } - lu->tgt = tgt; - lu->sdev = NULL; - lu->lun = lun_entry & 0xffff; - lu->retries = 0; + lu->tgt = tgt; + lu->lun = lun_entry & 0xffff; + lu->retries = 0; + lu->has_sdev = false; + lu->blocked = false; + ++tgt->dont_block; INIT_LIST_HEAD(&lu->orb_list); INIT_DELAYED_WORK(&lu->work, sbp2_login); @@ -813,7 +1040,7 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, u32 *directory, if (timeout > tgt->mgt_orb_timeout) fw_notify("%s: config rom contains %ds " "management ORB timeout, limiting " - "to %ds\n", tgt->unit->device.bus_id, + "to %ds\n", tgt->bus_id, timeout / 1000, tgt->mgt_orb_timeout / 1000); break; @@ -836,12 +1063,12 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model, u32 firmware_revision) { int i; - unsigned w = sbp2_param_workarounds; + unsigned int w = sbp2_param_workarounds; if (w) fw_notify("Please notify linux1394-devel@lists.sourceforge.net " "if you need the workarounds parameter for %s\n", - tgt->unit->device.bus_id); + tgt->bus_id); if (w & SBP2_WORKAROUND_OVERRIDE) goto out; @@ -863,8 +1090,7 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model, if (w) fw_notify("Workarounds for %s: 0x%x " "(firmware_revision 0x%06x, model_id 0x%06x)\n", - tgt->unit->device.bus_id, - w, firmware_revision, model); + tgt->bus_id, w, firmware_revision, model); tgt->workarounds = w; } @@ -888,6 +1114,8 @@ static int sbp2_probe(struct device *dev) tgt->unit = unit; kref_init(&tgt->kref); INIT_LIST_HEAD(&tgt->lu_list); + tgt->bus_id = unit->device.bus_id; + tgt->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; if (fw_device_enable_phys_dma(device) < 0) goto fail_shost_put; @@ -895,6 +1123,9 @@ static int sbp2_probe(struct device *dev) if (scsi_add_host(shost, &unit->device) < 0) goto fail_shost_put; + fw_device_get(device); + fw_unit_get(unit); + /* Initialize to values that won't match anything in our table. */ firmware_revision = 0xff000000; model = 0xff000000; @@ -909,8 +1140,6 @@ static int sbp2_probe(struct device *dev) sbp2_init_workarounds(tgt, model, firmware_revision); - get_device(&unit->device); - /* Do the login in a workqueue so we can easily reschedule retries. */ list_for_each_entry(lu, &tgt->lu_list, link) sbp2_queue_work(lu, 0); @@ -938,10 +1167,13 @@ static void sbp2_reconnect(struct work_struct *work) { struct sbp2_logical_unit *lu = container_of(work, struct sbp2_logical_unit, work.work); - struct fw_unit *unit = lu->tgt->unit; - struct fw_device *device = fw_device(unit->device.parent); + struct sbp2_target *tgt = lu->tgt; + struct fw_device *device = fw_device(tgt->unit->device.parent); int generation, node_id, local_node_id; + if (fw_device_is_shutdown(device)) + goto out; + generation = device->generation; smp_rmb(); /* node_id must not be older than generation */ node_id = device->node_id; @@ -950,10 +1182,17 @@ static void sbp2_reconnect(struct work_struct *work) if (sbp2_send_management_orb(lu, node_id, generation, SBP2_RECONNECT_REQUEST, lu->login_id, NULL) < 0) { - if (lu->retries++ >= 5) { - fw_error("failed to reconnect to %s\n", - unit->device.bus_id); - /* Fall back and try to log in again. */ + /* + * If reconnect was impossible even though we are in the + * current generation, fall back and try to log in again. + * + * We could check for "Function rejected" status, but + * looking at the bus generation as simpler and more general. + */ + smp_rmb(); /* get current card generation */ + if (generation == device->card->generation || + lu->retries++ >= 5) { + fw_error("%s: failed to reconnect\n", tgt->bus_id); lu->retries = 0; PREPARE_DELAYED_WORK(&lu->work, sbp2_login); } @@ -961,17 +1200,18 @@ static void sbp2_reconnect(struct work_struct *work) goto out; } - lu->generation = generation; - lu->tgt->node_id = node_id; - lu->tgt->address_high = local_node_id << 16; + tgt->node_id = node_id; + tgt->address_high = local_node_id << 16; + sbp2_set_generation(lu, generation); - fw_notify("reconnected to %s LUN %04x (%d retries)\n", - unit->device.bus_id, lu->lun, lu->retries); + fw_notify("%s: reconnected to LUN %04x (%d retries)\n", + tgt->bus_id, lu->lun, lu->retries); sbp2_agent_reset(lu); sbp2_cancel_orbs(lu); + sbp2_conditionally_unblock(lu); out: - sbp2_target_put(lu->tgt); + sbp2_target_put(tgt); } static void sbp2_update(struct fw_unit *unit) @@ -986,6 +1226,7 @@ static void sbp2_update(struct fw_unit *unit) * Iteration over tgt->lu_list is therefore safe here. */ list_for_each_entry(lu, &tgt->lu_list, link) { + sbp2_conditionally_block(lu); lu->retries = 0; sbp2_queue_work(lu, 0); } @@ -1063,7 +1304,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) if (status != NULL) { if (STATUS_GET_DEAD(*status)) - sbp2_agent_reset(orb->lu); + sbp2_agent_reset_no_wait(orb->lu); switch (STATUS_GET_RESPONSE(*status)) { case SBP2_STATUS_REQUEST_COMPLETE: @@ -1089,6 +1330,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) * or when sending the write (less likely). */ result = DID_BUS_BUSY << 16; + sbp2_conditionally_block(orb->lu); } dma_unmap_single(device->card->device, orb->base.request_bus, @@ -1129,9 +1371,12 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, * tables. */ if (count == 1 && sg_dma_len(sg) < SBP2_MAX_SG_ELEMENT_LENGTH) { - orb->request.data_descriptor.high = lu->tgt->address_high; - orb->request.data_descriptor.low = sg_dma_address(sg); - orb->request.misc |= COMMAND_ORB_DATA_SIZE(sg_dma_len(sg)); + orb->request.data_descriptor.high = + cpu_to_be32(lu->tgt->address_high); + orb->request.data_descriptor.low = + cpu_to_be32(sg_dma_address(sg)); + orb->request.misc |= + cpu_to_be32(COMMAND_ORB_DATA_SIZE(sg_dma_len(sg))); return 0; } @@ -1152,16 +1397,14 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, goto fail_page_table; } l = min(sg_len, SBP2_MAX_SG_ELEMENT_LENGTH); - orb->page_table[j].low = sg_addr; - orb->page_table[j].high = (l << 16); + orb->page_table[j].low = cpu_to_be32(sg_addr); + orb->page_table[j].high = cpu_to_be32(l << 16); sg_addr += l; sg_len -= l; j++; } } - fw_memcpy_to_be32(orb->page_table, orb->page_table, - sizeof(orb->page_table[0]) * j); orb->page_table_bus = dma_map_single(device->card->device, orb->page_table, sizeof(orb->page_table), DMA_TO_DEVICE); @@ -1175,11 +1418,10 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, * initiator (i.e. us), but data_descriptor can refer to data * on other nodes so we need to put our ID in descriptor.high. */ - orb->request.data_descriptor.high = lu->tgt->address_high; - orb->request.data_descriptor.low = orb->page_table_bus; - orb->request.misc |= - COMMAND_ORB_PAGE_TABLE_PRESENT | - COMMAND_ORB_DATA_SIZE(j); + orb->request.data_descriptor.high = cpu_to_be32(lu->tgt->address_high); + orb->request.data_descriptor.low = cpu_to_be32(orb->page_table_bus); + orb->request.misc |= cpu_to_be32(COMMAND_ORB_PAGE_TABLE_PRESENT | + COMMAND_ORB_DATA_SIZE(j)); return 0; @@ -1197,7 +1439,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) struct sbp2_logical_unit *lu = cmd->device->hostdata; struct fw_device *device = fw_device(lu->tgt->unit->device.parent); struct sbp2_command_orb *orb; - unsigned max_payload; + unsigned int max_payload; int retval = SCSI_MLQUEUE_HOST_BUSY; /* @@ -1225,8 +1467,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) orb->done = done; orb->cmd = cmd; - orb->request.next.high = SBP2_ORB_NULL; - orb->request.next.low = 0x0; + orb->request.next.high = cpu_to_be32(SBP2_ORB_NULL); /* * At speed 100 we can do 512 bytes per packet, at speed 200, * 1024 bytes per packet etc. The SBP-2 max_payload field @@ -1235,25 +1476,17 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) */ max_payload = min(device->max_speed + 7, device->card->max_receive - 1); - orb->request.misc = + orb->request.misc = cpu_to_be32( COMMAND_ORB_MAX_PAYLOAD(max_payload) | COMMAND_ORB_SPEED(device->max_speed) | - COMMAND_ORB_NOTIFY; + COMMAND_ORB_NOTIFY); if (cmd->sc_data_direction == DMA_FROM_DEVICE) - orb->request.misc |= - COMMAND_ORB_DIRECTION(SBP2_DIRECTION_FROM_MEDIA); - else if (cmd->sc_data_direction == DMA_TO_DEVICE) - orb->request.misc |= - COMMAND_ORB_DIRECTION(SBP2_DIRECTION_TO_MEDIA); + orb->request.misc |= cpu_to_be32(COMMAND_ORB_DIRECTION); if (scsi_sg_count(cmd) && sbp2_map_scatterlist(orb, device, lu) < 0) goto out; - fw_memcpy_to_be32(&orb->request, &orb->request, sizeof(orb->request)); - - memset(orb->request.command_block, - 0, sizeof(orb->request.command_block)); memcpy(orb->request.command_block, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd)); orb->base.callback = complete_command_orb; @@ -1275,13 +1508,14 @@ static int sbp2_scsi_slave_alloc(struct scsi_device *sdev) { struct sbp2_logical_unit *lu = sdev->hostdata; + /* (Re-)Adding logical units via the SCSI stack is not supported. */ + if (!lu) + return -ENOSYS; + 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); + /* SBP-2 requires quadlet alignment of the data buffers. */ + blk_queue_update_dma_alignment(sdev->request_queue, 4 - 1); if (lu->tgt->workarounds & SBP2_WORKAROUND_INQUIRY_36) sdev->inquiry_len = 36; @@ -1319,7 +1553,7 @@ static int sbp2_scsi_abort(struct scsi_cmnd *cmd) { struct sbp2_logical_unit *lu = cmd->device->hostdata; - fw_notify("sbp2_scsi_abort\n"); + fw_notify("%s: sbp2_scsi_abort\n", lu->tgt->bus_id); sbp2_agent_reset(lu); sbp2_cancel_orbs(lu); @@ -1339,16 +1573,14 @@ sbp2_sysfs_ieee1394_id_show(struct device *dev, struct device_attribute *attr, { struct scsi_device *sdev = to_scsi_device(dev); struct sbp2_logical_unit *lu; - struct fw_device *device; if (!sdev) return 0; lu = sdev->hostdata; - device = fw_device(lu->tgt->unit->device.parent); - return sprintf(buf, "%08x%08x:%06x:%04x\n", - device->config_rom[3], device->config_rom[4], + return sprintf(buf, "%016llx:%06x:%04x\n", + (unsigned long long)lu->tgt->guid, lu->tgt->directory_id, lu->lun); } |