Commit e57d2011 authored by Kristian Høgsberg's avatar Kristian Høgsberg Committed by Stefan Richter
Browse files

firewire: Add ref-counting for sbp2 orbs (fix command abortion)

This handles the case where we get the status write before getting the
complete_transaction callback ("status write for unknown orb").  In
this case, we just assume that the initial orb pointer transaction
succeeded and finish the orb.  To prevent the transaction callback
from touching freed memory, we ref-count the orb structures.
Signed-off-by: default avatarKristian Høgsberg <>
Signed-off-by: default avatarStefan Richter <>
parent 8a2d9ed3
......@@ -159,6 +159,7 @@ struct sbp2_pointer {
struct sbp2_orb {
struct fw_transaction t;
struct kref kref;
dma_addr_t request_bus;
int rcode;
struct sbp2_pointer pointer;
......@@ -279,6 +280,14 @@ static const struct {
static void
free_orb(struct kref *kref)
struct sbp2_orb *orb = container_of(kref, struct sbp2_orb, kref);
static void
sbp2_status_write(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source,
......@@ -312,8 +321,8 @@ sbp2_status_write(struct fw_card *card, struct fw_request *request,
spin_lock_irqsave(&card->lock, flags);
list_for_each_entry(orb, &sd->orb_list, link) {
if (STATUS_GET_ORB_HIGH(status) == 0 &&
STATUS_GET_ORB_LOW(status) == orb->request_bus &&
orb->rcode == RCODE_COMPLETE) {
STATUS_GET_ORB_LOW(status) == orb->request_bus) {
orb->rcode = RCODE_COMPLETE;
......@@ -325,6 +334,8 @@ sbp2_status_write(struct fw_card *card, struct fw_request *request,
fw_error("status write for unknown orb\n");
kref_put(&orb->kref, free_orb);
fw_send_response(card, request, RCODE_COMPLETE);
......@@ -335,13 +346,27 @@ complete_transaction(struct fw_card *card, int rcode,
struct sbp2_orb *orb = data;
unsigned long flags;
orb->rcode = rcode;
if (rcode != RCODE_COMPLETE) {
spin_lock_irqsave(&card->lock, flags);
* This is a little tricky. We can get the status write for
* the orb before we get this callback. The status write
* handler above will assume the orb pointer transaction was
* successful and set the rcode to RCODE_COMPLETE for the orb.
* So this callback only sets the rcode if it hasn't already
* been set and only does the cleanup if the transaction
* failed and we didn't already get a status write.
spin_lock_irqsave(&card->lock, flags);
if (orb->rcode == -1)
orb->rcode = rcode;
if (orb->rcode != RCODE_COMPLETE) {
spin_unlock_irqrestore(&card->lock, flags);
orb->callback(orb, NULL);
spin_unlock_irqrestore(&card->lock, flags);
kref_put(&orb->kref, free_orb);
static void
......@@ -360,6 +385,10 @@ sbp2_send_orb(struct sbp2_orb *orb, struct fw_unit *unit,
list_add_tail(&orb->link, &sd->orb_list);
spin_unlock_irqrestore(&device->card->lock, flags);
/* Take a ref for the orb list and for the transaction callback. */
fw_send_request(device->card, &orb->t, TCODE_WRITE_BLOCK_REQUEST,
node_id, generation, device->max_speed, offset,
&orb->pointer, sizeof(orb->pointer),
......@@ -416,6 +445,7 @@ sbp2_send_management_orb(struct fw_unit *unit, int node_id, int generation,
if (orb == NULL)
return -ENOMEM;
orb->response_bus =
dma_map_single(device->card->device, &orb->response,
sizeof(orb->response), DMA_FROM_DEVICE);
......@@ -490,7 +520,7 @@ sbp2_send_management_orb(struct fw_unit *unit, int node_id, int generation,
if (response)
orb->response, sizeof(orb->response));
kref_put(&orb->base.kref, free_orb);
return retval;
......@@ -886,7 +916,6 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
orb->cmd->result = result;
static int sbp2_command_orb_map_scatterlist(struct sbp2_command_orb *orb)
......@@ -1005,6 +1034,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
/* Initialize rcode to something not RCODE_COMPLETE. */
orb->base.rcode = -1;
orb->unit = unit;
orb->done = done;
......@@ -1051,10 +1081,11 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
sbp2_send_orb(&orb->base, unit, sd->node_id, sd->generation,
sd->command_block_agent_address + SBP2_ORB_POINTER);
kref_put(&orb->base.kref, free_orb);
return 0;
kref_put(&orb->base.kref, free_orb);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment