summaryrefslogblamecommitdiff
path: root/drivers/scsi/libfc/fc_fcp.c
blob: a070e571243976edc0b345f9dde68931351f7c3e (plain) (tree)
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622


































































































































































                                                                               
                                                         




















                                                                           
                                                         













                                                             
                                                         



























































                                                                          
                           





























                                                                         
                                              




























































































                                                                           

                                                  




























































































































































































































                                                                                

                                             





























                                                                      




                                     


























































































































































































                                                                               
                                                                 


























                                                                         
                                           






































                                                                              
                                                     


















































                                                                         
                                                            


























































































































































































































































































































                                                                                
                                   































































































































































































































































































































































                                                                                
                                                                          








































































































                                                                               
                                                           

















































































                                                                               
                                                                                

                                                                  
                                    
                           
                                                      








































                                                                             
                                                          















                                                     
                                  

                                     
                           












                                                            
                              




























                                                                              
                                          



















































                                                                               
                                                                            
























                                                                               
                                           
































































































































                                                                             
/*
 * Copyright(c) 2007 Intel Corporation. All rights reserved.
 * Copyright(c) 2008 Red Hat, Inc.  All rights reserved.
 * Copyright(c) 2008 Mike Christie
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Maintained at www.Open-FCoE.org
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/scatterlist.h>
#include <linux/err.h>
#include <linux/crc32.h>

#include <scsi/scsi_tcq.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>

#include <scsi/fc/fc_fc2.h>

#include <scsi/libfc.h>
#include <scsi/fc_encode.h>

MODULE_AUTHOR("Open-FCoE.org");
MODULE_DESCRIPTION("libfc");
MODULE_LICENSE("GPL");

static int fc_fcp_debug;

#define FC_DEBUG_FCP(fmt...)			\
	do {					\
		if (fc_fcp_debug)		\
			FC_DBG(fmt);		\
	} while (0)

static struct kmem_cache *scsi_pkt_cachep;

/* SRB state definitions */
#define FC_SRB_FREE		0		/* cmd is free */
#define FC_SRB_CMD_SENT		(1 << 0)	/* cmd has been sent */
#define FC_SRB_RCV_STATUS	(1 << 1)	/* response has arrived */
#define FC_SRB_ABORT_PENDING	(1 << 2)	/* cmd abort sent to device */
#define FC_SRB_ABORTED		(1 << 3)	/* abort acknowleged */
#define FC_SRB_DISCONTIG	(1 << 4)	/* non-sequential data recvd */
#define FC_SRB_COMPL		(1 << 5)	/* fc_io_compl has been run */
#define FC_SRB_FCP_PROCESSING_TMO (1 << 6)	/* timer function processing */
#define FC_SRB_NOMEM		(1 << 7)	/* dropped to out of mem */

#define FC_SRB_READ		(1 << 1)
#define FC_SRB_WRITE		(1 << 0)

/*
 * The SCp.ptr should be tested and set under the host lock. NULL indicates
 * that the command has been retruned to the scsi layer.
 */
#define CMD_SP(Cmnd)		    ((struct fc_fcp_pkt *)(Cmnd)->SCp.ptr)
#define CMD_ENTRY_STATUS(Cmnd)	    ((Cmnd)->SCp.have_data_in)
#define CMD_COMPL_STATUS(Cmnd)	    ((Cmnd)->SCp.this_residual)
#define CMD_SCSI_STATUS(Cmnd)	    ((Cmnd)->SCp.Status)
#define CMD_RESID_LEN(Cmnd)	    ((Cmnd)->SCp.buffers_residual)

struct fc_fcp_internal {
	mempool_t	*scsi_pkt_pool;
	struct list_head scsi_pkt_queue;
	u8		throttled;
};

#define fc_get_scsi_internal(x)	((struct fc_fcp_internal *)(x)->scsi_priv)

/*
 * function prototypes
 * FC scsi I/O related functions
 */
static void fc_fcp_recv_data(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_recv(struct fc_seq *, struct fc_frame *, void *);
static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_complete_locked(struct fc_fcp_pkt *);
static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);
static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp);
static void fc_timeout_error(struct fc_fcp_pkt *);
static void fc_fcp_timeout(unsigned long data);
static void fc_fcp_rec(struct fc_fcp_pkt *);
static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_rec_resp(struct fc_seq *, struct fc_frame *, void *);
static void fc_io_compl(struct fc_fcp_pkt *);

static void fc_fcp_srr(struct fc_fcp_pkt *, enum fc_rctl, u32);
static void fc_fcp_srr_resp(struct fc_seq *, struct fc_frame *, void *);
static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);

/*
 * command status codes
 */
#define FC_COMPLETE		0
#define FC_CMD_ABORTED		1
#define FC_CMD_RESET		2
#define FC_CMD_PLOGO		3
#define FC_SNS_RCV		4
#define FC_TRANS_ERR		5
#define FC_DATA_OVRRUN		6
#define FC_DATA_UNDRUN		7
#define FC_ERROR		8
#define FC_HRD_ERROR		9
#define FC_CMD_TIME_OUT		10

/*
 * Error recovery timeout values.
 */
#define FC_SCSI_ER_TIMEOUT	(10 * HZ)
#define FC_SCSI_TM_TOV		(10 * HZ)
#define FC_SCSI_REC_TOV		(2 * HZ)
#define FC_HOST_RESET_TIMEOUT	(30 * HZ)

#define FC_MAX_ERROR_CNT	5
#define FC_MAX_RECOV_RETRY	3

#define FC_FCP_DFLT_QUEUE_DEPTH 32

/**
 * fc_fcp_pkt_alloc - allocation routine for scsi_pkt packet
 * @lp:		fc lport struct
 * @gfp:	gfp flags for allocation
 *
 * This is used by upper layer scsi driver.
 * Return Value : scsi_pkt structure or null on allocation failure.
 * Context	: call from process context. no locking required.
 */
static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
{
	struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
	struct fc_fcp_pkt *fsp;

	fsp = mempool_alloc(si->scsi_pkt_pool, gfp);
	if (fsp) {
		memset(fsp, 0, sizeof(*fsp));
		fsp->lp = lp;
		atomic_set(&fsp->ref_cnt, 1);
		init_timer(&fsp->timer);
		INIT_LIST_HEAD(&fsp->list);
		spin_lock_init(&fsp->scsi_pkt_lock);
	}
	return fsp;
}

/**
 * fc_fcp_pkt_release() - release hold on scsi_pkt packet
 * @fsp:	fcp packet struct
 *
 * This is used by upper layer scsi driver.
 * Context	: call from process  and interrupt context.
 *		  no locking required
 */
static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
{
	if (atomic_dec_and_test(&fsp->ref_cnt)) {
		struct fc_fcp_internal *si = fc_get_scsi_internal(fsp->lp);

		mempool_free(fsp, si->scsi_pkt_pool);
	}
}

static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp)
{
	atomic_inc(&fsp->ref_cnt);
}

/**
 * fc_fcp_pkt_destory() - release hold on scsi_pkt packet
 * @seq:		exchange sequence
 * @fsp:	fcp packet struct
 *
 * Release hold on scsi_pkt packet set to keep scsi_pkt
 * till EM layer exch resource is not freed.
 * Context	: called from from EM layer.
 *		  no locking required
 */
static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
{
	fc_fcp_pkt_release(fsp);
}

/**
 * fc_fcp_lock_pkt() - lock a packet and get a ref to it.
 * @fsp:	fcp packet
 *
 * We should only return error if we return a command to scsi-ml before
 * getting a response. This can happen in cases where we send a abort, but
 * do not wait for the response and the abort and command can be passing
 * each other on the wire/network-layer.
 *
 * Note: this function locks the packet and gets a reference to allow
 * callers to call the completion function while the lock is held and
 * not have to worry about the packets refcount.
 *
 * TODO: Maybe we should just have callers grab/release the lock and
 * have a function that they call to verify the fsp and grab a ref if
 * needed.
 */
static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp)
{
	spin_lock_bh(&fsp->scsi_pkt_lock);
	if (fsp->state & FC_SRB_COMPL) {
		spin_unlock_bh(&fsp->scsi_pkt_lock);
		return -EPERM;
	}

	fc_fcp_pkt_hold(fsp);
	return 0;
}

static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)
{
	spin_unlock_bh(&fsp->scsi_pkt_lock);
	fc_fcp_pkt_release(fsp);
}

