Merge branch '3.1-rc-fixes' of git://linux-iscsi.org/target-pending
* '3.1-rc-fixes' of git://linux-iscsi.org/target-pending: iscsi-target: Fix sendpage breakage with proper padding+DataDigest iovec offsets iscsi-target: Disable markers + remove dangerous local scope array usage target: Skip non hex characters for VPD=0x83 NAA IEEE Registered Extended tcm_fc: Work queue based approach instead of managing own thread and event based mechanism tcm_fc: Invalidation of DDP context for FCoE target in error conditions target: Fix race between multiple invocations of target_qf_do_work()
This commit is contained in:
Коммит
4be0ed42c5
|
@ -1430,7 +1430,7 @@ static int iscsi_enforce_integrity_rules(
|
||||||
u8 DataSequenceInOrder = 0;
|
u8 DataSequenceInOrder = 0;
|
||||||
u8 ErrorRecoveryLevel = 0, SessionType = 0;
|
u8 ErrorRecoveryLevel = 0, SessionType = 0;
|
||||||
u8 IFMarker = 0, OFMarker = 0;
|
u8 IFMarker = 0, OFMarker = 0;
|
||||||
u8 IFMarkInt_Reject = 0, OFMarkInt_Reject = 0;
|
u8 IFMarkInt_Reject = 1, OFMarkInt_Reject = 1;
|
||||||
u32 FirstBurstLength = 0, MaxBurstLength = 0;
|
u32 FirstBurstLength = 0, MaxBurstLength = 0;
|
||||||
struct iscsi_param *param = NULL;
|
struct iscsi_param *param = NULL;
|
||||||
|
|
||||||
|
|
|
@ -874,40 +874,6 @@ void iscsit_inc_session_usage_count(struct iscsi_session *sess)
|
||||||
spin_unlock_bh(&sess->session_usage_lock);
|
spin_unlock_bh(&sess->session_usage_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Used before iscsi_do[rx,tx]_data() to determine iov and [rx,tx]_marker
|
|
||||||
* array counts needed for sync and steering.
|
|
||||||
*/
|
|
||||||
static int iscsit_determine_sync_and_steering_counts(
|
|
||||||
struct iscsi_conn *conn,
|
|
||||||
struct iscsi_data_count *count)
|
|
||||||
{
|
|
||||||
u32 length = count->data_length;
|
|
||||||
u32 marker, markint;
|
|
||||||
|
|
||||||
count->sync_and_steering = 1;
|
|
||||||
|
|
||||||
marker = (count->type == ISCSI_RX_DATA) ?
|
|
||||||
conn->of_marker : conn->if_marker;
|
|
||||||
markint = (count->type == ISCSI_RX_DATA) ?
|
|
||||||
(conn->conn_ops->OFMarkInt * 4) :
|
|
||||||
(conn->conn_ops->IFMarkInt * 4);
|
|
||||||
count->ss_iov_count = count->iov_count;
|
|
||||||
|
|
||||||
while (length > 0) {
|
|
||||||
if (length >= marker) {
|
|
||||||
count->ss_iov_count += 3;
|
|
||||||
count->ss_marker_count += 2;
|
|
||||||
|
|
||||||
length -= marker;
|
|
||||||
marker = markint;
|
|
||||||
} else
|
|
||||||
length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup conn->if_marker and conn->of_marker values based upon
|
* Setup conn->if_marker and conn->of_marker values based upon
|
||||||
* the initial marker-less interval. (see iSCSI v19 A.2)
|
* the initial marker-less interval. (see iSCSI v19 A.2)
|
||||||
|
@ -1290,7 +1256,7 @@ int iscsit_fe_sendpage_sg(
|
||||||
struct kvec iov;
|
struct kvec iov;
|
||||||
u32 tx_hdr_size, data_len;
|
u32 tx_hdr_size, data_len;
|
||||||
u32 offset = cmd->first_data_sg_off;
|
u32 offset = cmd->first_data_sg_off;
|
||||||
int tx_sent;
|
int tx_sent, iov_off;
|
||||||
|
|
||||||
send_hdr:
|
send_hdr:
|
||||||
tx_hdr_size = ISCSI_HDR_LEN;
|
tx_hdr_size = ISCSI_HDR_LEN;
|
||||||
|
@ -1310,9 +1276,19 @@ send_hdr:
|
||||||
}
|
}
|
||||||
|
|
||||||
data_len = cmd->tx_size - tx_hdr_size - cmd->padding;
|
data_len = cmd->tx_size - tx_hdr_size - cmd->padding;
|
||||||
if (conn->conn_ops->DataDigest)
|
/*
|
||||||
|
* Set iov_off used by padding and data digest tx_data() calls below
|
||||||
|
* in order to determine proper offset into cmd->iov_data[]
|
||||||
|
*/
|
||||||
|
if (conn->conn_ops->DataDigest) {
|
||||||
data_len -= ISCSI_CRC_LEN;
|
data_len -= ISCSI_CRC_LEN;
|
||||||
|
if (cmd->padding)
|
||||||
|
iov_off = (cmd->iov_data_count - 2);
|
||||||
|
else
|
||||||
|
iov_off = (cmd->iov_data_count - 1);
|
||||||
|
} else {
|
||||||
|
iov_off = (cmd->iov_data_count - 1);
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Perform sendpage() for each page in the scatterlist
|
* Perform sendpage() for each page in the scatterlist
|
||||||
*/
|
*/
|
||||||
|
@ -1341,8 +1317,7 @@ send_pg:
|
||||||
|
|
||||||
send_padding:
|
send_padding:
|
||||||
if (cmd->padding) {
|
if (cmd->padding) {
|
||||||
struct kvec *iov_p =
|
struct kvec *iov_p = &cmd->iov_data[iov_off++];
|
||||||
&cmd->iov_data[cmd->iov_data_count-1];
|
|
||||||
|
|
||||||
tx_sent = tx_data(conn, iov_p, 1, cmd->padding);
|
tx_sent = tx_data(conn, iov_p, 1, cmd->padding);
|
||||||
if (cmd->padding != tx_sent) {
|
if (cmd->padding != tx_sent) {
|
||||||
|
@ -1356,8 +1331,7 @@ send_padding:
|
||||||
|
|
||||||
send_datacrc:
|
send_datacrc:
|
||||||
if (conn->conn_ops->DataDigest) {
|
if (conn->conn_ops->DataDigest) {
|
||||||
struct kvec *iov_d =
|
struct kvec *iov_d = &cmd->iov_data[iov_off];
|
||||||
&cmd->iov_data[cmd->iov_data_count];
|
|
||||||
|
|
||||||
tx_sent = tx_data(conn, iov_d, 1, ISCSI_CRC_LEN);
|
tx_sent = tx_data(conn, iov_d, 1, ISCSI_CRC_LEN);
|
||||||
if (ISCSI_CRC_LEN != tx_sent) {
|
if (ISCSI_CRC_LEN != tx_sent) {
|
||||||
|
@ -1431,8 +1405,7 @@ static int iscsit_do_rx_data(
|
||||||
struct iscsi_data_count *count)
|
struct iscsi_data_count *count)
|
||||||
{
|
{
|
||||||
int data = count->data_length, rx_loop = 0, total_rx = 0, iov_len;
|
int data = count->data_length, rx_loop = 0, total_rx = 0, iov_len;
|
||||||
u32 rx_marker_val[count->ss_marker_count], rx_marker_iov = 0;
|
struct kvec *iov_p;
|
||||||
struct kvec iov[count->ss_iov_count], *iov_p;
|
|
||||||
struct msghdr msg;
|
struct msghdr msg;
|
||||||
|
|
||||||
if (!conn || !conn->sock || !conn->conn_ops)
|
if (!conn || !conn->sock || !conn->conn_ops)
|
||||||
|
@ -1440,93 +1413,8 @@ static int iscsit_do_rx_data(
|
||||||
|
|
||||||
memset(&msg, 0, sizeof(struct msghdr));
|
memset(&msg, 0, sizeof(struct msghdr));
|
||||||
|
|
||||||
if (count->sync_and_steering) {
|
iov_p = count->iov;
|
||||||
int size = 0;
|
iov_len = count->iov_count;
|
||||||
u32 i, orig_iov_count = 0;
|
|
||||||
u32 orig_iov_len = 0, orig_iov_loc = 0;
|
|
||||||
u32 iov_count = 0, per_iov_bytes = 0;
|
|
||||||
u32 *rx_marker, old_rx_marker = 0;
|
|
||||||
struct kvec *iov_record;
|
|
||||||
|
|
||||||
memset(&rx_marker_val, 0,
|
|
||||||
count->ss_marker_count * sizeof(u32));
|
|
||||||
memset(&iov, 0, count->ss_iov_count * sizeof(struct kvec));
|
|
||||||
|
|
||||||
iov_record = count->iov;
|
|
||||||
orig_iov_count = count->iov_count;
|
|
||||||
rx_marker = &conn->of_marker;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
size = data;
|
|
||||||
orig_iov_len = iov_record[orig_iov_loc].iov_len;
|
|
||||||
while (size > 0) {
|
|
||||||
pr_debug("rx_data: #1 orig_iov_len %u,"
|
|
||||||
" orig_iov_loc %u\n", orig_iov_len, orig_iov_loc);
|
|
||||||
pr_debug("rx_data: #2 rx_marker %u, size"
|
|
||||||
" %u\n", *rx_marker, size);
|
|
||||||
|
|
||||||
if (orig_iov_len >= *rx_marker) {
|
|
||||||
iov[iov_count].iov_len = *rx_marker;
|
|
||||||
iov[iov_count++].iov_base =
|
|
||||||
(iov_record[orig_iov_loc].iov_base +
|
|
||||||
per_iov_bytes);
|
|
||||||
|
|
||||||
iov[iov_count].iov_len = (MARKER_SIZE / 2);
|
|
||||||
iov[iov_count++].iov_base =
|
|
||||||
&rx_marker_val[rx_marker_iov++];
|
|
||||||
iov[iov_count].iov_len = (MARKER_SIZE / 2);
|
|
||||||
iov[iov_count++].iov_base =
|
|
||||||
&rx_marker_val[rx_marker_iov++];
|
|
||||||
old_rx_marker = *rx_marker;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* OFMarkInt is in 32-bit words.
|
|
||||||
*/
|
|
||||||
*rx_marker = (conn->conn_ops->OFMarkInt * 4);
|
|
||||||
size -= old_rx_marker;
|
|
||||||
orig_iov_len -= old_rx_marker;
|
|
||||||
per_iov_bytes += old_rx_marker;
|
|
||||||
|
|
||||||
pr_debug("rx_data: #3 new_rx_marker"
|
|
||||||
" %u, size %u\n", *rx_marker, size);
|
|
||||||
} else {
|
|
||||||
iov[iov_count].iov_len = orig_iov_len;
|
|
||||||
iov[iov_count++].iov_base =
|
|
||||||
(iov_record[orig_iov_loc].iov_base +
|
|
||||||
per_iov_bytes);
|
|
||||||
|
|
||||||
per_iov_bytes = 0;
|
|
||||||
*rx_marker -= orig_iov_len;
|
|
||||||
size -= orig_iov_len;
|
|
||||||
|
|
||||||
if (size)
|
|
||||||
orig_iov_len =
|
|
||||||
iov_record[++orig_iov_loc].iov_len;
|
|
||||||
|
|
||||||
pr_debug("rx_data: #4 new_rx_marker"
|
|
||||||
" %u, size %u\n", *rx_marker, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data += (rx_marker_iov * (MARKER_SIZE / 2));
|
|
||||||
|
|
||||||
iov_p = &iov[0];
|
|
||||||
iov_len = iov_count;
|
|
||||||
|
|
||||||
if (iov_count > count->ss_iov_count) {
|
|
||||||
pr_err("iov_count: %d, count->ss_iov_count:"
|
|
||||||
" %d\n", iov_count, count->ss_iov_count);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (rx_marker_iov > count->ss_marker_count) {
|
|
||||||
pr_err("rx_marker_iov: %d, count->ss_marker"
|
|
||||||
"_count: %d\n", rx_marker_iov,
|
|
||||||
count->ss_marker_count);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
iov_p = count->iov;
|
|
||||||
iov_len = count->iov_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (total_rx < data) {
|
while (total_rx < data) {
|
||||||
rx_loop = kernel_recvmsg(conn->sock, &msg, iov_p, iov_len,
|
rx_loop = kernel_recvmsg(conn->sock, &msg, iov_p, iov_len,
|
||||||
|
@ -1541,16 +1429,6 @@ static int iscsit_do_rx_data(
|
||||||
rx_loop, total_rx, data);
|
rx_loop, total_rx, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count->sync_and_steering) {
|
|
||||||
int j;
|
|
||||||
for (j = 0; j < rx_marker_iov; j++) {
|
|
||||||
pr_debug("rx_data: #5 j: %d, offset: %d\n",
|
|
||||||
j, rx_marker_val[j]);
|
|
||||||
conn->of_marker_offset = rx_marker_val[j];
|
|
||||||
}
|
|
||||||
total_rx -= (rx_marker_iov * (MARKER_SIZE / 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
return total_rx;
|
return total_rx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1559,8 +1437,7 @@ static int iscsit_do_tx_data(
|
||||||
struct iscsi_data_count *count)
|
struct iscsi_data_count *count)
|
||||||
{
|
{
|
||||||
int data = count->data_length, total_tx = 0, tx_loop = 0, iov_len;
|
int data = count->data_length, total_tx = 0, tx_loop = 0, iov_len;
|
||||||
u32 tx_marker_val[count->ss_marker_count], tx_marker_iov = 0;
|
struct kvec *iov_p;
|
||||||
struct kvec iov[count->ss_iov_count], *iov_p;
|
|
||||||
struct msghdr msg;
|
struct msghdr msg;
|
||||||
|
|
||||||
if (!conn || !conn->sock || !conn->conn_ops)
|
if (!conn || !conn->sock || !conn->conn_ops)
|
||||||
|
@ -1573,98 +1450,8 @@ static int iscsit_do_tx_data(
|
||||||
|
|
||||||
memset(&msg, 0, sizeof(struct msghdr));
|
memset(&msg, 0, sizeof(struct msghdr));
|
||||||
|
|
||||||
if (count->sync_and_steering) {
|
iov_p = count->iov;
|
||||||
int size = 0;
|
iov_len = count->iov_count;
|
||||||
u32 i, orig_iov_count = 0;
|
|
||||||
u32 orig_iov_len = 0, orig_iov_loc = 0;
|
|
||||||
u32 iov_count = 0, per_iov_bytes = 0;
|
|
||||||
u32 *tx_marker, old_tx_marker = 0;
|
|
||||||
struct kvec *iov_record;
|
|
||||||
|
|
||||||
memset(&tx_marker_val, 0,
|
|
||||||
count->ss_marker_count * sizeof(u32));
|
|
||||||
memset(&iov, 0, count->ss_iov_count * sizeof(struct kvec));
|
|
||||||
|
|
||||||
iov_record = count->iov;
|
|
||||||
orig_iov_count = count->iov_count;
|
|
||||||
tx_marker = &conn->if_marker;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
size = data;
|
|
||||||
orig_iov_len = iov_record[orig_iov_loc].iov_len;
|
|
||||||
while (size > 0) {
|
|
||||||
pr_debug("tx_data: #1 orig_iov_len %u,"
|
|
||||||
" orig_iov_loc %u\n", orig_iov_len, orig_iov_loc);
|
|
||||||
pr_debug("tx_data: #2 tx_marker %u, size"
|
|
||||||
" %u\n", *tx_marker, size);
|
|
||||||
|
|
||||||
if (orig_iov_len >= *tx_marker) {
|
|
||||||
iov[iov_count].iov_len = *tx_marker;
|
|
||||||
iov[iov_count++].iov_base =
|
|
||||||
(iov_record[orig_iov_loc].iov_base +
|
|
||||||
per_iov_bytes);
|
|
||||||
|
|
||||||
tx_marker_val[tx_marker_iov] =
|
|
||||||
(size - *tx_marker);
|
|
||||||
iov[iov_count].iov_len = (MARKER_SIZE / 2);
|
|
||||||
iov[iov_count++].iov_base =
|
|
||||||
&tx_marker_val[tx_marker_iov++];
|
|
||||||
iov[iov_count].iov_len = (MARKER_SIZE / 2);
|
|
||||||
iov[iov_count++].iov_base =
|
|
||||||
&tx_marker_val[tx_marker_iov++];
|
|
||||||
old_tx_marker = *tx_marker;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IFMarkInt is in 32-bit words.
|
|
||||||
*/
|
|
||||||
*tx_marker = (conn->conn_ops->IFMarkInt * 4);
|
|
||||||
size -= old_tx_marker;
|
|
||||||
orig_iov_len -= old_tx_marker;
|
|
||||||
per_iov_bytes += old_tx_marker;
|
|
||||||
|
|
||||||
pr_debug("tx_data: #3 new_tx_marker"
|
|
||||||
" %u, size %u\n", *tx_marker, size);
|
|
||||||
pr_debug("tx_data: #4 offset %u\n",
|
|
||||||
tx_marker_val[tx_marker_iov-1]);
|
|
||||||
} else {
|
|
||||||
iov[iov_count].iov_len = orig_iov_len;
|
|
||||||
iov[iov_count++].iov_base
|
|
||||||
= (iov_record[orig_iov_loc].iov_base +
|
|
||||||
per_iov_bytes);
|
|
||||||
|
|
||||||
per_iov_bytes = 0;
|
|
||||||
*tx_marker -= orig_iov_len;
|
|
||||||
size -= orig_iov_len;
|
|
||||||
|
|
||||||
if (size)
|
|
||||||
orig_iov_len =
|
|
||||||
iov_record[++orig_iov_loc].iov_len;
|
|
||||||
|
|
||||||
pr_debug("tx_data: #5 new_tx_marker"
|
|
||||||
" %u, size %u\n", *tx_marker, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data += (tx_marker_iov * (MARKER_SIZE / 2));
|
|
||||||
|
|
||||||
iov_p = &iov[0];
|
|
||||||
iov_len = iov_count;
|
|
||||||
|
|
||||||
if (iov_count > count->ss_iov_count) {
|
|
||||||
pr_err("iov_count: %d, count->ss_iov_count:"
|
|
||||||
" %d\n", iov_count, count->ss_iov_count);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (tx_marker_iov > count->ss_marker_count) {
|
|
||||||
pr_err("tx_marker_iov: %d, count->ss_marker"
|
|
||||||
"_count: %d\n", tx_marker_iov,
|
|
||||||
count->ss_marker_count);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
iov_p = count->iov;
|
|
||||||
iov_len = count->iov_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (total_tx < data) {
|
while (total_tx < data) {
|
||||||
tx_loop = kernel_sendmsg(conn->sock, &msg, iov_p, iov_len,
|
tx_loop = kernel_sendmsg(conn->sock, &msg, iov_p, iov_len,
|
||||||
|
@ -1679,9 +1466,6 @@ static int iscsit_do_tx_data(
|
||||||
tx_loop, total_tx, data);
|
tx_loop, total_tx, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count->sync_and_steering)
|
|
||||||
total_tx -= (tx_marker_iov * (MARKER_SIZE / 2));
|
|
||||||
|
|
||||||
return total_tx;
|
return total_tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1702,12 +1486,6 @@ int rx_data(
|
||||||
c.data_length = data;
|
c.data_length = data;
|
||||||
c.type = ISCSI_RX_DATA;
|
c.type = ISCSI_RX_DATA;
|
||||||
|
|
||||||
if (conn->conn_ops->OFMarker &&
|
|
||||||
(conn->conn_state >= TARG_CONN_STATE_LOGGED_IN)) {
|
|
||||||
if (iscsit_determine_sync_and_steering_counts(conn, &c) < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return iscsit_do_rx_data(conn, &c);
|
return iscsit_do_rx_data(conn, &c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1728,12 +1506,6 @@ int tx_data(
|
||||||
c.data_length = data;
|
c.data_length = data;
|
||||||
c.type = ISCSI_TX_DATA;
|
c.type = ISCSI_TX_DATA;
|
||||||
|
|
||||||
if (conn->conn_ops->IFMarker &&
|
|
||||||
(conn->conn_state >= TARG_CONN_STATE_LOGGED_IN)) {
|
|
||||||
if (iscsit_determine_sync_and_steering_counts(conn, &c) < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return iscsit_do_tx_data(conn, &c);
|
return iscsit_do_tx_data(conn, &c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
#include <scsi/scsi.h>
|
#include <scsi/scsi.h>
|
||||||
|
|
||||||
|
@ -154,6 +155,37 @@ target_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
target_parse_naa_6h_vendor_specific(struct se_device *dev, unsigned char *buf_off)
|
||||||
|
{
|
||||||
|
unsigned char *p = &dev->se_sub_dev->t10_wwn.unit_serial[0];
|
||||||
|
unsigned char *buf = buf_off;
|
||||||
|
int cnt = 0, next = 1;
|
||||||
|
/*
|
||||||
|
* Generate up to 36 bits of VENDOR SPECIFIC IDENTIFIER starting on
|
||||||
|
* byte 3 bit 3-0 for NAA IEEE Registered Extended DESIGNATOR field
|
||||||
|
* format, followed by 64 bits of VENDOR SPECIFIC IDENTIFIER EXTENSION
|
||||||
|
* to complete the payload. These are based from VPD=0x80 PRODUCT SERIAL
|
||||||
|
* NUMBER set via vpd_unit_serial in target_core_configfs.c to ensure
|
||||||
|
* per device uniqeness.
|
||||||
|
*/
|
||||||
|
while (*p != '\0') {
|
||||||
|
if (cnt >= 13)
|
||||||
|
break;
|
||||||
|
if (!isxdigit(*p)) {
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (next != 0) {
|
||||||
|
buf[cnt++] |= hex_to_bin(*p++);
|
||||||
|
next = 0;
|
||||||
|
} else {
|
||||||
|
buf[cnt] = hex_to_bin(*p++) << 4;
|
||||||
|
next = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Device identification VPD, for a complete list of
|
* Device identification VPD, for a complete list of
|
||||||
* DESIGNATOR TYPEs see spc4r17 Table 459.
|
* DESIGNATOR TYPEs see spc4r17 Table 459.
|
||||||
|
@ -219,8 +251,7 @@ target_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf)
|
||||||
* VENDOR_SPECIFIC_IDENTIFIER and
|
* VENDOR_SPECIFIC_IDENTIFIER and
|
||||||
* VENDOR_SPECIFIC_IDENTIFIER_EXTENTION
|
* VENDOR_SPECIFIC_IDENTIFIER_EXTENTION
|
||||||
*/
|
*/
|
||||||
buf[off++] |= hex_to_bin(dev->se_sub_dev->t10_wwn.unit_serial[0]);
|
target_parse_naa_6h_vendor_specific(dev, &buf[off]);
|
||||||
hex2bin(&buf[off], &dev->se_sub_dev->t10_wwn.unit_serial[1], 12);
|
|
||||||
|
|
||||||
len = 20;
|
len = 20;
|
||||||
off = (len + 4);
|
off = (len + 4);
|
||||||
|
|
|
@ -977,15 +977,17 @@ static void target_qf_do_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct se_device *dev = container_of(work, struct se_device,
|
struct se_device *dev = container_of(work, struct se_device,
|
||||||
qf_work_queue);
|
qf_work_queue);
|
||||||
|
LIST_HEAD(qf_cmd_list);
|
||||||
struct se_cmd *cmd, *cmd_tmp;
|
struct se_cmd *cmd, *cmd_tmp;
|
||||||
|
|
||||||
spin_lock_irq(&dev->qf_cmd_lock);
|
spin_lock_irq(&dev->qf_cmd_lock);
|
||||||
list_for_each_entry_safe(cmd, cmd_tmp, &dev->qf_cmd_list, se_qf_node) {
|
list_splice_init(&dev->qf_cmd_list, &qf_cmd_list);
|
||||||
|
spin_unlock_irq(&dev->qf_cmd_lock);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(cmd, cmd_tmp, &qf_cmd_list, se_qf_node) {
|
||||||
list_del(&cmd->se_qf_node);
|
list_del(&cmd->se_qf_node);
|
||||||
atomic_dec(&dev->dev_qf_count);
|
atomic_dec(&dev->dev_qf_count);
|
||||||
smp_mb__after_atomic_dec();
|
smp_mb__after_atomic_dec();
|
||||||
spin_unlock_irq(&dev->qf_cmd_lock);
|
|
||||||
|
|
||||||
pr_debug("Processing %s cmd: %p QUEUE_FULL in work queue"
|
pr_debug("Processing %s cmd: %p QUEUE_FULL in work queue"
|
||||||
" context: %s\n", cmd->se_tfo->get_fabric_name(), cmd,
|
" context: %s\n", cmd->se_tfo->get_fabric_name(), cmd,
|
||||||
|
@ -997,10 +999,7 @@ static void target_qf_do_work(struct work_struct *work)
|
||||||
* has been added to head of queue
|
* has been added to head of queue
|
||||||
*/
|
*/
|
||||||
transport_add_cmd_to_queue(cmd, cmd->t_state);
|
transport_add_cmd_to_queue(cmd, cmd->t_state);
|
||||||
|
|
||||||
spin_lock_irq(&dev->qf_cmd_lock);
|
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&dev->qf_cmd_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *transport_dump_cmd_direction(struct se_cmd *cmd)
|
unsigned char *transport_dump_cmd_direction(struct se_cmd *cmd)
|
||||||
|
|
|
@ -98,8 +98,7 @@ struct ft_tpg {
|
||||||
struct list_head list; /* linkage in ft_lport_acl tpg_list */
|
struct list_head list; /* linkage in ft_lport_acl tpg_list */
|
||||||
struct list_head lun_list; /* head of LUNs */
|
struct list_head lun_list; /* head of LUNs */
|
||||||
struct se_portal_group se_tpg;
|
struct se_portal_group se_tpg;
|
||||||
struct task_struct *thread; /* processing thread */
|
struct workqueue_struct *workqueue;
|
||||||
struct se_queue_obj qobj; /* queue for processing thread */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ft_lport_acl {
|
struct ft_lport_acl {
|
||||||
|
@ -110,16 +109,10 @@ struct ft_lport_acl {
|
||||||
struct se_wwn fc_lport_wwn;
|
struct se_wwn fc_lport_wwn;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ft_cmd_state {
|
|
||||||
FC_CMD_ST_NEW = 0,
|
|
||||||
FC_CMD_ST_REJ
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Commands
|
* Commands
|
||||||
*/
|
*/
|
||||||
struct ft_cmd {
|
struct ft_cmd {
|
||||||
enum ft_cmd_state state;
|
|
||||||
u32 lun; /* LUN from request */
|
u32 lun; /* LUN from request */
|
||||||
struct ft_sess *sess; /* session held for cmd */
|
struct ft_sess *sess; /* session held for cmd */
|
||||||
struct fc_seq *seq; /* sequence in exchange mgr */
|
struct fc_seq *seq; /* sequence in exchange mgr */
|
||||||
|
@ -127,7 +120,7 @@ struct ft_cmd {
|
||||||
struct fc_frame *req_frame;
|
struct fc_frame *req_frame;
|
||||||
unsigned char *cdb; /* pointer to CDB inside frame */
|
unsigned char *cdb; /* pointer to CDB inside frame */
|
||||||
u32 write_data_len; /* data received on writes */
|
u32 write_data_len; /* data received on writes */
|
||||||
struct se_queue_req se_req;
|
struct work_struct work;
|
||||||
/* Local sense buffer */
|
/* Local sense buffer */
|
||||||
unsigned char ft_sense_buffer[TRANSPORT_SENSE_BUFFER];
|
unsigned char ft_sense_buffer[TRANSPORT_SENSE_BUFFER];
|
||||||
u32 was_ddp_setup:1; /* Set only if ddp is setup */
|
u32 was_ddp_setup:1; /* Set only if ddp is setup */
|
||||||
|
@ -177,7 +170,6 @@ int ft_is_state_remove(struct se_cmd *);
|
||||||
/*
|
/*
|
||||||
* other internal functions.
|
* other internal functions.
|
||||||
*/
|
*/
|
||||||
int ft_thread(void *);
|
|
||||||
void ft_recv_req(struct ft_sess *, struct fc_frame *);
|
void ft_recv_req(struct ft_sess *, struct fc_frame *);
|
||||||
struct ft_tpg *ft_lport_find_tpg(struct fc_lport *);
|
struct ft_tpg *ft_lport_find_tpg(struct fc_lport *);
|
||||||
struct ft_node_acl *ft_acl_get(struct ft_tpg *, struct fc_rport_priv *);
|
struct ft_node_acl *ft_acl_get(struct ft_tpg *, struct fc_rport_priv *);
|
||||||
|
|
|
@ -62,8 +62,8 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
se_cmd = &cmd->se_cmd;
|
se_cmd = &cmd->se_cmd;
|
||||||
pr_debug("%s: cmd %p state %d sess %p seq %p se_cmd %p\n",
|
pr_debug("%s: cmd %p sess %p seq %p se_cmd %p\n",
|
||||||
caller, cmd, cmd->state, cmd->sess, cmd->seq, se_cmd);
|
caller, cmd, cmd->sess, cmd->seq, se_cmd);
|
||||||
pr_debug("%s: cmd %p cdb %p\n",
|
pr_debug("%s: cmd %p cdb %p\n",
|
||||||
caller, cmd, cmd->cdb);
|
caller, cmd, cmd->cdb);
|
||||||
pr_debug("%s: cmd %p lun %d\n", caller, cmd, cmd->lun);
|
pr_debug("%s: cmd %p lun %d\n", caller, cmd, cmd->lun);
|
||||||
|
@ -90,38 +90,6 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
|
||||||
16, 4, cmd->cdb, MAX_COMMAND_SIZE, 0);
|
16, 4, cmd->cdb, MAX_COMMAND_SIZE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ft_queue_cmd(struct ft_sess *sess, struct ft_cmd *cmd)
|
|
||||||
{
|
|
||||||
struct ft_tpg *tpg = sess->tport->tpg;
|
|
||||||
struct se_queue_obj *qobj = &tpg->qobj;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
qobj = &sess->tport->tpg->qobj;
|
|
||||||
spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
|
|
||||||
list_add_tail(&cmd->se_req.qr_list, &qobj->qobj_list);
|
|
||||||
atomic_inc(&qobj->queue_cnt);
|
|
||||||
spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
|
|
||||||
|
|
||||||
wake_up_process(tpg->thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ft_cmd *ft_dequeue_cmd(struct se_queue_obj *qobj)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
struct se_queue_req *qr;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
|
|
||||||
if (list_empty(&qobj->qobj_list)) {
|
|
||||||
spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
qr = list_first_entry(&qobj->qobj_list, struct se_queue_req, qr_list);
|
|
||||||
list_del(&qr->qr_list);
|
|
||||||
atomic_dec(&qobj->queue_cnt);
|
|
||||||
spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
|
|
||||||
return container_of(qr, struct ft_cmd, se_req);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ft_free_cmd(struct ft_cmd *cmd)
|
static void ft_free_cmd(struct ft_cmd *cmd)
|
||||||
{
|
{
|
||||||
struct fc_frame *fp;
|
struct fc_frame *fp;
|
||||||
|
@ -282,9 +250,7 @@ u32 ft_get_task_tag(struct se_cmd *se_cmd)
|
||||||
|
|
||||||
int ft_get_cmd_state(struct se_cmd *se_cmd)
|
int ft_get_cmd_state(struct se_cmd *se_cmd)
|
||||||
{
|
{
|
||||||
struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
|
return 0;
|
||||||
|
|
||||||
return cmd->state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ft_is_state_remove(struct se_cmd *se_cmd)
|
int ft_is_state_remove(struct se_cmd *se_cmd)
|
||||||
|
@ -505,6 +471,8 @@ int ft_queue_tm_resp(struct se_cmd *se_cmd)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ft_send_work(struct work_struct *work);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle incoming FCP command.
|
* Handle incoming FCP command.
|
||||||
*/
|
*/
|
||||||
|
@ -523,7 +491,9 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
|
||||||
goto busy;
|
goto busy;
|
||||||
}
|
}
|
||||||
cmd->req_frame = fp; /* hold frame during cmd */
|
cmd->req_frame = fp; /* hold frame during cmd */
|
||||||
ft_queue_cmd(sess, cmd);
|
|
||||||
|
INIT_WORK(&cmd->work, ft_send_work);
|
||||||
|
queue_work(sess->tport->tpg->workqueue, &cmd->work);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
busy:
|
busy:
|
||||||
|
@ -563,12 +533,13 @@ void ft_recv_req(struct ft_sess *sess, struct fc_frame *fp)
|
||||||
/*
|
/*
|
||||||
* Send new command to target.
|
* Send new command to target.
|
||||||
*/
|
*/
|
||||||
static void ft_send_cmd(struct ft_cmd *cmd)
|
static void ft_send_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
|
struct ft_cmd *cmd = container_of(work, struct ft_cmd, work);
|
||||||
struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame);
|
struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame);
|
||||||
struct se_cmd *se_cmd;
|
struct se_cmd *se_cmd;
|
||||||
struct fcp_cmnd *fcp;
|
struct fcp_cmnd *fcp;
|
||||||
int data_dir;
|
int data_dir = 0;
|
||||||
u32 data_len;
|
u32 data_len;
|
||||||
int task_attr;
|
int task_attr;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -675,42 +646,3 @@ static void ft_send_cmd(struct ft_cmd *cmd)
|
||||||
err:
|
err:
|
||||||
ft_send_resp_code_and_free(cmd, FCP_CMND_FIELDS_INVALID);
|
ft_send_resp_code_and_free(cmd, FCP_CMND_FIELDS_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Handle request in the command thread.
|
|
||||||
*/
|
|
||||||
static void ft_exec_req(struct ft_cmd *cmd)
|
|
||||||
{
|
|
||||||
pr_debug("cmd state %x\n", cmd->state);
|
|
||||||
switch (cmd->state) {
|
|
||||||
case FC_CMD_ST_NEW:
|
|
||||||
ft_send_cmd(cmd);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Processing thread.
|
|
||||||
* Currently one thread per tpg.
|
|
||||||
*/
|
|
||||||
int ft_thread(void *arg)
|
|
||||||
{
|
|
||||||
struct ft_tpg *tpg = arg;
|
|
||||||
struct se_queue_obj *qobj = &tpg->qobj;
|
|
||||||
struct ft_cmd *cmd;
|
|
||||||
|
|
||||||
while (!kthread_should_stop()) {
|
|
||||||
schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT);
|
|
||||||
if (kthread_should_stop())
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
cmd = ft_dequeue_cmd(qobj);
|
|
||||||
if (cmd)
|
|
||||||
ft_exec_req(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -327,7 +327,6 @@ static struct se_portal_group *ft_add_tpg(
|
||||||
tpg->index = index;
|
tpg->index = index;
|
||||||
tpg->lport_acl = lacl;
|
tpg->lport_acl = lacl;
|
||||||
INIT_LIST_HEAD(&tpg->lun_list);
|
INIT_LIST_HEAD(&tpg->lun_list);
|
||||||
transport_init_queue_obj(&tpg->qobj);
|
|
||||||
|
|
||||||
ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg,
|
ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg,
|
||||||
tpg, TRANSPORT_TPG_TYPE_NORMAL);
|
tpg, TRANSPORT_TPG_TYPE_NORMAL);
|
||||||
|
@ -336,8 +335,8 @@ static struct se_portal_group *ft_add_tpg(
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tpg->thread = kthread_run(ft_thread, tpg, "ft_tpg%lu", index);
|
tpg->workqueue = alloc_workqueue("tcm_fc", 0, 1);
|
||||||
if (IS_ERR(tpg->thread)) {
|
if (!tpg->workqueue) {
|
||||||
kfree(tpg);
|
kfree(tpg);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -356,7 +355,7 @@ static void ft_del_tpg(struct se_portal_group *se_tpg)
|
||||||
pr_debug("del tpg %s\n",
|
pr_debug("del tpg %s\n",
|
||||||
config_item_name(&tpg->se_tpg.tpg_group.cg_item));
|
config_item_name(&tpg->se_tpg.tpg_group.cg_item));
|
||||||
|
|
||||||
kthread_stop(tpg->thread);
|
destroy_workqueue(tpg->workqueue);
|
||||||
|
|
||||||
/* Wait for sessions to be freed thru RCU, for BUG_ON below */
|
/* Wait for sessions to be freed thru RCU, for BUG_ON below */
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
|
|
|
@ -219,43 +219,41 @@ void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp)
|
||||||
if (cmd->was_ddp_setup) {
|
if (cmd->was_ddp_setup) {
|
||||||
BUG_ON(!ep);
|
BUG_ON(!ep);
|
||||||
BUG_ON(!lport);
|
BUG_ON(!lport);
|
||||||
}
|
/*
|
||||||
|
* Since DDP (Large Rx offload) was setup for this request,
|
||||||
/*
|
* payload is expected to be copied directly to user buffers.
|
||||||
* Doesn't expect payload if DDP is setup. Payload
|
*/
|
||||||
* is expected to be copied directly to user buffers
|
buf = fc_frame_payload_get(fp, 1);
|
||||||
* due to DDP (Large Rx offload),
|
if (buf)
|
||||||
*/
|
pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, "
|
||||||
buf = fc_frame_payload_get(fp, 1);
|
|
||||||
if (buf)
|
|
||||||
pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, "
|
|
||||||
"cmd->sg_cnt 0x%x. DDP was setup"
|
"cmd->sg_cnt 0x%x. DDP was setup"
|
||||||
" hence not expected to receive frame with "
|
" hence not expected to receive frame with "
|
||||||
"payload, Frame will be dropped if "
|
"payload, Frame will be dropped if"
|
||||||
"'Sequence Initiative' bit in f_ctl is "
|
"'Sequence Initiative' bit in f_ctl is"
|
||||||
"not set\n", __func__, ep->xid, f_ctl,
|
"not set\n", __func__, ep->xid, f_ctl,
|
||||||
cmd->sg, cmd->sg_cnt);
|
cmd->sg, cmd->sg_cnt);
|
||||||
/*
|
/*
|
||||||
* Invalidate HW DDP context if it was setup for respective
|
* Invalidate HW DDP context if it was setup for respective
|
||||||
* command. Invalidation of HW DDP context is requited in both
|
* command. Invalidation of HW DDP context is requited in both
|
||||||
* situation (success and error).
|
* situation (success and error).
|
||||||
*/
|
*/
|
||||||
ft_invl_hw_context(cmd);
|
ft_invl_hw_context(cmd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If "Sequence Initiative (TSI)" bit set in f_ctl, means last
|
* If "Sequence Initiative (TSI)" bit set in f_ctl, means last
|
||||||
* write data frame is received successfully where payload is
|
* write data frame is received successfully where payload is
|
||||||
* posted directly to user buffer and only the last frame's
|
* posted directly to user buffer and only the last frame's
|
||||||
* header is posted in receive queue.
|
* header is posted in receive queue.
|
||||||
*
|
*
|
||||||
* If "Sequence Initiative (TSI)" bit is not set, means error
|
* If "Sequence Initiative (TSI)" bit is not set, means error
|
||||||
* condition w.r.t. DDP, hence drop the packet and let explict
|
* condition w.r.t. DDP, hence drop the packet and let explict
|
||||||
* ABORTS from other end of exchange timer trigger the recovery.
|
* ABORTS from other end of exchange timer trigger the recovery.
|
||||||
*/
|
*/
|
||||||
if (f_ctl & FC_FC_SEQ_INIT)
|
if (f_ctl & FC_FC_SEQ_INIT)
|
||||||
goto last_frame;
|
goto last_frame;
|
||||||
else
|
else
|
||||||
goto drop;
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
rel_off = ntohl(fh->fh_parm_offset);
|
rel_off = ntohl(fh->fh_parm_offset);
|
||||||
frame_len = fr_len(fp);
|
frame_len = fr_len(fp);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче