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 d21367d3305f..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 */ | |||
38 | EXPORT_SYMBOL(fc_cpu_mask); | 38 | EXPORT_SYMBOL(fc_cpu_mask); |
39 | static u16 fc_cpu_order; /* 2's power to represent total possible cpus */ | 39 | static u16 fc_cpu_order; /* 2's power to represent total possible cpus */ |
40 | static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ | 40 | static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ |
41 | struct workqueue_struct *fc_exch_workqueue; | 41 | static struct workqueue_struct *fc_exch_workqueue; |
42 | 42 | ||
43 | /* | 43 | /* |
44 | * Structure and function definitions for managing Fibre Channel Exchanges | 44 | * 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) | |||
558 | return sp; | 558 | return sp; |
559 | } | 559 | } |
560 | 560 | ||
561 | /* | ||
562 | * Set the response handler for the exchange associated with a sequence. | ||
563 | */ | ||
564 | static void fc_seq_set_resp(struct fc_seq *sp, | ||
565 | void (*resp)(struct fc_seq *, struct fc_frame *, | ||
566 | void *), | ||
567 | void *arg) | ||
568 | { | ||
569 | struct fc_exch *ep = fc_seq_exch(sp); | ||
570 | |||
571 | spin_lock_bh(&ep->ex_lock); | ||
572 | ep->resp = resp; | ||
573 | ep->arg = arg; | ||
574 | spin_unlock_bh(&ep->ex_lock); | ||
575 | } | ||
576 | |||
561 | /** | 577 | /** |
562 | * fc_seq_exch_abort() - Abort an exchange and sequence | 578 | * fc_seq_exch_abort() - Abort an exchange and sequence |
563 | * @req_sp: The sequence to be aborted | 579 | * @req_sp: The sequence to be aborted |
@@ -650,13 +666,10 @@ static void fc_exch_timeout(struct work_struct *work) | |||
650 | if (e_stat & ESB_ST_ABNORMAL) | 666 | if (e_stat & ESB_ST_ABNORMAL) |
651 | rc = fc_exch_done_locked(ep); | 667 | rc = fc_exch_done_locked(ep); |
652 | spin_unlock_bh(&ep->ex_lock); | 668 | spin_unlock_bh(&ep->ex_lock); |
669 | if (!rc) | ||
670 | fc_exch_delete(ep); | ||
653 | if (resp) | 671 | if (resp) |
654 | resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg); | 672 | resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg); |
655 | if (!rc) { | ||
656 | /* delete the exchange if it's already being aborted */ | ||
657 | fc_exch_delete(ep); | ||
658 | return; | ||
659 | } | ||
660 | fc_seq_exch_abort(sp, 2 * ep->r_a_tov); | 673 | fc_seq_exch_abort(sp, 2 * ep->r_a_tov); |
661 | goto done; | 674 | goto done; |
662 | } | 675 | } |
@@ -1266,6 +1279,8 @@ free: | |||
1266 | * @fp: The request frame | 1279 | * @fp: The request frame |
1267 | * | 1280 | * |
1268 | * On success, the sequence pointer will be returned and also in fr_seq(@fp). | 1281 | * On success, the sequence pointer will be returned and also in fr_seq(@fp). |
1282 | * A reference will be held on the exchange/sequence for the caller, which | ||
1283 | * must call fc_seq_release(). | ||
1269 | */ | 1284 | */ |
1270 | static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) | 1285 | static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) |
1271 | { | 1286 | { |
@@ -1283,6 +1298,15 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) | |||
1283 | } | 1298 | } |
1284 | 1299 | ||
1285 | /** | 1300 | /** |
1301 | * fc_seq_release() - Release the hold | ||
1302 | * @sp: The sequence. | ||
1303 | */ | ||
1304 | static void fc_seq_release(struct fc_seq *sp) | ||
1305 | { | ||
1306 | fc_exch_release(fc_seq_exch(sp)); | ||
1307 | } | ||
1308 | |||
1309 | /** | ||
1286 | * fc_exch_recv_req() - Handler for an incoming request | 1310 | * fc_exch_recv_req() - Handler for an incoming request |
1287 | * @lport: The local port that received the request | 1311 | * @lport: The local port that received the request |
1288 | * @mp: The EM that the exchange is on | 1312 | * @mp: The EM that the exchange is on |
@@ -2151,6 +2175,7 @@ err: | |||
2151 | fc_exch_mgr_del(ema); | 2175 | fc_exch_mgr_del(ema); |
2152 | return -ENOMEM; | 2176 | return -ENOMEM; |
2153 | } | 2177 | } |
2178 | EXPORT_SYMBOL(fc_exch_mgr_list_clone); | ||
2154 | 2179 | ||
2155 | /** | 2180 | /** |
2156 | * fc_exch_mgr_alloc() - Allocate an exchange manager | 2181 | * fc_exch_mgr_alloc() - Allocate an exchange manager |
@@ -2254,16 +2279,45 @@ void fc_exch_mgr_free(struct fc_lport *lport) | |||
2254 | EXPORT_SYMBOL(fc_exch_mgr_free); | 2279 | EXPORT_SYMBOL(fc_exch_mgr_free); |
2255 | 2280 | ||
2256 | /** | 2281 | /** |
2282 | * fc_find_ema() - Lookup and return appropriate Exchange Manager Anchor depending | ||
2283 | * upon 'xid'. | ||
2284 | * @f_ctl: f_ctl | ||
2285 | * @lport: The local port the frame was received on | ||
2286 | * @fh: The received frame header | ||
2287 | */ | ||
2288 | static struct fc_exch_mgr_anchor *fc_find_ema(u32 f_ctl, | ||
2289 | struct fc_lport *lport, | ||
2290 | struct fc_frame_header *fh) | ||
2291 | { | ||
2292 | struct fc_exch_mgr_anchor *ema; | ||
2293 | u16 xid; | ||
2294 | |||
2295 | if (f_ctl & FC_FC_EX_CTX) | ||
2296 | xid = ntohs(fh->fh_ox_id); | ||
2297 | else { | ||
2298 | xid = ntohs(fh->fh_rx_id); | ||
2299 | if (xid == FC_XID_UNKNOWN) | ||
2300 | return list_entry(lport->ema_list.prev, | ||
2301 | typeof(*ema), ema_list); | ||
2302 | } | ||
2303 | |||
2304 | list_for_each_entry(ema, &lport->ema_list, ema_list) { | ||
2305 | if ((xid >= ema->mp->min_xid) && | ||
2306 | (xid <= ema->mp->max_xid)) | ||
2307 | return ema; | ||
2308 | } | ||
2309 | return NULL; | ||
2310 | } | ||
2311 | /** | ||
2257 | * fc_exch_recv() - Handler for received frames | 2312 | * fc_exch_recv() - Handler for received frames |
2258 | * @lport: The local port the frame was received on | 2313 | * @lport: The local port the frame was received on |
2259 | * @fp: The received frame | 2314 | * @fp: The received frame |
2260 | */ | 2315 | */ |
2261 | void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp) | 2316 | void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp) |
2262 | { | 2317 | { |
2263 | struct fc_frame_header *fh = fc_frame_header_get(fp); | 2318 | struct fc_frame_header *fh = fc_frame_header_get(fp); |
2264 | struct fc_exch_mgr_anchor *ema; | 2319 | struct fc_exch_mgr_anchor *ema; |
2265 | u32 f_ctl, found = 0; | 2320 | u32 f_ctl; |
2266 | u16 oxid; | ||
2267 | 2321 | ||
2268 | /* lport lock ? */ | 2322 | /* lport lock ? */ |
2269 | if (!lport || lport->state == LPORT_ST_DISABLED) { | 2323 | if (!lport || lport->state == LPORT_ST_DISABLED) { |
@@ -2274,24 +2328,17 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp) | |||
2274 | } | 2328 | } |
2275 | 2329 | ||
2276 | f_ctl = ntoh24(fh->fh_f_ctl); | 2330 | f_ctl = ntoh24(fh->fh_f_ctl); |
2277 | oxid = ntohs(fh->fh_ox_id); | 2331 | ema = fc_find_ema(f_ctl, lport, fh); |
2278 | if (f_ctl & FC_FC_EX_CTX) { | 2332 | if (!ema) { |
2279 | list_for_each_entry(ema, &lport->ema_list, ema_list) { | 2333 | FC_LPORT_DBG(lport, "Unable to find Exchange Manager Anchor," |
2280 | if ((oxid >= ema->mp->min_xid) && | 2334 | "fc_ctl <0x%x>, xid <0x%x>\n", |
2281 | (oxid <= ema->mp->max_xid)) { | 2335 | f_ctl, |
2282 | found = 1; | 2336 | (f_ctl & FC_FC_EX_CTX) ? |
2283 | break; | 2337 | ntohs(fh->fh_ox_id) : |
2284 | } | 2338 | ntohs(fh->fh_rx_id)); |
2285 | } | 2339 | fc_frame_free(fp); |
2286 | 2340 | return; | |
2287 | if (!found) { | 2341 | } |
2288 | FC_LPORT_DBG(lport, "Received response for out " | ||
2289 | "of range oxid:%hx\n", oxid); | ||
2290 | fc_frame_free(fp); | ||
2291 | return; | ||
2292 | } | ||
2293 | } else | ||
2294 | ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list); | ||
2295 | 2342 | ||
2296 | /* | 2343 | /* |
2297 | * If frame is marked invalid, just drop it. | 2344 | * If frame is marked invalid, just drop it. |
@@ -2329,6 +2376,9 @@ int fc_exch_init(struct fc_lport *lport) | |||
2329 | if (!lport->tt.seq_start_next) | 2376 | if (!lport->tt.seq_start_next) |
2330 | lport->tt.seq_start_next = fc_seq_start_next; | 2377 | lport->tt.seq_start_next = fc_seq_start_next; |
2331 | 2378 | ||
2379 | if (!lport->tt.seq_set_resp) | ||
2380 | lport->tt.seq_set_resp = fc_seq_set_resp; | ||
2381 | |||
2332 | if (!lport->tt.exch_seq_send) | 2382 | if (!lport->tt.exch_seq_send) |
2333 | lport->tt.exch_seq_send = fc_exch_seq_send; | 2383 | lport->tt.exch_seq_send = fc_exch_seq_send; |
2334 | 2384 | ||
@@ -2350,6 +2400,9 @@ int fc_exch_init(struct fc_lport *lport) | |||
2350 | if (!lport->tt.seq_assign) | 2400 | if (!lport->tt.seq_assign) |
2351 | lport->tt.seq_assign = fc_seq_assign; | 2401 | lport->tt.seq_assign = fc_seq_assign; |
2352 | 2402 | ||
2403 | if (!lport->tt.seq_release) | ||
2404 | lport->tt.seq_release = fc_seq_release; | ||
2405 | |||
2353 | return 0; | 2406 | return 0; |
2354 | } | 2407 | } |
2355 | EXPORT_SYMBOL(fc_exch_init); | 2408 | EXPORT_SYMBOL(fc_exch_init); |
@@ -2357,7 +2410,7 @@ EXPORT_SYMBOL(fc_exch_init); | |||
2357 | /** | 2410 | /** |
2358 | * fc_setup_exch_mgr() - Setup an exchange manager | 2411 | * fc_setup_exch_mgr() - Setup an exchange manager |
2359 | */ | 2412 | */ |
2360 | int fc_setup_exch_mgr() | 2413 | int fc_setup_exch_mgr(void) |
2361 | { | 2414 | { |
2362 | fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch), | 2415 | fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch), |
2363 | 0, SLAB_HWCACHE_ALIGN, NULL); | 2416 | 0, SLAB_HWCACHE_ALIGN, NULL); |
@@ -2395,7 +2448,7 @@ int fc_setup_exch_mgr() | |||
2395 | /** | 2448 | /** |
2396 | * fc_destroy_exch_mgr() - Destroy an exchange manager | 2449 | * fc_destroy_exch_mgr() - Destroy an exchange manager |
2397 | */ | 2450 | */ |
2398 | void fc_destroy_exch_mgr() | 2451 | void fc_destroy_exch_mgr(void) |
2399 | { | 2452 | { |
2400 | destroy_workqueue(fc_exch_workqueue); | 2453 | destroy_workqueue(fc_exch_workqueue); |
2401 | kmem_cache_destroy(fc_em_cachep); | 2454 | kmem_cache_destroy(fc_em_cachep); |