static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay)
{
	if (!(fsp->state & FC_SRB_COMPL))
		mod_timer(&fsp->timer, jiffies + delay);
}

static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
{
	if (!fsp->seq_ptr)
		return -EINVAL;

	fsp->state |= FC_SRB_ABORT_PENDING;
	return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0);
}

/*
 * Retry command.
 * An abort isn't needed.
 */
static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
{
	if (fsp->seq_ptr) {
		fsp->lp->tt.exch_done(fsp->seq_ptr);
		fsp->seq_ptr = NULL;
	}

	fsp->state &= ~FC_SRB_ABORT_PENDING;
	fsp->io_status = 0;
	fsp->status_code = FC_ERROR;
	fc_fcp_complete_locked(fsp);
}

/*
 * Receive SCSI data from target.
 * Called after receiving solicited data.
 */
static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
	struct scsi_cmnd *sc = fsp->cmd;
	struct fc_lport *lp = fsp->lp;
	struct fcoe_dev_stats *stats;
	struct fc_frame_header *fh;
	size_t start_offset;
	size_t offset;
	u32 crc;
	u32 copy_len = 0;
	size_t len;
	void *buf;
	struct scatterlist *sg;
	size_t remaining;

	fh = fc_frame_header_get(fp);
	offset = ntohl(fh->fh_parm_offset);
	start_offset = offset;
	len = fr_len(fp) - sizeof(*fh);
	buf = fc_frame_payload_get(fp, 0);

	if (offset + len > fsp->data_len) {
		/* this should never happen */
		if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) &&
		    fc_frame_crc_check(fp))
			goto crc_err;
		FC_DEBUG_FCP("data received past end. len %zx offset %zx "
			     "data_len %x\n", len, offset, fsp->data_len);
		fc_fcp_retry_cmd(fsp);
		return;
	}
	if (offset != fsp->xfer_len)
		fsp->state |= FC_SRB_DISCONTIG;

	crc = 0;
	if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
		crc = crc32(~0, (u8 *) fh, sizeof(*fh));

	sg = scsi_sglist(sc);
	remaining = len;

	while (remaining > 0 && sg) {
		size_t off;
		void *page_addr;
		size_t sg_bytes;

		if (offset >= sg->length) {
			offset -= sg->length;
			sg = sg_next(sg);
			continue;
		}
		sg_bytes = min(remaining, sg->length - offset);

		/*
		 * The scatterlist item may be bigger than PAGE_SIZE,
		 * but we are limited to mapping PAGE_SIZE at a time.
		 */
		off = offset + sg->offset;
		sg_bytes = min(sg_bytes, (size_t)
			       (PAGE_SIZE - (off & ~PAGE_MASK)));
		page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT),
					KM_SOFTIRQ0);
		if (!page_addr)
			break;		/* XXX panic? */

		if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
			crc = crc32(crc, buf, sg_bytes);
		memcpy((char *)page_addr + (off & ~PAGE_MASK), buf,
		       sg_bytes);

		kunmap_atomic(page_addr, KM_SOFTIRQ0);
		buf += sg_bytes;
		offset += sg_bytes;
		remaining -= sg_bytes;
		copy_len += sg_bytes;
	}

	if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
		buf = fc_frame_payload_get(fp, 0);
		if (len % 4) {
			crc = crc32(crc, buf + len, 4 - (len % 4));
			len += 4 - (len % 4);
		}

		if (~crc != le32_to_cpu(fr_crc(fp))) {
crc_err:
			stats = lp->dev_stats[smp_processor_id()];
			stats->ErrorFrames++;
			if (stats->InvalidCRCCount++ < 5)
				FC_DBG("CRC error on data frame\n");
			/*
			 * Assume the frame is total garbage.
			 * We may have copied it over the good part
			 * of the buffer.
			 * If so, we need to retry the entire operation.
			 * Otherwise, ignore it.
			 */
			if (fsp->state & FC_SRB_DISCONTIG)
				fc_fcp_retry_cmd(fsp);
			return;
		}
	}

	if (fsp->xfer_contig_end == start_offset)
		fsp->xfer_contig_end += copy_len;
	fsp->xfer_len += copy_len;

	/*
	 * In the very rare event that this data arrived after the response
	 * and completes the transfer, call the completion handler.
	 */
	if (unlikely(fsp->state & FC_SRB_RCV_STATUS) &&
	    fsp->xfer_len == fsp->data_len - fsp->scsi_resid)
		fc_fcp_complete_locked(fsp);
}

/**
 * fc_fcp_send_data() -  Send SCSI data to target.
 * @fsp: ptr to fc_fcp_pkt
 * @sp: ptr to this sequence
 * @offset: starting offset for this data request
 * @seq_blen: the burst length for this data request
 *
 * Called after receiving a Transfer Ready data descriptor.
 * if LLD is capable of seq offload then send down seq_blen
 * size of data in single frame, otherwise send multiple FC
 * frames of max FC frame payload supported by target port.
 *
 * Returns : 0 for success.
 */
static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
			    size_t offset, size_t seq_blen)
{
	struct fc_exch *ep;
	struct scsi_cmnd *sc;
	struct scatterlist *sg;
	struct fc_frame *fp = NULL;
	struct fc_lport *lp = fsp->lp;
	size_t remaining;
	size_t t_blen;
	size_t tlen;
	size_t sg_bytes;
	size_t frame_offset, fh_parm_offset;
	int error;
	void *data = NULL;
	void *page_addr;
	int using_sg = lp->sg_supp;
	u32 f_ctl;

	WARN_ON(seq_blen <= 0);
	if (unlikely(offset + seq_blen > fsp->data_len)) {
		/* this should never happen */
		FC_DEBUG_FCP("xfer-ready past end. seq_blen %zx offset %zx\n",
			     seq_blen, offset);
		fc_fcp_send_abort(fsp);
		return 0;
	} else if (offset != fsp->xfer_len) {
		/* Out of Order Data Request - no problem, but unexpected. */
		FC_DEBUG_FCP("xfer-ready non-contiguous. "
			     "seq_blen %zx offset %zx\n", seq_blen, offset);
	}

	/*
	 * if LLD is capable of seq_offload then set transport
	 * burst length (t_blen) to seq_blen, otherwise set t_blen
	 * to max FC frame payload previously set in fsp->max_payload.
	 */
	t_blen = lp->seq_offload ? seq_blen : fsp->max_payload;
	WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);
	if (t_blen > 512)
		t_blen &= ~(512 - 1);	/* round down to block size */
	WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);	/* won't go below 256 */
	sc = fsp->cmd;

	remaining = seq_blen;
	fh_parm_offset = frame_offset = offset;
	tlen = 0;
	seq = lp->tt.seq_start_next(seq);
	f_ctl = FC_FC_REL_OFF;
	WARN_ON(!seq);

	/*
	 * If a get_page()/put_page() will fail, don't use sg lists
	 * in the fc_frame structure.
	 *
	 * The put_page() may be long after the I/O has completed
	 * in the case of FCoE, since the network driver does it
	 * via free_skb().  See the test in free_pages_check().
	 *
	 * Test this case with 'dd </dev/zero >/dev/st0 bs=64k'.
	 */
	if (using_sg) {
		for (sg = scsi_sglist(sc); sg; sg = sg_next(sg)) {
			if (page_count(sg_page(sg)) == 0 ||
			    (sg_page(sg)->flags & (1 << PG_lru |
						   1 << PG_private |
						   1 << PG_locked |
						   1 << PG_active |
						   1 << PG_slab |
						   1 << PG_swapcache |
						   1 << PG_writeback |
						   1 << PG_reserved |
						   1 << PG_buddy))) {
				using_sg = 0;
				break;
			}
		}
	}
	sg = scsi_sglist(sc);

	while (remaining > 0 && sg) {
		if (offset >= sg->length) {
			offset -= sg->length;
			sg = sg_next(sg);
			continue;
		}
		if (!fp) {
			tlen = min(t_blen, remaining);

			/*
			 * TODO.  Temporary workaround.	 fc_seq_send() can't
			 * handle odd lengths in non-linear skbs.
			 * This will be the final fragment only.
			 */
			if (tlen % 4)
				using_sg = 0;
			if (using_sg) {
				fp = _fc_frame_alloc(lp, 0);
				if (!fp)
					return -ENOMEM;
			} else {
				fp = fc_frame_alloc(lp, tlen);
				if (!fp)
					return -ENOMEM;

				data = (void *)(fr_hdr(fp)) +
					sizeof(struct fc_frame_header);
			}
			fh_parm_offset = frame_offset;
			fr_max_payload(fp) = fsp->max_payload;
		}
		sg_bytes = min(tlen, sg->length - offset);
		if (using_sg) {
			WARN_ON(skb_shinfo(fp_skb(fp))->nr_frags >
				FC_FRAME_SG_LEN);
			get_page(sg_page(sg));
			skb_fill_page_desc(fp_skb(fp),
					   skb_shinfo(fp_skb(fp))->nr_frags,
					   sg_page(sg), sg->offset + offset,
					   sg_bytes);
			fp_skb(fp)->data_len += sg_bytes;
			fr_len(fp) += sg_bytes;
			fp_skb(fp)->truesize += PAGE_SIZE;
		} else {
			size_t off = offset + sg->offset;

			/*
			 * The scatterlist item may be bigger than PAGE_SIZE,
			 * but we must not cross pages inside the kmap.
			 */
			sg_bytes = min(sg_bytes, (size_t) (PAGE_SIZE -
							   (off & ~PAGE_MASK)));
			page_addr = kmap_atomic(sg_page(sg) +
						(off >> PAGE_SHIFT),
						KM_SOFTIRQ0);
			memcpy(data, (char *)page_addr + (off & ~PAGE_MASK),
			       sg_bytes);
			kunmap_atomic(page_addr, KM_SOFTIRQ0);
			data += sg_bytes;
		}
		offset += sg_bytes;
		frame_offset += sg_bytes;
		tlen -= sg_bytes;
		remaining -= sg_bytes;

		if (tlen)
			continue;

		/*
		 * Send sequence with transfer sequence initiative in case
		 * this is last FCP frame of the sequence.
		 */
		if (remaining == 0)
			f_ctl |= FC_FC_SEQ_INIT | FC_FC_END_SEQ;

		ep = fc_seq_exch(seq);
		fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
			       FC_TYPE_FCP, f_ctl, fh_parm_offset);

		/*
		 * send fragment using for a sequence.
		 */
		error = lp->tt.seq_send(lp, seq, fp);
		if (error) {
			WARN_ON(1);		/* send error should be rare */
			fc_fcp_retry_cmd(fsp);
			return 0;
		}
		fp = NULL;
	}
	fsp->xfer_len += seq_blen;	/* premature count? */
	return 0;
}

