summaryrefslogtreecommitdiff
path: root/drivers/scsi/libfc/fc_exch.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/libfc/fc_exch.c')
-rw-r--r--drivers/scsi/libfc/fc_exch.c145
1 files changed, 115 insertions, 30 deletions
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index ec2a1aec2350..28231badd9e6 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
@@ -67,6 +67,11 @@ struct workqueue_struct *fc_exch_workqueue;
struct fc_exch_pool {
u16 next_index;
u16 total_exches;
+
+ /* two cache of free slot in exch array */
+ u16 left;
+ u16 right;
+
spinlock_t lock;
struct list_head ex_list;
};
@@ -108,7 +113,6 @@ struct fc_exch_mgr {
atomic_t non_bls_resp;
} stats;
};
-#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
/**
* struct fc_exch_mgr_anchor - primary structure for list of EMs
@@ -397,13 +401,23 @@ static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index,
static void fc_exch_delete(struct fc_exch *ep)
{
struct fc_exch_pool *pool;
+ u16 index;
pool = ep->pool;
spin_lock_bh(&pool->lock);
WARN_ON(pool->total_exches <= 0);
pool->total_exches--;
- fc_exch_ptr_set(pool, (ep->xid - ep->em->min_xid) >> fc_cpu_order,
- NULL);
+
+ /* update cache of free slot */
+ index = (ep->xid - ep->em->min_xid) >> fc_cpu_order;
+ if (pool->left == FC_XID_UNKNOWN)
+ pool->left = index;
+ else if (pool->right == FC_XID_UNKNOWN)
+ pool->right = index;
+ else
+ pool->next_index = index;
+
+ fc_exch_ptr_set(pool, index, NULL);
list_del(&ep->ex_list);
spin_unlock_bh(&pool->lock);
fc_exch_release(ep); /* drop hold for exch in mp */
@@ -544,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
@@ -679,6 +709,19 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
pool = per_cpu_ptr(mp->pool, cpu);
spin_lock_bh(&pool->lock);
put_cpu();
+
+ /* peek cache of free slot */
+ if (pool->left != FC_XID_UNKNOWN) {
+ index = pool->left;
+ pool->left = FC_XID_UNKNOWN;
+ goto hit;
+ }
+ if (pool->right != FC_XID_UNKNOWN) {
+ index = pool->right;
+ pool->right = FC_XID_UNKNOWN;
+ goto hit;
+ }
+
index = pool->next_index;
/* allocate new exch from pool */
while (fc_exch_ptr_get(pool, index)) {
@@ -687,7 +730,7 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
goto err;
}
pool->next_index = index == mp->pool_max_index ? 0 : index + 1;
-
+hit:
fc_exch_hold(ep); /* hold for exch in mp */
spin_lock_init(&ep->ex_lock);
/*
@@ -1236,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)
{
@@ -1247,12 +1292,21 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
list_for_each_entry(ema, &lport->ema_list, ema_list)
if ((!ema->match || ema->match(fp)) &&
- fc_seq_lookup_recip(lport, ema->mp, fp) != FC_RJT_NONE)
+ fc_seq_lookup_recip(lport, ema->mp, fp) == FC_RJT_NONE)
break;
return fr_seq(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
@@ -1343,7 +1397,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
}
if (ep->esb_stat & ESB_ST_COMPLETE) {
atomic_inc(&mp->stats.xid_not_found);
- goto out;
+ goto rel;
}
if (ep->rxid == FC_XID_UNKNOWN)
ep->rxid = ntohs(fh->fh_rx_id);
@@ -2121,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
@@ -2181,6 +2236,8 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
goto free_mempool;
for_each_possible_cpu(cpu) {
pool = per_cpu_ptr(mp->pool, cpu);
+ pool->left = FC_XID_UNKNOWN;
+ pool->right = FC_XID_UNKNOWN;
spin_lock_init(&pool->lock);
INIT_LIST_HEAD(&pool->ex_list);
}
@@ -2222,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) {
@@ -2242,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.
@@ -2297,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;
@@ -2318,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);
@@ -2325,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);
@@ -2363,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);