diff options
Diffstat (limited to 'drivers/scsi/libfc/fc_exch.c')
-rw-r--r-- | drivers/scsi/libfc/fc_exch.c | 111 |
1 files changed, 82 insertions, 29 deletions
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index d21367d3305..28231badd9e 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -38,7 +38,7 @@ u16 fc_cpu_mask; /* cpu mask for possible cpus */ EXPORT_SYMBOL(fc_cpu_mask); static u16 fc_cpu_order; /* 2's power to represent total possible cpus */ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ -struct workqueue_struct *fc_exch_workqueue; +static struct workqueue_struct *fc_exch_workqueue; /* * Structure and function definitions for managing Fibre Channel Exchanges @@ -558,6 +558,22 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp) return sp; } +/* + * Set the response handler for the exchange associated with a sequence. + */ +static void fc_seq_set_resp(struct fc_seq *sp, + void (*resp)(struct fc_seq *, struct fc_frame *, + void *), + void *arg) +{ + struct fc_exch *ep = fc_seq_exch(sp); + + spin_lock_bh(&ep->ex_lock); + ep->resp = resp; + ep->arg = arg; + spin_unlock_bh(&ep->ex_lock); +} + /** * fc_seq_exch_abort() - Abort an exchange and sequence * @req_sp: The sequence to be aborted @@ -650,13 +666,10 @@ static void fc_exch_timeout(struct work_struct *work) if (e_stat & ESB_ST_ABNORMAL) rc = fc_exch_done_locked(ep); spin_unlock_bh(&ep->ex_lock); + if (!rc) + fc_exch_delete(ep); if (resp) resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg); - if (!rc) { - /* delete the exchange if it's already being aborted */ - fc_exch_delete(ep); - return; - } fc_seq_exch_abort(sp, 2 * ep->r_a_tov); goto done; } @@ -1266,6 +1279,8 @@ free: * @fp: The request frame * * On success, the sequence pointer will be returned and also in fr_seq(@fp). + * A reference will be held on the exchange/sequence for the caller, which + * must call fc_seq_release(). */ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) { @@ -1283,6 +1298,15 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) } /** + * fc_seq_release() - Release the hold + * @sp: The sequence. + */ +static void fc_seq_release(struct fc_seq *sp) +{ + fc_exch_release(fc_seq_exch(sp)); +} + +/** * fc_exch_recv_req() - Handler for an incoming request * @lport: The local port that received the request * @mp: The EM that the exchange is on @@ -2151,6 +2175,7 @@ err: fc_exch_mgr_del(ema); return -ENOMEM; } +EXPORT_SYMBOL(fc_exch_mgr_list_clone); /** * fc_exch_mgr_alloc() - Allocate an exchange manager @@ -2254,16 +2279,45 @@ void fc_exch_mgr_free(struct fc_lport *lport) EXPORT_SYMBOL(fc_exch_mgr_free); /** + * fc_find_ema() - Lookup and return appropriate Exchange Manager Anchor depending + * upon 'xid'. + * @f_ctl: f_ctl + * @lport: The local port the frame was received on + * @fh: The received frame header + */ +static struct fc_exch_mgr_anchor *fc_find_ema(u32 f_ctl, + struct fc_lport *lport, + struct fc_frame_header *fh) +{ + struct fc_exch_mgr_anchor *ema; + u16 xid; + + if (f_ctl & FC_FC_EX_CTX) + xid = ntohs(fh->fh_ox_id); + else { + xid = ntohs(fh->fh_rx_id); + if (xid == FC_XID_UNKNOWN) + return list_entry(lport->ema_list.prev, + typeof(*ema), ema_list); + } + + list_for_each_entry(ema, &lport->ema_list, ema_list) { + if ((xid >= ema->mp->min_xid) && + (xid <= ema->mp->max_xid)) + return ema; + } + return NULL; +} +/** * fc_exch_recv() - Handler for received frames * @lport: The local port the frame was received on - * @fp: The received frame + * @fp: The received frame */ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); struct fc_exch_mgr_anchor *ema; - u32 f_ctl, found = 0; - u16 oxid; + u32 f_ctl; /* lport lock ? */ if (!lport || lport->state == LPORT_ST_DISABLED) { @@ -2274,24 +2328,17 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp) } f_ctl = ntoh24(fh->fh_f_ctl); - oxid = ntohs(fh->fh_ox_id); - if (f_ctl & FC_FC_EX_CTX) { - list_for_each_entry(ema, &lport->ema_list, ema_list) { - if ((oxid >= ema->mp->min_xid) && - (oxid <= ema->mp->max_xid)) { - found = 1; - break; - } - } - - if (!found) { - FC_LPORT_DBG(lport, "Received response for out " - "of range oxid:%hx\n", oxid); - fc_frame_free(fp); - return; - } - } else - ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list); + ema = fc_find_ema(f_ctl, lport, fh); + if (!ema) { + FC_LPORT_DBG(lport, "Unable to find Exchange Manager Anchor," + "fc_ctl <0x%x>, xid <0x%x>\n", + f_ctl, + (f_ctl & FC_FC_EX_CTX) ? + ntohs(fh->fh_ox_id) : + ntohs(fh->fh_rx_id)); + fc_frame_free(fp); + return; + } /* * If frame is marked invalid, just drop it. @@ -2329,6 +2376,9 @@ int fc_exch_init(struct fc_lport *lport) if (!lport->tt.seq_start_next) lport->tt.seq_start_next = fc_seq_start_next; + if (!lport->tt.seq_set_resp) + lport->tt.seq_set_resp = fc_seq_set_resp; + if (!lport->tt.exch_seq_send) lport->tt.exch_seq_send = fc_exch_seq_send; @@ -2350,6 +2400,9 @@ int fc_exch_init(struct fc_lport *lport) if (!lport->tt.seq_assign) lport->tt.seq_assign = fc_seq_assign; + if (!lport->tt.seq_release) + lport->tt.seq_release = fc_seq_release; + return 0; } EXPORT_SYMBOL(fc_exch_init); @@ -2357,7 +2410,7 @@ EXPORT_SYMBOL(fc_exch_init); /** * fc_setup_exch_mgr() - Setup an exchange manager */ -int fc_setup_exch_mgr() +int fc_setup_exch_mgr(void) { fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch), 0, SLAB_HWCACHE_ALIGN, NULL); @@ -2395,7 +2448,7 @@ int fc_setup_exch_mgr() /** * fc_destroy_exch_mgr() - Destroy an exchange manager */ -void fc_destroy_exch_mgr() +void fc_destroy_exch_mgr(void) { destroy_workqueue(fc_exch_workqueue); kmem_cache_destroy(fc_em_cachep); |