static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
	int ba_done = 1;
	struct fc_ba_rjt *brp;
	struct fc_frame_header *fh;

	fh = fc_frame_header_get(fp);
	switch (fh->fh_r_ctl) {
	case FC_RCTL_BA_ACC:
		break;
	case FC_RCTL_BA_RJT:
		brp = fc_frame_payload_get(fp, sizeof(*brp));
		if (brp && brp->br_reason == FC_BA_RJT_LOG_ERR)
			break;
		/* fall thru */
	default:
		/*
		 * we will let the command timeout
		 * and scsi-ml recover in this case,
		 * therefore cleared the ba_done flag.
		 */
		ba_done = 0;
	}

	if (ba_done) {
		fsp->state |= FC_SRB_ABORTED;
		fsp->state &= ~FC_SRB_ABORT_PENDING;

		if (fsp->wait_for_comp)
			complete(&fsp->tm_done);
		else
			fc_fcp_complete_locked(fsp);
	}
}

/**
 * fc_fcp_reduce_can_queue() - drop can_queue
 * @lp: lport to drop queueing for
 *
 * If we are getting memory allocation failures, then we may
 * be trying to execute too many commands. We let the running
 * commands complete or timeout, then try again with a reduced
 * can_queue. Eventually we will hit the point where we run
 * on all reserved structs.
 */
static void fc_fcp_reduce_can_queue(struct fc_lport *lp)
{
	struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
	unsigned long flags;
	int can_queue;

	spin_lock_irqsave(lp->host->host_lock, flags);
	if (si->throttled)
		goto done;
	si->throttled = 1;

	can_queue = lp->host->can_queue;
	can_queue >>= 1;
	if (!can_queue)
		can_queue = 1;
	lp->host->can_queue = can_queue;
	shost_printk(KERN_ERR, lp->host, "Could not allocate frame.\n"
		     "Reducing can_queue to %d.\n", can_queue);
done:
	spin_unlock_irqrestore(lp->host->host_lock, flags);
}

/**
 * fc_fcp_recv() - Reveive FCP frames
 * @seq: The sequence the frame is on
 * @fp: The FC frame
 * @arg: The related FCP packet
 *
 * Return   : None
 * Context  : called from Soft IRQ context
 *	      can not called holding list lock
 */
static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
	struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
	struct fc_lport *lp;
	struct fc_frame_header *fh;
	struct fcp_txrdy *dd;
	u8 r_ctl;
	int rc = 0;

	if (IS_ERR(fp))
		goto errout;

	fh = fc_frame_header_get(fp);
	r_ctl = fh->fh_r_ctl;
	lp = fsp->lp;

	if (!(lp->state & LPORT_ST_READY))
		goto out;
	if (fc_fcp_lock_pkt(fsp))
		goto out;
	fsp->last_pkt_time = jiffies;

	if (fh->fh_type == FC_TYPE_BLS) {
		fc_fcp_abts_resp(fsp, fp);
		goto unlock;
	}

	if (fsp->state & (FC_SRB_ABORTED | FC_SRB_ABORT_PENDING))
		goto unlock;

	if (r_ctl == FC_RCTL_DD_DATA_DESC) {
		/*
		 * received XFER RDY from the target
		 * need to send data to the target
		 */
		WARN_ON(fr_flags(fp) & FCPHF_CRC_UNCHECKED);
		dd = fc_frame_payload_get(fp, sizeof(*dd));
		WARN_ON(!dd);

		rc = fc_fcp_send_data(fsp, seq,
				      (size_t) ntohl(dd->ft_data_ro),
				      (size_t) ntohl(dd->ft_burst_len));
		if (!rc)
			seq->rec_data = fsp->xfer_len;
		else if (rc == -ENOMEM)
			fsp->state |= FC_SRB_NOMEM;
	} else if (r_ctl == FC_RCTL_DD_SOL_DATA) {
		/*
		 * received a DATA frame
		 * next we will copy the data to the system buffer
		 */
		WARN_ON(fr_len(fp) < sizeof(*fh));	/* len may be 0 */
		fc_fcp_recv_data(fsp, fp);
		seq->rec_data = fsp->xfer_contig_end;
	} else if (r_ctl == FC_RCTL_DD_CMD_STATUS) {
		WARN_ON(fr_flags(fp) & FCPHF_CRC_UNCHECKED);

		fc_fcp_resp(fsp, fp);
	} else {
		FC_DBG("unexpected frame.  r_ctl %x\n", r_ctl);
	}
unlock:
	fc_fcp_unlock_pkt(fsp);
out:
	fc_frame_free(fp);
errout:
	if (IS_ERR(fp))
		fc_fcp_error(fsp, fp);
	else if (rc == -ENOMEM)
		fc_fcp_reduce_can_queue(lp);
}

static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
	struct fc_frame_header *fh;
	struct fcp_resp *fc_rp;
	struct fcp_resp_ext *rp_ex;
	struct fcp_resp_rsp_info *fc_rp_info;
	u32 plen;
	u32 expected_len;
	u32 respl = 0;
	u32 snsl = 0;
	u8 flags = 0;

	plen = fr_len(fp);
	fh = (struct fc_frame_header *)fr_hdr(fp);
	if (unlikely(plen < sizeof(*fh) + sizeof(*fc_rp)))
		goto len_err;
	plen -= sizeof(*fh);
	fc_rp = (struct fcp_resp *)(fh + 1);
	fsp->cdb_status = fc_rp->fr_status;
	flags = fc_rp->fr_flags;
	fsp->scsi_comp_flags = flags;
	expected_len = fsp->data_len;

	if (unlikely((flags & ~FCP_CONF_REQ) || fc_rp->fr_status)) {
		rp_ex = (void *)(fc_rp + 1);
		if (flags & (FCP_RSP_LEN_VAL | FCP_SNS_LEN_VAL)) {
			if (plen < sizeof(*fc_rp) + sizeof(*rp_ex))
				goto len_err;
			fc_rp_info = (struct fcp_resp_rsp_info *)(rp_ex + 1);
			if (flags & FCP_RSP_LEN_VAL) {
				respl = ntohl(rp_ex->fr_rsp_len);
				if (respl != sizeof(*fc_rp_info))
					goto len_err;
				if (fsp->wait_for_comp) {
					/* Abuse cdb_status for rsp code */
					fsp->cdb_status = fc_rp_info->rsp_code;
					complete(&fsp->tm_done);
					/*
					 * tmfs will not have any scsi cmd so
					 * exit here
					 */
					return;
				} else
					goto err;
			}
			if (flags & FCP_SNS_LEN_VAL) {
				snsl = ntohl(rp_ex->fr_sns_len);
				if (snsl > SCSI_SENSE_BUFFERSIZE)
					snsl = SCSI_SENSE_BUFFERSIZE;
				memcpy(fsp->cmd->sense_buffer,
				       (char *)fc_rp_info + respl, snsl);
			}
		}
		if (flags & (FCP_RESID_UNDER | FCP_RESID_OVER)) {
			if (plen < sizeof(*fc_rp) + sizeof(rp_ex->fr_resid))
				goto len_err;
			if (flags & FCP_RESID_UNDER) {
				fsp->scsi_resid = ntohl(rp_ex->fr_resid);
				/*
				 * The cmnd->underflow is the minimum number of
				 * bytes that must be transfered for this
				 * command.  Provided a sense condition is not
				 * present, make sure the actual amount
				 * transferred is at least the underflow value
				 * or fail.
				 */
				if (!(flags & FCP_SNS_LEN_VAL) &&
				    (fc_rp->fr_status == 0) &&
				    (scsi_bufflen(fsp->cmd) -
				     fsp->scsi_resid) < fsp->cmd->underflow)
					goto err;
				expected_len -= fsp->scsi_resid;
			} else {
				fsp->status_code = FC_ERROR;
			}
		}
	}
	fsp->state |= FC_SRB_RCV_STATUS;

	/*
	 * Check for missing or extra data frames.
	 */
	if (unlikely(fsp->xfer_len != expected_len)) {
		if (fsp->xfer_len < expected_len) {
			/*
			 * Some data may be queued locally,
			 * Wait a at least one jiffy to see if it is delivered.
			 * If this expires without data, we may do SRR.
			 */
			fc_fcp_timer_set(fsp, 2);
			return;
		}
		fsp->status_code = FC_DATA_OVRRUN;
		FC_DBG("tgt %6x xfer len %zx greater than expected len %x. "
		       "data len %x\n",
		       fsp->rport->port_id,
		       fsp->xfer_len, expected_len, fsp->data_len);
	}
	fc_fcp_complete_locked(fsp);
	return;

len_err:
	FC_DBG("short FCP response. flags 0x%x len %u respl %u snsl %u\n",
	       flags, fr_len(fp), respl, snsl);
err:
	fsp->status_code = FC_ERROR;
	fc_fcp_complete_locked(fsp);
}

/**
 * fc_fcp_complete_locked() - complete processing of a fcp packet
 * @fsp:	fcp packet
 *
 * This function may sleep if a timer is pending. The packet lock must be
 * held, and the host lock must not be held.
 */
static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
{
	struct fc_lport *lp = fsp->lp;
	struct fc_seq *seq;
	struct fc_exch *ep;
	u32 f_ctl;

	if (fsp->state & FC_SRB_ABORT_PENDING)
		return;

	if (fsp->state & FC_SRB_ABORTED) {
		if (!fsp->status_code)
			fsp->status_code = FC_CMD_ABORTED;
	} else {
		/*
		 * Test for transport underrun, independent of response
		 * underrun status.
		 */
		if (fsp->xfer_len < fsp->data_len && !fsp->io_status &&
		    (!(fsp->scsi_comp_flags & FCP_RESID_UNDER) ||
		     fsp->xfer_len < fsp->data_len - fsp->scsi_resid)) {
			fsp->status_code = FC_DATA_UNDRUN;
			fsp->io_status = 0;
		}
	}

	seq = fsp->seq_ptr;
	if (seq) {
		fsp->seq_ptr = NULL;
		if (unlikely(fsp->scsi_comp_flags & FCP_CONF_REQ)) {
			struct fc_frame *conf_frame;
			struct fc_seq *csp;

			csp = lp->tt.seq_start_next(seq);
			conf_frame = fc_frame_alloc(fsp->lp, 0);
			if (conf_frame) {
				f_ctl = FC_FC_SEQ_INIT;
				f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ;
				ep = fc_seq_exch(seq);
				fc_fill_fc_hdr(conf_frame, FC_RCTL_DD_SOL_CTL,
					       ep->did, ep->sid,
					       FC_TYPE_FCP, f_ctl, 0);
				lp->tt.seq_send(lp, csp, conf_frame);
			}
		}
		lp->tt.exch_done(seq);
	}
	fc_io_compl(fsp);
}

static void fc_fcp_cleanup_cmd(struct fc_fcp_pkt *fsp, int error)
{
	struct fc_lport *lp = fsp->lp;

	if (fsp->seq_ptr) {
		lp->tt.exch_done(fsp->seq_ptr);
		fsp->seq_ptr = NULL;
	}
	fsp->status_code = error;
}

/**
 * fc_fcp_cleanup_each_cmd() - Cleanup active commads
 * @lp:		logical port
 * @id:		target id
 * @lun:	lun
 * @error:	fsp status code
 *
 * If lun or id is -1, they are ignored.
 */
static void fc_fcp_cleanup_each_cmd(struct fc_lport *lp, unsigned int id,
				    unsigned int lun, int error)
{
	struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
	struct fc_fcp_pkt *fsp;
	struct scsi_cmnd *sc_cmd;
	unsigned long flags;

	spin_lock_irqsave(lp->host->host_lock, flags);
restart:
	list_for_each_entry(fsp, &si->scsi_pkt_queue, list) {
		sc_cmd = fsp->cmd;
		if (id != -1 && scmd_id(sc_cmd) != id)
			continue;

		if (lun != -1 && sc_cmd->device->lun != lun)
			continue;

		fc_fcp_pkt_hold(fsp);
		spin_unlock_irqrestore(lp->host->host_lock, flags);

		if (!fc_fcp_lock_pkt(fsp)) {
			fc_fcp_cleanup_cmd(fsp, error);
			fc_io_compl(fsp);
			fc_fcp_unlock_pkt(fsp);
		}

		fc_fcp_pkt_release(fsp);
		spin_lock_irqsave(lp->host->host_lock, flags);
		/*
		 * while we dropped the lock multiple pkts could
		 * have been released, so we have to start over.
		 */
		goto restart;
	}
	spin_unlock_irqrestore(lp->host->host_lock, flags);
}

static void fc_fcp_abort_io(struct fc_lport *lp)
{
	fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_HRD_ERROR);
}

/**
 * fc_fcp_pkt_send() - send a fcp packet to the lower level.
 * @lp:		fc lport
 * @fsp:	fc packet.
 *
 * This is called by upper layer protocol.
 * Return   : zero for success and -1 for failure
 * Context  : called from queuecommand which can be called from process
 *	      or scsi soft irq.
 * Locks    : called with the host lock and irqs disabled.
 */
static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
{
	struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
	int rc;

	fsp->cmd->SCp.ptr = (char *)fsp;
	fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
	fsp->cdb_cmd.fc_flags = fsp->req_flags & ~FCP_CFL_LEN_MASK;

	int_to_scsilun(fsp->cmd->device->lun,
		       (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
	memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len);
	list_add_tail(&fsp->list, &si->scsi_pkt_queue);

	spin_unlock_irq(lp->host->host_lock);
	rc = lp->tt.fcp_cmd_send(lp, fsp, fc_fcp_recv);
	spin_lock_irq(lp->host->host_lock);
	if (rc)
		list_del(&fsp->list);

	return rc;
}

static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
			   void (*resp)(struct fc_seq *,
					struct fc_frame *fp,
					void *arg))
{
	struct fc_frame *fp;
	struct fc_seq *seq;
	struct fc_rport *rport;
	struct fc_rport_libfc_priv *rp;
	const size_t len = sizeof(fsp->cdb_cmd);
	int rc = 0;

	if (fc_fcp_lock_pkt(fsp))
		return 0;

	fp = fc_frame_alloc(lp, sizeof(fsp->cdb_cmd));
	if (!fp) {
		rc = -1;
		goto unlock;
	}

	memcpy(fc_frame_payload_get(fp, len), &fsp->cdb_cmd, len);
	fr_cmd(fp) = fsp->cmd;
	rport = fsp->rport;
	fsp->max_payload = rport->maxframe_size;
	rp = rport->dd_data;

	fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id,
		       fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);

	seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0);
	if (!seq) {
		fc_frame_free(fp);
		rc = -1;
		goto unlock;
	}
	fsp->last_pkt_time = jiffies;
	fsp->seq_ptr = seq;
	fc_fcp_pkt_hold(fsp);	/* hold for fc_fcp_pkt_destroy */

	setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp);
	fc_fcp_timer_set(fsp,
			 (fsp->tgt_flags & FC_RP_FLAGS_REC_SUPPORTED) ?
			 FC_SCSI_REC_TOV : FC_SCSI_ER_TIMEOUT);
unlock:
	fc_fcp_unlock_pkt(fsp);
	return rc;
}

/*
 * transport error handler
 */
static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
	int error = PTR_ERR(fp);

	if (fc_fcp_lock_pkt(fsp))
		return;

	switch (error) {
	case -FC_EX_CLOSED:
		fc_fcp_retry_cmd(fsp);
		goto unlock;
	default:
		FC_DBG("unknown error %ld\n", PTR_ERR(fp));
	}
	/*
	 * clear abort pending, because the lower layer
	 * decided to force completion.
	 */
	fsp->state &= ~FC_SRB_ABORT_PENDING;
	fsp->status_code = FC_CMD_PLOGO;
	fc_fcp_complete_locked(fsp);
unlock:
	fc_fcp_unlock_pkt(fsp);
}

/*
 * Scsi abort handler- calls to send an abort
 * and then wait for abort completion
 */
static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
{
	int rc = FAILED;

	if (fc_fcp_send_abort(fsp))
		return FAILED;

	init_completion(&fsp->tm_done);
	fsp->wait_for_comp = 1;

	spin_unlock_bh(&fsp->scsi_pkt_lock);
	rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV);
	spin_lock_bh(&fsp->scsi_pkt_lock);
	fsp->wait_for_comp = 0;

	if (!rc) {
		FC_DBG("target abort cmd  failed\n");
		rc = FAILED;
	} else if (fsp->state & FC_SRB_ABORTED) {
		FC_DBG("target abort cmd  passed\n");
		rc = SUCCESS;
		fc_fcp_complete_locked(fsp);
	}

	return rc;
}

/*
 * Retry LUN reset after resource allocation failed.
 */
static void fc_lun_reset_send(unsigned long data)
{
	struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
	struct fc_lport *lp = fsp->lp;
	if (lp->tt.fcp_cmd_send(lp, fsp, fc_tm_done)) {
		if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)
			return;
		if (fc_fcp_lock_pkt(fsp))
			return;
		setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp);
		fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
		fc_fcp_unlock_pkt(fsp);
	}
}

/*
 * Scsi device reset handler- send a LUN RESET to the device
 * and wait for reset reply
 */
static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
			unsigned int id, unsigned int lun)
{
	int rc;

	fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
	fsp->cdb_cmd.fc_tm_flags = FCP_TMF_LUN_RESET;
	int_to_scsilun(lun, (struct scsi_lun *)fsp->cdb_cmd.fc_lun);

	fsp->wait_for_comp = 1;
	init_completion(&fsp->tm_done);

	fc_lun_reset_send((unsigned long)fsp);

	/*
	 * wait for completion of reset
	 * after that make sure all commands are terminated
	 */
	rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV);

	spin_lock_bh(&fsp->scsi_pkt_lock);
	fsp->state |= FC_SRB_COMPL;
	spin_unlock_bh(&fsp->scsi_pkt_lock);

	del_timer_sync(&fsp->timer);

	spin_lock_bh(&fsp->scsi_pkt_lock);
	if (fsp->seq_ptr) {
		lp->tt.exch_done(fsp->seq_ptr);
		fsp->seq_ptr = NULL;
	}
	fsp->wait_for_comp = 0;
	spin_unlock_bh(&fsp->scsi_pkt_lock);

	if (!rc) {
		FC_DBG("lun reset failed\n");
		return FAILED;
	}

	/* cdb_status holds the tmf's rsp code */
	if (fsp->cdb_status != FCP_TMF_CMPL)
		return FAILED;

	FC_DBG("lun reset to lun %u completed\n", lun);
	fc_fcp_cleanup_each_cmd(lp, id, lun, FC_CMD_ABORTED);
	return SUCCESS;
}

/*
 * Task Managment response handler
 */
static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
	struct fc_fcp_pkt *fsp = arg;
	struct fc_frame_header *fh;

	if (IS_ERR(fp)) {
		/*
		 * If there is an error just let it timeout or wait
		 * for TMF to be aborted if it timedout.
		 *
		 * scsi-eh will escalate for when either happens.
		 */
		return;
	}

	if (fc_fcp_lock_pkt(fsp))
		return;

	/*
	 * raced with eh timeout handler.
	 */
	if (!fsp->seq_ptr || !fsp->wait_for_comp) {
		spin_unlock_bh(&fsp->scsi_pkt_lock);
		return;
	}

	fh = fc_frame_header_get(fp);
	if (fh->fh_type != FC_TYPE_BLS)
		fc_fcp_resp(fsp, fp);
	fsp->seq_ptr = NULL;
	fsp->lp->tt.exch_done(seq);
	fc_frame_free(fp);
	fc_fcp_unlock_pkt(fsp);
}

static void fc_fcp_cleanup(struct fc_lport *lp)
{
	fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_ERROR);
}

/*
 * fc_fcp_timeout: called by OS timer function.
 *
 * The timer has been inactivated and must be reactivated if desired
 * using fc_fcp_timer_set().
 *
 * Algorithm:
 *
 * If REC is supported, just issue it, and return.  The REC exchange will
 * complete or time out, and recovery can continue at that point.
 *
 * Otherwise, if the response has been received without all the data,
 * it has been ER_TIMEOUT since the response was received.
 *
 * If the response has not been received,
 * we see if data was received recently.  If it has been, we continue waiting,
 * otherwise, we abort the command.
 */
static void fc_fcp_timeout(unsigned long data)
{
	struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
	struct fc_rport *rport = fsp->rport;
	struct fc_rport_libfc_priv *rp = rport->dd_data;

	if (fc_fcp_lock_pkt(fsp))
		return;

	if (fsp->cdb_cmd.fc_tm_flags)
		goto unlock;

	fsp->state |= FC_SRB_FCP_PROCESSING_TMO;

	if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED)
		fc_fcp_rec(fsp);
	else if (time_after_eq(fsp->last_pkt_time + (FC_SCSI_ER_TIMEOUT / 2),
			       jiffies))
		fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
	else if (fsp->state & FC_SRB_RCV_STATUS)
		fc_fcp_complete_locked(fsp);
	else
		fc_timeout_error(fsp);
	fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO;
unlock:
	fc_fcp_unlock_pkt(fsp);
}

/*
 * Send a REC ELS request
 */
static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
{
	struct fc_lport *lp;
	struct fc_frame *fp;
	struct fc_rport *rport;
	struct fc_rport_libfc_priv *rp;

	lp = fsp->lp;
	rport = fsp->rport;
	rp = rport->dd_data;
	if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) {
		fsp->status_code = FC_HRD_ERROR;
		fsp->io_status = 0;
		fc_fcp_complete_locked(fsp);
		return;
	}
	fp = fc_frame_alloc(lp, sizeof(struct fc_els_rec));
	if (!fp)
		goto retry;

	fr_seq(fp) = fsp->seq_ptr;
	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id,
		       fc_host_port_id(rp->local_port->host), FC_TYPE_ELS,
		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
	if (lp->tt.elsct_send(lp, rport, fp, ELS_REC, fc_fcp_rec_resp,
			      fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) {
		fc_fcp_pkt_hold(fsp);		/* hold while REC outstanding */
		return;
	}
	fc_frame_free(fp);
retry:
	if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
		fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
	else
		fc_timeout_error(fsp);
}

/*
 * Receive handler for REC ELS frame
 * if it is a reject then let the scsi layer to handle
 * the timeout. if it is a LS_ACC then if the io was not completed
 * then set the timeout and return otherwise complete the exchange
 * and tell the scsi layer to restart the I/O.
 */
static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
	struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
	struct fc_els_rec_acc *recp;
	struct fc_els_ls_rjt *rjt;
	u32 e_stat;
	u8 opcode;
	u32 offset;
	enum dma_data_direction data_dir;
	enum fc_rctl r_ctl;
	struct fc_rport_libfc_priv *rp;

	if (IS_ERR(fp)) {
		fc_fcp_rec_error(fsp, fp);
		return;
	}

	if (fc_fcp_lock_pkt(fsp))
		goto out;

	fsp->recov_retry = 0;
	opcode = fc_frame_payload_op(fp);
	if (opcode == ELS_LS_RJT) {
		rjt = fc_frame_payload_get(fp, sizeof(*rjt));
		switch (rjt->er_reason) {
		default:
			FC_DEBUG_FCP("device %x unexpected REC reject "
				     "reason %d expl %d\n",
				     fsp->rport->port_id, rjt->er_reason,
				     rjt->er_explan);
			/* fall through */
		case ELS_RJT_UNSUP:
			FC_DEBUG_FCP("device does not support REC\n");
			rp = fsp->rport->dd_data;
			/*
			 * if we do not spport RECs or got some bogus
			 * reason then resetup timer so we check for
			 * making progress.
			 */
			rp->flags &= ~FC_RP_FLAGS_REC_SUPPORTED;
			fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
			break;
		case ELS_RJT_LOGIC:
		case ELS_RJT_UNAB:
			/*
			 * If no data transfer, the command frame got dropped
			 * so we just retry.  If data was transferred, we
			 * lost the response but the target has no record,
			 * so we abort and retry.
			 */
			if (rjt->er_explan == ELS_EXPL_OXID_RXID &&
			    fsp->xfer_len == 0) {
				fc_fcp_retry_cmd(fsp);
				break;
			}
			fc_timeout_error(fsp);
			break;
		}
	} else if (opcode == ELS_LS_ACC) {
		if (fsp->state & FC_SRB_ABORTED)
			goto unlock_out;

		data_dir = fsp->cmd->sc_data_direction;
		recp = fc_frame_payload_get(fp, sizeof(*recp));
		offset = ntohl(recp->reca_fc4value);
		e_stat = ntohl(recp->reca_e_stat);

		if (e_stat & ESB_ST_COMPLETE) {

			/*
			 * The exchange is complete.
			 *
			 * For output, we must've lost the response.
			 * For input, all data must've been sent.
			 * We lost may have lost the response
			 * (and a confirmation was requested) and maybe
			 * some data.
			 *
			 * If all data received, send SRR
			 * asking for response.	 If partial data received,
			 * or gaps, SRR requests data at start of gap.
			 * Recovery via SRR relies on in-order-delivery.
			 */
			if (data_dir == DMA_TO_DEVICE) {
				r_ctl = FC_RCTL_DD_CMD_STATUS;
			} else if (fsp->xfer_contig_end == offset) {
				r_ctl = FC_RCTL_DD_CMD_STATUS;
			} else {
				offset = fsp->xfer_contig_end;
				r_ctl = FC_RCTL_DD_SOL_DATA;
			}
			fc_fcp_srr(fsp, r_ctl, offset);
		} else if (e_stat & ESB_ST_SEQ_INIT) {

			/*
			 * The remote port has the initiative, so just
			 * keep waiting for it to complete.
			 */
			fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
		} else {

			/*
			 * The exchange is incomplete, we have seq. initiative.
			 * Lost response with requested confirmation,
			 * lost confirmation, lost transfer ready or
			 * lost write data.
			 *
			 * For output, if not all data was received, ask
			 * for transfer ready to be repeated.
			 *
			 * If we received or sent all the data, send SRR to
			 * request response.
			 *
			 * If we lost a response, we may have lost some read
			 * data as well.
			 */
			r_ctl = FC_RCTL_DD_SOL_DATA;
			if (data_dir == DMA_TO_DEVICE) {
				r_ctl = FC_RCTL_DD_CMD_STATUS;
				if (offset < fsp->data_len)
					r_ctl = FC_RCTL_DD_DATA_DESC;
			} else if (offset == fsp->xfer_contig_end) {
				r_ctl = FC_RCTL_DD_CMD_STATUS;
			} else if (fsp->xfer_contig_end < offset) {
				offset = fsp->xfer_contig_end;
			}
			fc_fcp_srr(fsp, r_ctl, offset);
		}
	}
unlock_out:
	fc_fcp_unlock_pkt(fsp);
out:
	fc_fcp_pkt_release(fsp);	/* drop hold for outstanding REC */
	fc_frame_free(fp);
}

/*
 * Handle error response or timeout for REC exchange.
 */
static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
	int error = PTR_ERR(fp);

	if (fc_fcp_lock_pkt(fsp))
		goto out;

	switch (error) {
	case -FC_EX_CLOSED:
		fc_fcp_retry_cmd(fsp);
		break;

	default:
		FC_DBG("REC %p fid %x error unexpected error %d\n",
		       fsp, fsp->rport->port_id, error);
		fsp->status_code = FC_CMD_PLOGO;
		/* fall through */

	case -FC_EX_TIMEOUT:
		/*
		 * Assume REC or LS_ACC was lost.
		 * The exchange manager will have aborted REC, so retry.
		 */
		FC_DBG("REC fid %x error error %d retry %d/%d\n",
		       fsp->rport->port_id, error, fsp->recov_retry,
		       FC_MAX_RECOV_RETRY);
		if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
			fc_fcp_rec(fsp);
		else
			fc_timeout_error(fsp);
		break;
	}
	fc_fcp_unlock_pkt(fsp);
out:
	fc_fcp_pkt_release(fsp);	/* drop hold for outstanding REC */
}

/*
 * Time out error routine:
 * abort's the I/O close the exchange and
 * send completion notification to scsi layer
 */
static void fc_timeout_error(struct fc_fcp_pkt *fsp)
{
	fsp->status_code = FC_CMD_TIME_OUT;
	fsp->cdb_status = 0;
	fsp->io_status = 0;
	/*
	 * if this fails then we let the scsi command timer fire and
	 * scsi-ml escalate.
	 */
	fc_fcp_send_abort(fsp);
}

/*
 * Sequence retransmission request.
 * This is called after receiving status but insufficient data, or
 * when expecting status but the request has timed out.
 */
static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
{
	struct fc_lport *lp = fsp->lp;
	struct fc_rport *rport;
	struct fc_rport_libfc_priv *rp;
	struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr);
	struct fc_seq *seq;
	struct fcp_srr *srr;
	struct fc_frame *fp;
	u8 cdb_op;

	rport = fsp->rport;
	rp = rport->dd_data;
	cdb_op = fsp->cdb_cmd.fc_cdb[0];

	if (!(rp->flags & FC_RP_FLAGS_RETRY) || rp->rp_state != RPORT_ST_READY)
		goto retry;			/* shouldn't happen */
	fp = fc_frame_alloc(lp, sizeof(*srr));
	if (!fp)
		goto retry;

	srr = fc_frame_payload_get(fp, sizeof(*srr));
	memset(srr, 0, sizeof(*srr));
	srr->srr_op = ELS_SRR;
	srr->srr_ox_id = htons(ep->oxid);
	srr->srr_rx_id = htons(ep->rxid);
	srr->srr_r_ctl = r_ctl;
	srr->srr_rel_off = htonl(offset);

	fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id,
		       fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);

	seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL,
				   fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
	if (!seq) {
		fc_frame_free(fp);
		goto retry;
	}
	fsp->recov_seq = seq;
	fsp->xfer_len = offset;
	fsp->xfer_contig_end = offset;
	fsp->state &= ~FC_SRB_RCV_STATUS;
	fc_fcp_pkt_hold(fsp);		/* hold for outstanding SRR */
	return;
retry:
	fc_fcp_retry_cmd(fsp);
}

/*
 * Handle response from SRR.
 */
static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
	struct fc_fcp_pkt *fsp = arg;
	struct fc_frame_header *fh;

	if (IS_ERR(fp)) {
		fc_fcp_srr_error(fsp, fp);
		return;
	}

	if (fc_fcp_lock_pkt(fsp))
		goto out;

	fh = fc_frame_header_get(fp);
	/*
	 * BUG? fc_fcp_srr_error calls exch_done which would release
	 * the ep. But if fc_fcp_srr_error had got -FC_EX_TIMEOUT,
	 * then fc_exch_timeout would be sending an abort. The exch_done
	 * call by fc_fcp_srr_error would prevent fc_exch.c from seeing
	 * an abort response though.
	 */
	if (fh->fh_type == FC_TYPE_BLS) {
		fc_fcp_unlock_pkt(fsp);
		return;
	}

	fsp->recov_seq = NULL;
	switch (fc_frame_payload_op(fp)) {
	case ELS_LS_ACC:
		fsp->recov_retry = 0;
		fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
		break;
	case ELS_LS_RJT:
	default:
		fc_timeout_error(fsp);
		break;
	}
	fc_fcp_unlock_pkt(fsp);
	fsp->lp->tt.exch_done(seq);
out:
	fc_frame_free(fp);
	fc_fcp_pkt_release(fsp);	/* drop hold for outstanding SRR */
}

static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
	if (fc_fcp_lock_pkt(fsp))
		goto out;
	fsp->lp->tt.exch_done(fsp->recov_seq);
	fsp->recov_seq = NULL;
	switch (PTR_ERR(fp)) {
	case -FC_EX_TIMEOUT:
		if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
			fc_fcp_rec(fsp);
		else
			fc_timeout_error(fsp);
		break;
	case -FC_EX_CLOSED:			/* e.g., link failure */
		/* fall through */
	default:
		fc_fcp_retry_cmd(fsp);
		break;
	}
	fc_fcp_unlock_pkt(fsp);
out:
	fc_fcp_pkt_release(fsp);	/* drop hold for outstanding SRR */
}

static inline int fc_fcp_lport_queue_ready(struct fc_lport *lp)
{
	/* lock ? */
	return (lp->state == LPORT_ST_READY) && lp->link_up && !lp->qfull;
}

/**
 * fc_queuecommand - The queuecommand function of the scsi template
 * @cmd:	struct scsi_cmnd to be executed
 * @done:	Callback function to be called when cmd is completed
 *
 * this is the i/o strategy routine, called by the scsi layer
 * this routine is called with holding the host_lock.
 */
int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
{
	struct fc_lport *lp;
	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
	struct fc_fcp_pkt *fsp;
	struct fc_rport_libfc_priv *rp;
	int rval;
	int rc = 0;
	struct fcoe_dev_stats *stats;

	lp = shost_priv(sc_cmd->device->host);

	rval = fc_remote_port_chkready(rport);
	if (rval) {
		sc_cmd->result = rval;
		done(sc_cmd);
		goto out;
	}

	if (!*(struct fc_remote_port **)rport->dd_data) {
		/*
		 * rport is transitioning from blocked/deleted to
		 * online
		 */
		sc_cmd->result = DID_IMM_RETRY << 16;
		done(sc_cmd);
		goto out;
	}

	rp = rport->dd_data;

	if (!fc_fcp_lport_queue_ready(lp)) {
		rc = SCSI_MLQUEUE_HOST_BUSY;
		goto out;
	}

	fsp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC);
	if (fsp == NULL) {
		rc = SCSI_MLQUEUE_HOST_BUSY;
		goto out;
	}

	/*
	 * build the libfc request pkt
	 */
	fsp->cmd = sc_cmd;	/* save the cmd */
	fsp->lp = lp;		/* save the softc ptr */
	fsp->rport = rport;	/* set the remote port ptr */
	sc_cmd->scsi_done = done;

	/*
	 * set up the transfer length
	 */
	fsp->data_len = scsi_bufflen(sc_cmd);
	fsp->xfer_len = 0;

	/*
	 * setup the data direction
	 */
	stats = lp->dev_stats[smp_processor_id()];
	if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
		fsp->req_flags = FC_SRB_READ;
		stats->InputRequests++;
		stats->InputMegabytes = fsp->data_len;
	} else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
		fsp->req_flags = FC_SRB_WRITE;
		stats->OutputRequests++;
		stats->OutputMegabytes = fsp->data_len;
	} else {
		fsp->req_flags = 0;
		stats->ControlRequests++;
	}

	fsp->tgt_flags = rp->flags;

	init_timer(&fsp->timer);
	fsp->timer.data = (unsigned long)fsp;

	/*
	 * send it to the lower layer
	 * if we get -1 return then put the request in the pending
	 * queue.
	 */
	rval = fc_fcp_pkt_send(lp, fsp);
	if (rval != 0) {
		fsp->state = FC_SRB_FREE;
		fc_fcp_pkt_release(fsp);
		rc = SCSI_MLQUEUE_HOST_BUSY;
	}
out:
	return rc;
}
EXPORT_SYMBOL(fc_queuecommand);

/**
 * fc_io_compl() -  Handle responses for completed commands
 * @fsp:	scsi packet
 *
 * Translates a error to a Linux SCSI error.
 *
 * The fcp packet lock must be held when calling.
 */
static void fc_io_compl(struct fc_fcp_pkt *fsp)
{
	struct fc_fcp_internal *si;
	struct scsi_cmnd *sc_cmd;
	struct fc_lport *lp;
	unsigned long flags;

	fsp->state |= FC_SRB_COMPL;
	if (!(fsp->state & FC_SRB_FCP_PROCESSING_TMO)) {
		spin_unlock_bh(&fsp->scsi_pkt_lock);
		del_timer_sync(&fsp->timer);
		spin_lock_bh(&fsp->scsi_pkt_lock);
	}

	lp = fsp->lp;
	si = fc_get_scsi_internal(lp);
	spin_lock_irqsave(lp->host->host_lock, flags);
	if (!fsp->cmd) {
		spin_unlock_irqrestore(lp->host->host_lock, flags);
		return;
	}

	/*
	 * if a command timed out while we had to try and throttle IO
	 * and it is now getting cleaned up, then we are about to
	 * try again so clear the throttled flag incase we get more
	 * time outs.
	 */
	if (si->throttled && fsp->state & FC_SRB_NOMEM)
		si->throttled = 0;

	sc_cmd = fsp->cmd;
	fsp->cmd = NULL;

	if (!sc_cmd->SCp.ptr) {
		spin_unlock_irqrestore(lp->host->host_lock, flags);
		return;
	}

	CMD_SCSI_STATUS(sc_cmd) = fsp->cdb_status;
	switch (fsp->status_code) {
	case FC_COMPLETE:
		if (fsp->cdb_status == 0) {
			/*
			 * good I/O status
			 */
			sc_cmd->result = DID_OK << 16;
			if (fsp->scsi_resid)
				CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
		} else if (fsp->cdb_status == QUEUE_FULL) {
			struct scsi_device *tmp_sdev;
			struct scsi_device *sdev = sc_cmd->device;

			shost_for_each_device(tmp_sdev, sdev->host) {
				if (tmp_sdev->id != sdev->id)
					continue;

				if (tmp_sdev->queue_depth > 1) {
					scsi_track_queue_full(tmp_sdev,
							      tmp_sdev->
							      queue_depth - 1);
				}
			}
			sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
		} else {
			/*
			 * transport level I/O was ok but scsi
			 * has non zero status
			 */
			sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
		}
		break;
	case FC_ERROR:
		sc_cmd->result = DID_ERROR << 16;
		break;
	case FC_DATA_UNDRUN:
		if ((fsp->cdb_status == 0) && !(fsp->req_flags & FC_SRB_READ)) {
			/*
			 * scsi status is good but transport level
			 * underrun.
			 */
			sc_cmd->result = DID_OK << 16;
		} else {
			/*
			 * scsi got underrun, this is an error
			 */
			CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
			sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
		}
		break;
	case FC_DATA_OVRRUN:
		/*
		 * overrun is an error
		 */
		sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
		break;
	case FC_CMD_ABORTED:
		sc_cmd->result = (DID_ABORT << 16) | fsp->io_status;
		break;
	case FC_CMD_TIME_OUT:
		sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status;
		break;
	case FC_CMD_RESET:
		sc_cmd->result = (DID_RESET << 16);
		break;
	case FC_HRD_ERROR:
		sc_cmd->result = (DID_NO_CONNECT << 16);
		break;
	default:
		sc_cmd->result = (DID_ERROR << 16);
		break;
	}

	list_del(&fsp->list);
	sc_cmd->SCp.ptr = NULL;
	sc_cmd->scsi_done(sc_cmd);
	spin_unlock_irqrestore(lp->host->host_lock, flags);

	/* release ref from initial allocation in queue command */
	fc_fcp_pkt_release(fsp);
}

/**
 * fc_fcp_complete() - complete processing of a fcp packet
 * @fsp:	fcp packet
 *
 * This function may sleep if a fsp timer is pending.
 * The host lock must not be held by caller.
 */
void fc_fcp_complete(struct fc_fcp_pkt *fsp)
{
	if (fc_fcp_lock_pkt(fsp))
		return;

	fc_fcp_complete_locked(fsp);
	fc_fcp_unlock_pkt(fsp);
}
EXPORT_SYMBOL(fc_fcp_complete);

/**
 * fc_eh_abort() - Abort a command
 * @sc_cmd:	scsi command to abort
 *
 * From scsi host template.
 * send ABTS to the target device  and wait for the response
 * sc_cmd is the pointer to the command to be aborted.
 */
int fc_eh_abort(struct scsi_cmnd *sc_cmd)
{
	struct fc_fcp_pkt *fsp;
	struct fc_lport *lp;
	int rc = FAILED;
	unsigned long flags;

	lp = shost_priv(sc_cmd->device->host);
	if (lp->state != LPORT_ST_READY)
		return rc;
	else if (!lp->link_up)
		return rc;

	spin_lock_irqsave(lp->host->host_lock, flags);
	fsp = CMD_SP(sc_cmd);
	if (!fsp) {
		/* command completed while scsi eh was setting up */
		spin_unlock_irqrestore(lp->host->host_lock, flags);
		return SUCCESS;
	}
	/* grab a ref so the fsp and sc_cmd cannot be relased from under us */
	fc_fcp_pkt_hold(fsp);
	spin_unlock_irqrestore(lp->host->host_lock, flags);

	if (fc_fcp_lock_pkt(fsp)) {
		/* completed while we were waiting for timer to be deleted */
		rc = SUCCESS;
		goto release_pkt;
	}

	rc = fc_fcp_pkt_abort(lp, fsp);
	fc_fcp_unlock_pkt(fsp);

release_pkt:
	fc_fcp_pkt_release(fsp);
	return rc;
}
EXPORT_SYMBOL(fc_eh_abort);

/**
 * fc_eh_device_reset() Reset a single LUN
 * @sc_cmd:	scsi command
 *
 * Set from scsi host template to send tm cmd to the target and wait for the
 * response.
 */
int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
{
	struct fc_lport *lp;
	struct fc_fcp_pkt *fsp;
	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
	int rc = FAILED;
	struct fc_rport_libfc_priv *rp;
	int rval;

	rval = fc_remote_port_chkready(rport);
	if (rval)
		goto out;

	rp = rport->dd_data;
	lp = shost_priv(sc_cmd->device->host);

	if (lp->state != LPORT_ST_READY)
		return rc;

	fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO);
	if (fsp == NULL) {
		FC_DBG("could not allocate scsi_pkt\n");
		sc_cmd->result = DID_NO_CONNECT << 16;
		goto out;
	}

	/*
	 * Build the libfc request pkt. Do not set the scsi cmnd, because
	 * the sc passed in is not setup for execution like when sent
	 * through the queuecommand callout.
	 */
	fsp->lp = lp;		/* save the softc ptr */
	fsp->rport = rport;	/* set the remote port ptr */

	/*
	 * flush outstanding commands
	 */
	rc = fc_lun_reset(lp, fsp, scmd_id(sc_cmd), sc_cmd->device->lun);
	fsp->state = FC_SRB_FREE;
	fc_fcp_pkt_release(fsp);

out:
	return rc;
}
EXPORT_SYMBOL(fc_eh_device_reset);

/**
 * fc_eh_host_reset() - The reset function will reset the ports on the host.
 * @sc_cmd:	scsi command
 */
int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)
{
	struct Scsi_Host *shost = sc_cmd->device->host;
	struct fc_lport *lp = shost_priv(shost);
	unsigned long wait_tmo;

	lp->tt.lport_reset(lp);
	wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT;
	while (!fc_fcp_lport_queue_ready(lp) && time_before(jiffies, wait_tmo))
		msleep(1000);

	if (fc_fcp_lport_queue_ready(lp)) {
		shost_printk(KERN_INFO, shost, "Host reset succeeded.\n");
		return SUCCESS;
	} else {
		shost_printk(KERN_INFO, shost, "Host reset failed. "
			     "lport not ready.\n");
		return FAILED;
	}
}
EXPORT_SYMBOL(fc_eh_host_reset);

/**
 * fc_slave_alloc() - configure queue depth
 * @sdev:	scsi device
 *
 * Configures queue depth based on host's cmd_per_len. If not set
 * then we use the libfc default.
 */
int fc_slave_alloc(struct scsi_device *sdev)
{
	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
	int queue_depth;

	if (!rport || fc_remote_port_chkready(rport))
		return -ENXIO;

	if (sdev->tagged_supported) {
		if (sdev->host->hostt->cmd_per_lun)
			queue_depth = sdev->host->hostt->cmd_per_lun;
		else
			queue_depth = FC_FCP_DFLT_QUEUE_DEPTH;
		scsi_activate_tcq(sdev, queue_depth);
	}
	return 0;
}
EXPORT_SYMBOL(fc_slave_alloc);

int fc_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
	scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
	return sdev->queue_depth;
}
EXPORT_SYMBOL(fc_change_queue_depth);

int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
{
	if (sdev->tagged_supported) {
		scsi_set_tag_type(sdev, tag_type);
		if (tag_type)
			scsi_activate_tcq(sdev, sdev->queue_depth);
		else
			scsi_deactivate_tcq(sdev, sdev->queue_depth);
	} else
		tag_type = 0;

	return tag_type;
}
EXPORT_SYMBOL(fc_change_queue_type);

void fc_fcp_destroy(struct fc_lport *lp)
{
	struct fc_fcp_internal *si = fc_get_scsi_internal(lp);

	if (!list_empty(&si->scsi_pkt_queue))
		printk(KERN_ERR "Leaked scsi packets.\n");

	mempool_destroy(si->scsi_pkt_pool);
	kfree(si);
	lp->scsi_priv = NULL;
}
EXPORT_SYMBOL(fc_fcp_destroy);

int fc_fcp_init(struct fc_lport *lp)
{
	int rc;
	struct fc_fcp_internal *si;

	if (!lp->tt.fcp_cmd_send)
		lp->tt.fcp_cmd_send = fc_fcp_cmd_send;

	if (!lp->tt.fcp_cleanup)
		lp->tt.fcp_cleanup = fc_fcp_cleanup;

	if (!lp->tt.fcp_abort_io)
		lp->tt.fcp_abort_io = fc_fcp_abort_io;

	si = kzalloc(sizeof(struct fc_fcp_internal), GFP_KERNEL);
	if (!si)
		return -ENOMEM;
	lp->scsi_priv = si;
	INIT_LIST_HEAD(&si->scsi_pkt_queue);

	si->scsi_pkt_pool = mempool_create_slab_pool(2, scsi_pkt_cachep);
	if (!si->scsi_pkt_pool) {
		rc = -ENOMEM;
		goto free_internal;
	}
	return 0;

free_internal:
	kfree(si);
	return rc;
}
EXPORT_SYMBOL(fc_fcp_init);

static int __init libfc_init(void)
{
	int rc;

	scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt",
					    sizeof(struct fc_fcp_pkt),
					    0, SLAB_HWCACHE_ALIGN, NULL);
	if (scsi_pkt_cachep == NULL) {
		FC_DBG("Unable to allocate SRB cache...module load failed!");
		return -ENOMEM;
	}

	rc = fc_setup_exch_mgr();
	if (rc)
		goto destroy_pkt_cache;

	rc = fc_setup_rport();
	if (rc)
		goto destroy_em;

	return rc;
destroy_em:
	fc_destroy_exch_mgr();
destroy_pkt_cache:
	kmem_cache_destroy(scsi_pkt_cachep);
	return rc;
}

static void __exit libfc_exit(void)
{
	kmem_cache_destroy(scsi_pkt_cachep);
	fc_destroy_exch_mgr();
	fc_destroy_rport();
}

module_init(libfc_init);
module_exit(libfc_exit);