Remove throttling code, instead signal scsipi layer to reduce the openings

and retry the command. Start with a small openings number and let scsipi
request to grow it up to the current send window.

Adjust ccb and pdu counts to avoid ressource shortages. These are still
very ad-hoc numbers, but seem to be sufficient for a Gigabit link.

Use separate condvar for PDU pool and add counter to help debugging.

Revert setting PDU disposition to UNUSED before freeing. free_pdu
uses this as a flag to detect already returned PDUs.

Add reference counter for open commands to defer unmapping a session
that would lead to crashes in scsipi.

Move session cleanup to cleanup thread.

Use get_sernum to retrieve current serial number where possible and
make it check for immediate commands itself.

Adjust debug output.
This commit is contained in:
mlelstv 2016-06-15 04:30:30 +00:00
parent ae0ee83b73
commit bbe94f43d2
7 changed files with 397 additions and 323 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi.h,v 1.3 2011/11/19 16:41:56 agc Exp $ */
/* $NetBSD: iscsi.h,v 1.4 2016/06/15 04:30:30 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2006,2011 The NetBSD Foundation, Inc.
@ -182,6 +182,7 @@ typedef struct {
#define ISCSI_STATUS_LOGOUT_CID_NOT_FOUND 45 /* Logout error: CID not found */
#define ISCSI_STATUS_LOGOUT_RECOVERY_NS 46 /* Logout error: Recovery not supported */
#define ISCSI_STATUS_LOGOUT_ERROR 47 /* Logout error: Unknown reason */
#define ISCSI_STATUS_QUEUE_FULL 48 /* iSCSI send window exhausted */
#define ISCSID_STATUS_SUCCESS 0 /* Indicates success. */
#define ISCSID_STATUS_LIST_EMPTY 1001 /* The requested list is empty. */

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_globals.h,v 1.20 2016/06/15 03:51:55 mlelstv Exp $ */
/* $NetBSD: iscsi_globals.h,v 1.21 2016/06/15 04:30:30 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@ -70,34 +70,26 @@
#define VERSION_MINOR 1
#define VERSION_STRING "NetBSD iSCSI Software Initiator 20110407"
/*
Various checks are made that the expected cmd Serial Number is less than
the actual command serial number. The extremely paranoid amongst us
believe that a malicious iSCSI server could set this artificially low
and effectively DoS a naive initiator. For this (possibly ludicrous)
reason, I have added the two definitions below (agc, 2011/04/09). The
throttling definition enables a check that the CmdSN is less than the
ExpCmdSN in iscsi_send.c, and is enabled by default. The second definition
effectively says "don't bother testing these values", and is used right
now only in iscsi_send.c.
*/
#define ISCSI_THROTTLING_ENABLED 1
#define ISCSI_SERVER_TRUSTED 0
/*
NOTE: CCBS_PER_SESSION must not exceed 256 due to the way the ITT
is constructed (it has the CCB index in its lower 8 bits). If it should ever
be necessary to increase the number beyond that (which isn't expected),
the corresponding ITT generation and extraction code must be rewritten.
*/
#define CCBS_PER_SESSION 64 /* ToDo: Reasonable number?? */
#define CCBS_PER_SESSION 32 /* ToDo: Reasonable number?? */
/*
NOTE: CCBS_FOR_SCSPI limits the number of outstanding commands for
SCSI commands, leaving some CCBs for keepalive and logout attempts,
which are needed for each connection.
*/
#define CCBS_FOR_SCSIPI 16 /* ToDo: Reasonable number?? */
/*
NOTE: PDUS_PER_CONNECTION is a number that could potentially impact
performance if set too low, as a single command may use up a lot of PDUs for
high values of First/MaxBurstLength and small values of
MaxRecvDataSegmentLength of the target.
*/
#define PDUS_PER_CONNECTION 128 /* ToDo: Reasonable number?? */
#define PDUS_PER_CONNECTION 64 /* ToDo: Reasonable number?? */
/* max outstanding serial nums before we give up on the connection */
#define SERNUM_BUFFER_LENGTH (CCBS_PER_SESSION / 2) /* ToDo: Reasonable?? */
@ -106,7 +98,7 @@ now only in iscsi_send.c.
#define DEFAULT_MaxRecvDataSegmentLength (64*1024)
/* Command timeout (reset on received PDU associated with the command's CCB) */
#define COMMAND_TIMEOUT (7 * hz) /* ToDo: Reasonable? (7 seconds) */
#define COMMAND_TIMEOUT (60 * hz) /* ToDo: Reasonable? (60 seconds) */
#define MAX_CCB_TIMEOUTS 3 /* Max number of tries to resend or SNACK */
#define MAX_CCB_TRIES 9 /* Max number of total tries to recover */
@ -131,12 +123,10 @@ now only in iscsi_send.c.
#define CCBF_COMPLETE 0x0001 /* received status */
#define CCBF_RESENT 0x0002 /* ccb was resent */
#define CCBF_SENDTARGET 0x0004 /* SendTargets text request, not negotiation */
#define CCBF_WAITING 0x0008 /* CCB is waiting for MaxCmdSN, wake it up */
#define CCBF_GOT_RSP 0x0010 /* Got at least one response to this request */
#define CCBF_REASSIGN 0x0020 /* Command can be reassigned */
#define CCBF_OTHERCONN 0x0040 /* a logout for a different connection */
#define CCBF_WAITQUEUE 0x0080 /* CCB is on waiting queue */
#define CCBF_THROTTLING 0x0100 /* CCB is on throttling queue */
/* --------------------------- Global Types ------------------------------- */
@ -322,6 +312,7 @@ struct connection_s {
kmutex_t lock;
kcondvar_t conn_cv;
kcondvar_t pdu_cv;
kcondvar_t ccb_cv;
kcondvar_t idle_cv;
@ -375,6 +366,7 @@ struct connection_s {
int recover; /* recovery count */
/* (reset on first successful data transfer) */
volatile unsigned usecount; /* number of active CCBs */
volatile unsigned pducount; /* number of active PDUs */
bool destroy; /* conn will be destroyed */
bool in_session;
@ -417,6 +409,8 @@ struct session_s {
device_t child_dev;
/* the child we're associated with - (NULL if not mapped) */
int refcount; /* session in use by scsipi */
/* local stuff */
TAILQ_ENTRY(session_s) sessions; /* the list of sessions */
@ -425,8 +419,8 @@ struct session_s {
kcondvar_t ccb_cv;
ccb_list_t ccb_pool; /* The free CCB pool */
ccb_list_t ccbs_throttled;
/* CCBs waiting for MaxCmdSN to increase */
int send_window;
uint16_t id; /* session ID (unique within driver) */
uint16_t TSIH; /* Target assigned session ID */
@ -638,7 +632,7 @@ sn_a_le_b(uint32_t a, uint32_t b)
/* in iscsi_ioctl.c */
void iscsi_init_cleanup(void);
void iscsi_destroy_cleanup(void);
int iscsi_destroy_cleanup(void);
void iscsi_notify_cleanup(void);
@ -652,7 +646,7 @@ void add_event(iscsi_event_t, uint32_t, uint32_t, uint32_t);
void kill_connection(connection_t *, uint32_t, int, bool);
void kill_session(session_t *, uint32_t, int, bool);
void kill_all_sessions(void);
int kill_all_sessions(void);
void handle_connection_error(connection_t *, uint32_t, int);
void add_connection_cleanup(connection_t *);
@ -664,7 +658,8 @@ int iscsiioctl(struct file *, u_long, void *);
session_t *find_session(uint32_t);
connection_t *find_connection(session_t *, uint32_t);
int ref_session(session_t *);
void unref_session(session_t *);
/* in iscsi_main.c */
@ -725,7 +720,6 @@ void create_ccbs(session_t *);
ccb_t *get_ccb(connection_t *, bool);
void free_ccb(ccb_t *);
void suspend_ccb(ccb_t *, bool);
void throttle_ccb(ccb_t *, bool);
void wake_ccb(ccb_t *, uint32_t);
void create_pdus(connection_t *);
@ -736,8 +730,9 @@ void init_sernum(sernum_buffer_t *);
int add_sernum(sernum_buffer_t *, uint32_t);
uint32_t ack_sernum(sernum_buffer_t *, uint32_t);
uint32_t get_sernum(session_t *, bool);
uint32_t get_sernum(session_t *, pdu_t *);
int sernum_in_window(session_t *);
int window_size(session_t *, int);
/* in iscsi_text.c */

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_ioctl.c,v 1.21 2016/06/05 15:04:31 mlelstv Exp $ */
/* $NetBSD: iscsi_ioctl.c,v 1.22 2016/06/15 04:30:30 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@ -430,6 +430,50 @@ find_connection(session_t *session, uint32_t id)
return curr;
}
/*
* ref_session:
* Reference a session
*
* Session cannot be release until reference count reaches zero
*
* Returns: 1 if reference counter would overflow
*/
int
ref_session(session_t *session)
{
int rc = 1;
mutex_enter(&iscsi_cleanup_mtx);
KASSERT(session != NULL);
if (session->refcount <= CCBS_PER_SESSION) {
session->refcount++;
rc = 0;
}
mutex_exit(&iscsi_cleanup_mtx);
return rc;
}
/*
* unref_session:
* Unreference a session
*
* Release session reference, trigger cleanup
*/
void
unref_session(session_t *session)
{
mutex_enter(&iscsi_cleanup_mtx);
KASSERT(session != NULL);
KASSERT(session->refcount > 0);
if (--session->refcount == 0)
cv_broadcast(&session->sess_cv);
mutex_exit(&iscsi_cleanup_mtx);
}
/*
* kill_connection:
@ -543,8 +587,7 @@ kill_connection(connection_t *conn, uint32_t status, int logout, bool recover)
void
kill_session(session_t *session, uint32_t status, int logout, bool recover)
{
connection_t *curr;
ccb_t *ccb;
connection_t *conn;
DEB(1, ("ISCSI: kill_session %d, status %d, logout %d, recover %d\n",
session->id, status, logout, recover));
@ -552,6 +595,7 @@ kill_session(session_t *session, uint32_t status, int logout, bool recover)
mutex_enter(&iscsi_cleanup_mtx);
if (session->terminating) {
mutex_exit(&iscsi_cleanup_mtx);
DEB(5, ("Session is being killed with status %d\n",session->terminating));
return;
}
@ -560,15 +604,16 @@ kill_session(session_t *session, uint32_t status, int logout, bool recover)
* don't do anything if session isn't established yet, termination will be
* handled elsewhere
*/
if (session->sessions.tqe_next == NULL &&
session->sessions.tqe_prev == NULL) {
if (session->sessions.tqe_next == NULL && session->sessions.tqe_prev == NULL) {
mutex_exit(&iscsi_cleanup_mtx);
DEB(5, ("Session is being killed which is not yet established\n"));
return;
}
session->terminating = status;
mutex_exit(&iscsi_cleanup_mtx);
if (recover) {
mutex_exit(&iscsi_cleanup_mtx);
/*
* Only recover when there's just one active connection left.
* Otherwise we get in all sorts of timing problems, and it doesn't
@ -576,41 +621,32 @@ kill_session(session_t *session, uint32_t status, int logout, bool recover)
* requested that we kill a multipathed session.
*/
if (session->active_connections == 1) {
curr = assign_connection(session, FALSE);
if (curr != NULL)
kill_connection(curr, status, logout, TRUE);
conn = assign_connection(session, FALSE);
if (conn != NULL)
kill_connection(conn, status, logout, TRUE);
}
/* don't allow the session to disappear when the target */
/* requested the logout */
session->terminating = ISCSI_STATUS_SUCCESS;
return;
}
/* remove from session list */
mutex_enter(&iscsi_cleanup_mtx);
if (session->refcount > 0) {
mutex_exit(&iscsi_cleanup_mtx);
DEB(5, ("Session is being killed while in use (refcnt = %d)\n",
session->refcount));
return;
}
/* Remove session from global list */
session->terminating = status;
TAILQ_REMOVE(&iscsi_sessions, session, sessions);
session->sessions.tqe_next = NULL;
session->sessions.tqe_prev = NULL;
mutex_exit(&iscsi_cleanup_mtx);
/* complete any throttled CCBs */
mutex_enter(&session->lock);
while ((ccb = TAILQ_FIRST(&session->ccbs_throttled)) != NULL) {
throttle_ccb(ccb, FALSE);
mutex_exit(&session->lock);
wake_ccb(ccb, ISCSI_STATUS_LOGOUT);
mutex_enter(&session->lock);
}
mutex_exit(&session->lock);
/*
* unmap first to give the system an opportunity to flush its buffers
*/
unmap_session(session);
/* kill all connections */
while ((curr = TAILQ_FIRST(&session->conn_list)) != NULL) {
kill_connection(curr, status, logout, FALSE);
while ((conn = TAILQ_FIRST(&session->conn_list)) != NULL) {
kill_connection(conn, status, logout, FALSE);
logout = NO_LOGOUT;
}
}
@ -676,6 +712,7 @@ create_connection(iscsi_login_parameters_t *par, session_t *session,
mutex_init(&connection->lock, MUTEX_DEFAULT, IPL_BIO);
cv_init(&connection->conn_cv, "conn");
cv_init(&connection->pdu_cv, "pdupool");
cv_init(&connection->ccb_cv, "ccbwait");
cv_init(&connection->idle_cv, "idle");
@ -691,6 +728,7 @@ create_connection(iscsi_login_parameters_t *par, session_t *session,
cv_destroy(&connection->idle_cv);
cv_destroy(&connection->ccb_cv);
cv_destroy(&connection->pdu_cv);
cv_destroy(&connection->conn_cv);
mutex_destroy(&connection->lock);
free(connection, M_DEVBUF);
@ -707,7 +745,7 @@ create_connection(iscsi_login_parameters_t *par, session_t *session,
connection->login_par = par;
DEB(5, ("Creating receive thread\n"));
if ((rc = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, iscsi_rcv_thread,
if ((rc = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, iscsi_rcv_thread,
connection, &connection->rcvproc,
"ConnRcv")) != 0) {
DEBOUT(("Can't create rcv thread (rc %d)\n", rc));
@ -715,6 +753,7 @@ create_connection(iscsi_login_parameters_t *par, session_t *session,
release_socket(connection->sock);
cv_destroy(&connection->idle_cv);
cv_destroy(&connection->ccb_cv);
cv_destroy(&connection->pdu_cv);
cv_destroy(&connection->conn_cv);
mutex_destroy(&connection->lock);
free(connection, M_DEVBUF);
@ -722,7 +761,7 @@ create_connection(iscsi_login_parameters_t *par, session_t *session,
return rc;
}
DEB(5, ("Creating send thread\n"));
if ((rc = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, iscsi_send_thread,
if ((rc = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, iscsi_send_thread,
connection, &connection->sendproc,
"ConnSend")) != 0) {
DEBOUT(("Can't create send thread (rc %d)\n", rc));
@ -746,6 +785,7 @@ create_connection(iscsi_login_parameters_t *par, session_t *session,
release_socket(connection->sock);
cv_destroy(&connection->idle_cv);
cv_destroy(&connection->ccb_cv);
cv_destroy(&connection->pdu_cv);
cv_destroy(&connection->conn_cv);
mutex_destroy(&connection->lock);
free(connection, M_DEVBUF);
@ -767,12 +807,21 @@ create_connection(iscsi_login_parameters_t *par, session_t *session,
return -1;
}
mutex_enter(&session->lock);
if (session->terminating) {
DEBC(connection, 0, ("Session terminating\n"));
kill_connection(connection, rc, NO_LOGOUT, FALSE);
mutex_exit(&session->lock);
par->status = session->terminating;
return -1;
}
connection->state = ST_FULL_FEATURE;
TAILQ_INSERT_TAIL(&session->conn_list, connection, connections);
connection->in_session = TRUE;
session->total_connections++;
session->active_connections++;
session->mru_connection = connection;
mutex_exit(&session->lock);
DEBC(connection, 5, ("Connection created successfully!\n"));
return 0;
@ -844,7 +893,13 @@ recreate_connection(iscsi_login_parameters_t *par, session_t *session,
session->active_connections++;
TAILQ_INIT(&old_waiting);
TAILQ_CONCAT(&old_waiting, &connection->ccbs_waiting, chain);
mutex_enter(&connection->lock);
while ((ccb = TAILQ_FIRST(&connection->ccbs_waiting)) != NULL) {
suspend_ccb(ccb, FALSE);
TAILQ_INSERT_TAIL(&old_waiting, ccb, chain);
}
mutex_exit(&connection->lock);
init_sernum(&connection->StatSN_buf);
cv_broadcast(&connection->idle_cv);
@ -869,7 +924,9 @@ recreate_connection(iscsi_login_parameters_t *par, session_t *session,
while ((ccb = TAILQ_FIRST(&old_waiting)) != NULL) {
TAILQ_REMOVE(&old_waiting, ccb, chain);
mutex_enter(&connection->lock);
suspend_ccb(ccb, TRUE);
mutex_exit(&connection->lock);
rc = send_task_management(connection, ccb, NULL, TASK_REASSIGN);
/* if we get an error on reassign, restart the original request */
@ -877,17 +934,22 @@ recreate_connection(iscsi_login_parameters_t *par, session_t *session,
mutex_enter(&session->lock);
if (sn_a_lt_b(ccb->CmdSN, session->ExpCmdSN)) {
pdu = ccb->pdu_waiting;
sn = get_sernum(session, !(pdu->pdu.Opcode & OP_IMMEDIATE));
sn = get_sernum(session, pdu);
/* update CmdSN */
DEBC(connection, 1, ("Resend Updating CmdSN - old %d, new %d\n",
ccb->CmdSN, sn));
DEBC(connection, 0, ("Resend ccb %p (%d) - updating CmdSN old %u, new %u\n",
ccb, rc, ccb->CmdSN, sn));
ccb->CmdSN = sn;
pdu->pdu.p.command.CmdSN = htonl(ccb->CmdSN);
} else {
DEBC(connection, 0, ("Resend ccb %p (%d) - CmdSN %u\n",
ccb, rc, ccb->CmdSN));
}
mutex_exit(&session->lock);
resend_pdu(ccb);
} else {
DEBC(connection, 0, ("Resend ccb %p (%d) CmdSN %u - reassigned\n",
ccb, rc, ccb->CmdSN));
ccb_timeout_start(ccb, COMMAND_TIMEOUT);
}
}
@ -1011,7 +1073,6 @@ login(iscsi_login_parameters_t *par, struct lwp *l, device_t dev)
}
TAILQ_INIT(&session->conn_list);
TAILQ_INIT(&session->ccb_pool);
TAILQ_INIT(&session->ccbs_throttled);
mutex_init(&session->lock, MUTEX_DEFAULT, IPL_BIO);
cv_init(&session->sess_cv, "session");
@ -1116,8 +1177,6 @@ add_connection(iscsi_login_parameters_t *par, struct lwp *l)
if ((par->status = check_login_pars(par)) == 0) {
create_connection(par, session, l);
}
iscsi_notify_cleanup();
}
@ -1516,10 +1575,11 @@ get_version(iscsi_get_version_parameters_t *par)
* Terminate all sessions (called when the driver unloads).
*/
void
int
kill_all_sessions(void)
{
session_t *sess;
int rc = 0;
mutex_enter(&iscsi_cleanup_mtx);
while ((sess = TAILQ_FIRST(&iscsi_sessions)) != NULL) {
@ -1528,7 +1588,13 @@ kill_all_sessions(void)
FALSE);
mutex_enter(&iscsi_cleanup_mtx);
}
if (TAILQ_FIRST(&iscsi_sessions) != NULL) {
DEBOUT(("Failed to kill all sessions\n"));
rc = EBUSY;
}
mutex_exit(&iscsi_cleanup_mtx);
return rc;
}
/*
@ -1566,6 +1632,7 @@ add_connection_cleanup(connection_t *conn)
mutex_enter(&iscsi_cleanup_mtx);
TAILQ_INSERT_TAIL(&iscsi_cleanupc_list, conn, connections);
mutex_exit(&iscsi_cleanup_mtx);
iscsi_notify_cleanup();
}
/*
@ -1658,24 +1725,19 @@ static void
iscsi_cleanup_thread(void *par)
{
int s, rc;
connection_t *conn;
session_t *sess, *nxts;
connection_t *conn, *nxtc;
ccb_t *ccb;
session_t *sess, *nxt;
uint32_t status;
#ifdef ISCSI_DEBUG
int last_usecount;
#endif
mutex_enter(&iscsi_cleanup_mtx);
while ((conn = TAILQ_FIRST(&iscsi_cleanupc_list)) != NULL ||
iscsi_num_send_threads ||
!iscsi_detaching) {
if (conn != NULL) {
while (iscsi_num_send_threads || !iscsi_detaching ||
!TAILQ_EMPTY(&iscsi_cleanupc_list) || !TAILQ_EMPTY(&iscsi_cleanups_list)) {
TAILQ_FOREACH_SAFE(conn, &iscsi_cleanupc_list, connections, nxtc) {
TAILQ_REMOVE(&iscsi_cleanupc_list, conn, connections);
mutex_exit(&iscsi_cleanup_mtx);
sess = conn->session;
status = conn->terminating;
/*
* This implies that connection cleanup only runs when
@ -1683,97 +1745,102 @@ iscsi_cleanup_thread(void *par)
*/
DEBC(conn, 5, ("Cleanup: Waiting for threads to exit\n"));
while (conn->sendproc || conn->rcvproc)
kpause("termwait", false, hz, NULL);
kpause("threads", false, hz, NULL);
last_usecount = 0;
while (conn->usecount > 0) {
if (conn->usecount != last_usecount) {
DEBC(conn, 5,("Cleanup: %d CCBs busy\n", conn->usecount));
last_usecount = conn->usecount;
mutex_enter(&conn->lock);
TAILQ_FOREACH(ccb, &conn->ccbs_waiting, chain) {
DEBC(conn, 5,("Cleanup: ccb=%p disp=%d timedout=%d\n", ccb,ccb->disp, ccb->timedout));
}
mutex_exit(&conn->lock);
}
kpause("finalwait", false, hz, NULL);
for (s=1; conn->usecount > 0 && s < 3; ++s)
kpause("usecount", false, hz, NULL);
if (conn->usecount > 0) {
DEBC(conn, 5, ("Cleanup: %d CCBs busy\n", conn->usecount));
/* retry later */
mutex_enter(&iscsi_cleanup_mtx);
TAILQ_INSERT_HEAD(&iscsi_cleanupc_list, conn, connections);
continue;
}
KASSERT(!conn->in_session);
callout_halt(&conn->timeout, NULL);
closef(conn->sock);
cv_destroy(&conn->idle_cv);
cv_destroy(&conn->ccb_cv);
cv_destroy(&conn->pdu_cv);
cv_destroy(&conn->conn_cv);
mutex_destroy(&conn->lock);
free(conn, M_DEVBUF);
if (--sess->total_connections == 0) {
DEB(1, ("Cleanup: session %d\n", sess->id));
TAILQ_INSERT_HEAD(&iscsi_cleanups_list, sess, sessions);
}
TAILQ_FOREACH_SAFE(sess, &iscsi_cleanups_list, sessions, nxt) {
if (sess->total_connections != 0)
continue;
TAILQ_REMOVE(&iscsi_cleanups_list, sess, sessions);
DEB(1, ("Cleanup: Unmap session %d\n", sess->id));
rc = unmap_session(sess);
if (rc == 0) {
DEB(1, ("Cleanup: Unmap session %d failed\n", sess->id));
TAILQ_INSERT_HEAD(&iscsi_cleanups_list, sess, sessions);
}
if (sess->target_list != NULL)
free(sess->target_list, M_TEMP);
/* notify event handlers of session shutdown */
add_event(ISCSI_SESSION_TERMINATED, sess->id, 0, status);
DEB(1, ("Cleanup: session ended %d\n", sess->id));
cv_destroy(&sess->ccb_cv);
cv_destroy(&sess->sess_cv);
mutex_destroy(&sess->lock);
free(sess, M_DEVBUF);
}
DEB(5, ("Cleanup: Done\n"));
mutex_enter(&iscsi_cleanup_mtx);
} else {
/* Go to sleep, but wake up every 30 seconds to
* check for dead event handlers */
rc = cv_timedwait(&iscsi_cleanup_cv, &iscsi_cleanup_mtx,
(TAILQ_FIRST(&event_handlers)) ? 30 * hz : 0);
/* handle ccb timeouts */
while ((ccb = TAILQ_FIRST(&iscsi_timeout_ccb_list)) != NULL) {
TAILQ_REMOVE(&iscsi_timeout_ccb_list, ccb, tchain);
KASSERT(ccb->timedout == TOUT_QUEUED);
ccb->timedout = TOUT_BUSY;
mutex_exit(&iscsi_cleanup_mtx);
ccb_timeout(ccb);
mutex_enter(&iscsi_cleanup_mtx);
if (ccb->timedout == TOUT_BUSY)
ccb->timedout = TOUT_NONE;
if (--sess->total_connections == 0) {
DEB(1, ("Cleanup: session %d\n", sess->id));
KASSERT(sess->sessions.tqe_prev == NULL);
TAILQ_INSERT_HEAD(&iscsi_cleanups_list, sess, sessions);
}
/* handle connection timeouts */
while ((conn = TAILQ_FIRST(&iscsi_timeout_conn_list)) != NULL) {
TAILQ_REMOVE(&iscsi_timeout_conn_list, conn, tchain);
KASSERT(conn->timedout == TOUT_QUEUED);
conn->timedout = TOUT_BUSY;
mutex_exit(&iscsi_cleanup_mtx);
connection_timeout(conn);
mutex_enter(&iscsi_cleanup_mtx);
if (conn->timedout == TOUT_BUSY)
conn->timedout = TOUT_NONE;
}
/* if timed out, not woken up */
if (rc == EWOULDBLOCK)
check_event_handlers();
}
TAILQ_FOREACH_SAFE(sess, &iscsi_cleanups_list, sessions, nxts) {
if (sess->refcount > 0)
continue;
TAILQ_REMOVE(&iscsi_cleanups_list, sess, sessions);
sess->sessions.tqe_next = NULL;
sess->sessions.tqe_prev = NULL;
mutex_exit(&iscsi_cleanup_mtx);
DEB(1, ("Cleanup: Unmap session %d\n", sess->id));
if (unmap_session(sess) == 0) {
DEB(1, ("Cleanup: Unmap session %d failed\n", sess->id));
mutex_enter(&iscsi_cleanup_mtx);
TAILQ_INSERT_HEAD(&iscsi_cleanups_list, sess, sessions);
continue;
}
if (sess->target_list != NULL)
free(sess->target_list, M_TEMP);
/* notify event handlers of session shutdown */
add_event(ISCSI_SESSION_TERMINATED, sess->id, 0, sess->terminating);
DEB(1, ("Cleanup: session ended %d\n", sess->id));
cv_destroy(&sess->ccb_cv);
cv_destroy(&sess->sess_cv);
mutex_destroy(&sess->lock);
free(sess, M_DEVBUF);
mutex_enter(&iscsi_cleanup_mtx);
}
/* handle ccb timeouts */
while ((ccb = TAILQ_FIRST(&iscsi_timeout_ccb_list)) != NULL) {
TAILQ_REMOVE(&iscsi_timeout_ccb_list, ccb, tchain);
KASSERT(ccb->timedout == TOUT_QUEUED);
ccb->timedout = TOUT_BUSY;
mutex_exit(&iscsi_cleanup_mtx);
ccb_timeout(ccb);
mutex_enter(&iscsi_cleanup_mtx);
if (ccb->timedout == TOUT_BUSY)
ccb->timedout = TOUT_NONE;
}
/* handle connection timeouts */
while ((conn = TAILQ_FIRST(&iscsi_timeout_conn_list)) != NULL) {
TAILQ_REMOVE(&iscsi_timeout_conn_list, conn, tchain);
KASSERT(conn->timedout == TOUT_QUEUED);
conn->timedout = TOUT_BUSY;
mutex_exit(&iscsi_cleanup_mtx);
connection_timeout(conn);
mutex_enter(&iscsi_cleanup_mtx);
if (conn->timedout == TOUT_BUSY)
conn->timedout = TOUT_NONE;
}
/* Go to sleep, but wake up every 30 seconds to
* check for dead event handlers */
rc = cv_timedwait(&iscsi_cleanup_cv, &iscsi_cleanup_mtx,
(TAILQ_FIRST(&event_handlers)) ? 120 * hz : 0);
/* if timed out, not woken up */
if (rc == EWOULDBLOCK)
check_event_handlers();
}
mutex_exit(&iscsi_cleanup_mtx);
@ -1807,7 +1874,7 @@ iscsi_init_cleanup(void)
}
}
void
int
iscsi_destroy_cleanup(void)
{
@ -1822,6 +1889,8 @@ iscsi_destroy_cleanup(void)
cv_destroy(&iscsi_event_cv);
cv_destroy(&iscsi_cleanup_cv);
mutex_destroy(&iscsi_cleanup_mtx);
return 0;
}
void

View File

@ -262,6 +262,7 @@ static int
iscsi_detach(device_t self, int flags)
{
struct iscsi_softc *sc;
int error;
DEB(1, ("ISCSI: detach\n"));
sc = (struct iscsi_softc *) device_private(self);
@ -274,8 +275,13 @@ iscsi_detach(device_t self, int flags)
iscsi_detaching = true;
mutex_exit(&sc->lock);
kill_all_sessions();
iscsi_destroy_cleanup();
error = kill_all_sessions();
if (error)
return error;
error = iscsi_destroy_cleanup();
if (error)
return error;
mutex_destroy(&sc->lock);
@ -348,6 +354,10 @@ map_session(session_t *session, device_t dev)
struct scsipi_channel *chan = &session->sc_channel;
const quirktab_t *tgt;
mutex_enter(&session->lock);
session->send_window = max(2, window_size(session, CCBS_FOR_SCSIPI));
mutex_exit(&session->lock);
/*
* Fill in the scsipi_adapter.
*/
@ -355,8 +365,8 @@ map_session(session_t *session, device_t dev)
adapt->adapt_nchannels = 1;
adapt->adapt_request = iscsi_scsipi_request;
adapt->adapt_minphys = iscsi_minphys;
adapt->adapt_openings = CCBS_PER_SESSION - 1;
adapt->adapt_max_periph = CCBS_PER_SESSION - 1;
adapt->adapt_openings = session->send_window;
adapt->adapt_max_periph = CCBS_FOR_SCSIPI;
/*
* Fill in the scsipi_channel.
@ -369,7 +379,7 @@ map_session(session_t *session, device_t dev)
chan->chan_adapter = adapt;
chan->chan_bustype = &scsi_bustype;
chan->chan_channel = 0;
chan->chan_flags = SCSIPI_CHAN_NOSETTLE;
chan->chan_flags = SCSIPI_CHAN_NOSETTLE | SCSIPI_CHAN_CANGROW;
chan->chan_ntargets = 1;
chan->chan_nluns = 16; /* ToDo: ??? */
chan->chan_id = session->id;
@ -405,6 +415,29 @@ unmap_session(session_t *session)
return rv;
}
/*
* grow_resources
* Try to grow openings up to current window size
*/
static void
grow_resources(session_t *session)
{
struct scsipi_adapter *adapt = &session->sc_adapter;
int win;
mutex_enter(&session->lock);
if (session->refcount < CCBS_FOR_SCSIPI &&
session->send_window < CCBS_FOR_SCSIPI) {
win = window_size(session, CCBS_FOR_SCSIPI - session->refcount);
if (win > session->send_window) {
session->send_window++;
adapt->adapt_openings++;
DEB(5, ("Grow send window to %d\n", session->send_window));
}
}
mutex_exit(&session->lock);
}
/******************************************************************************/
/*****************************************************************************
@ -425,20 +458,32 @@ iscsi_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
session_t *session;
int flags;
struct scsipi_xfer_mode *xm;
int error;
session = (session_t *) adapt; /* adapter is first field in session */
error = ref_session(session);
switch (req) {
case ADAPTER_REQ_RUN_XFER:
DEB(9, ("ISCSI: scsipi_request RUN_XFER\n"));
xs = arg;
flags = xs->xs_control;
if (error) {
DEB(9, ("ISCSI: refcount too high: %d, winsize %d\n",
session->refcount, session->send_window));
xs->error = XS_BUSY;
xs->status = XS_BUSY;
scsipi_done(xs);
return;
}
if ((flags & XS_CTL_POLL) != 0) {
xs->error = XS_DRIVER_STUFFUP;
DEBOUT(("Run Xfer request with polling\n"));
scsipi_done(xs);
return;
break;
}
/*
* NOTE: It appears that XS_CTL_DATA_UIO is not actually used anywhere.
@ -451,28 +496,32 @@ iscsi_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
xs->error = XS_DRIVER_STUFFUP;
DEBOUT(("Run Xfer with data in UIO\n"));
scsipi_done(xs);
return;
break;
}
send_run_xfer(session, xs);
DEB(15, ("scsipi_req returns\n"));
DEB(15, ("scsipi_req returns, refcount = %d\n", session->refcount));
return;
case ADAPTER_REQ_GROW_RESOURCES:
DEB(5, ("ISCSI: scsipi_request GROW_RESOURCES\n"));
return;
grow_resources(session);
break;
case ADAPTER_REQ_SET_XFER_MODE:
DEB(5, ("ISCSI: scsipi_request SET_XFER_MODE\n"));
xm = (struct scsipi_xfer_mode *)arg;
xm->xm_mode = PERIPH_CAP_TQING;
scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm);
return;
break;
default:
DEBOUT(("ISCSI: scsipi_request with invalid REQ code %d\n", req));
break;
}
DEBOUT(("ISCSI: scsipi_request with invalid REQ code %d\n", req));
if (!error)
unref_session(session);
}
/* cap the transfer at 64K */
@ -522,6 +571,8 @@ iscsi_done(ccb_t *ccb)
break;
case ISCSI_STATUS_TARGET_BUSY:
case ISCSI_STATUS_NO_RESOURCES:
DEBC(ccb->connection, 5, ("target busy, ccb %p\n", ccb));
xs->error = XS_BUSY;
xs->status = SCSI_BUSY;
break;
@ -532,7 +583,8 @@ iscsi_done(ccb_t *ccb)
xs->status = SCSI_BUSY;
break;
case ISCSI_STATUS_NO_RESOURCES:
case ISCSI_STATUS_QUEUE_FULL:
DEBC(ccb->connection, 5, ("queue full, ccb %p\n", ccb));
xs->error = XS_BUSY;
xs->status = SCSI_QUEUE_FULL;
break;
@ -550,6 +602,8 @@ iscsi_done(ccb_t *ccb)
} else {
DEBOUT(("ISCSI: iscsi_done CCB %p without XS\n", ccb));
}
unref_session(ccb->session);
}
SYSCTL_SETUP(sysctl_iscsi_setup, "ISCSI subtree setup")

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_rcv.c,v 1.20 2016/06/05 14:00:12 mlelstv Exp $ */
/* $NetBSD: iscsi_rcv.c,v 1.21 2016/06/15 04:30:30 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@ -645,7 +645,8 @@ receive_data_in_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
done = sn_empty(&req_ccb->DataSN_buf);
if (pdu->pdu.Flags & FLAG_STATUS) {
DEBC(conn, 10, ("Rx Data In Complete, done = %d\n", done));
DEBC(conn, 10, ("Rx Data In %d, done = %d\n",
req_ccb->CmdSN, done));
req_ccb->flags |= CCBF_COMPLETE;
/* successful transfer, reset recover count */
@ -760,8 +761,10 @@ receive_command_response_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
done = status || sn_empty(&req_ccb->DataSN_buf);
DEBC(conn, 10, ("Rx Command Response rsp = %x, status = %x\n",
pdu->pdu.OpcodeSpecific[0], pdu->pdu.OpcodeSpecific[1]));
DEBC(conn, 10, ("Rx Response: CmdSN %d, rsp = %x, status = %x\n",
req_ccb->CmdSN,
pdu->pdu.OpcodeSpecific[0],
pdu->pdu.OpcodeSpecific[1]));
rc = check_StatSN(conn, pdu->pdu.p.response.StatSN, done);
@ -968,7 +971,7 @@ STATIC int
receive_nop_in_pdu(connection_t *conn, pdu_t *pdu, ccb_t *req_ccb)
{
DEBC(conn, 10,
("Received NOP_In PDU, req_ccb=%p, ITT=%x, TTT=%x, StatSN=%x\n",
("Received NOP_In PDU, req_ccb=%p, ITT=%x, TTT=%x, StatSN=%u\n",
req_ccb, pdu->pdu.InitiatorTaskTag,
pdu->pdu.p.nop_in.TargetTransferTag,
ntohl(pdu->pdu.p.nop_in.StatSN)));
@ -1019,7 +1022,6 @@ STATIC int
receive_pdu(connection_t *conn, pdu_t *pdu)
{
ccb_t *req_ccb;
ccb_list_t waiting;
int rc;
uint32_t MaxCmdSN, ExpCmdSN, digest;
session_t *sess = conn->session;
@ -1036,9 +1038,11 @@ receive_pdu(connection_t *conn, pdu_t *pdu)
}
}
DEBC(conn, 99, ("Received PDU ExpCmdSN = %u, MaxCmdSN = %u\n",
DEBC(conn, 10, ("Received PDU StatSN=%u, ExpCmdSN=%u MaxCmdSN=%u ExpDataSN=%u\n",
ntohl(pdu->pdu.p.response.StatSN),
ntohl(pdu->pdu.p.response.ExpCmdSN),
ntohl(pdu->pdu.p.response.ExpCmdSN)));
ntohl(pdu->pdu.p.response.MaxCmdSN),
ntohl(pdu->pdu.p.response.ExpDataSN)));
req_ccb = ccb_from_itt(conn, pdu->pdu.InitiatorTaskTag);
@ -1131,65 +1135,14 @@ receive_pdu(connection_t *conn, pdu_t *pdu)
connection_timeout_start(conn, CONNECTION_TIMEOUT);
conn->num_timeouts = 0;
/*
* Un-throttle - wakeup all CCBs waiting for MaxCmdSN to increase.
* We have to handle wait/nowait CCBs a bit differently.
*/
/* Update session window */
mutex_enter(&sess->lock);
if (sn_a_lt_b(MaxCmdSN, ExpCmdSN-1)) {
/* both are ignored */
mutex_exit(&sess->lock);
return 0;
if (sn_a_le_b(ExpCmdSN - 1, MaxCmdSN)) {
if (sn_a_lt_b(sess->ExpCmdSN, ExpCmdSN))
sess->ExpCmdSN = ExpCmdSN;
if (sn_a_lt_b(sess->MaxCmdSN, MaxCmdSN))
sess->MaxCmdSN = MaxCmdSN;
}
if (sn_a_lt_b(sess->ExpCmdSN, ExpCmdSN))
sess->ExpCmdSN = ExpCmdSN;
if (sn_a_lt_b(sess->MaxCmdSN, MaxCmdSN)) {
sess->MaxCmdSN = MaxCmdSN;
if (TAILQ_FIRST(&sess->ccbs_throttled) == NULL) {
mutex_exit(&sess->lock);
return 0;
}
DEBC(conn, 5, ("Unthrottling - MaxCmdSN = %d\n", MaxCmdSN));
TAILQ_INIT(&waiting);
while ((req_ccb = TAILQ_FIRST(&sess->ccbs_throttled)) != NULL) {
if (!conn->terminating ||
(req_ccb->flags & CCBF_WAITING) != 0) {
throttle_ccb(req_ccb, FALSE);
TAILQ_INSERT_TAIL(&waiting, req_ccb, chain);
}
}
while ((req_ccb = TAILQ_FIRST(&waiting)) != NULL) {
if (!sernum_in_window(sess))
break;
mutex_exit(&sess->lock);
TAILQ_REMOVE(&waiting, req_ccb, chain);
DEBC(conn, 10, ("Unthrottling - ccb = %p, disp = %d\n",
req_ccb, req_ccb->disp));
if ((req_ccb->flags & CCBF_WAITING) != 0) {
cv_broadcast(&conn->ccb_cv);
} else {
send_command(req_ccb, req_ccb->disp, FALSE, FALSE);
}
mutex_enter(&sess->lock);
}
while ((req_ccb = TAILQ_FIRST(&waiting)) != NULL) {
TAILQ_REMOVE(&waiting, req_ccb, chain);
throttle_ccb(req_ccb, TRUE);
}
}
mutex_exit(&sess->lock);
return 0;
@ -1215,6 +1168,11 @@ iscsi_rcv_thread(void *par)
do {
while (!conn->terminating) {
pdu = get_pdu(conn, TRUE);
if (pdu == NULL) {
KASSERT(conn->terminating);
break;
}
pdu->uio.uio_iov = pdu->io_vec;
UIO_SETUP_SYSSPACE(&pdu->uio);
pdu->uio.uio_iovcnt = 1;

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_send.c,v 1.30 2016/06/05 13:54:28 mlelstv Exp $ */
/* $NetBSD: iscsi_send.c,v 1.31 2016/06/15 04:30:30 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
@ -93,7 +93,8 @@ assign_connection(session_t *session, bool waitok)
mutex_enter(&session->lock);
do {
if ((conn = session->mru_connection) == NULL) {
if (session->terminating ||
(conn = session->mru_connection) == NULL) {
mutex_exit(&session->lock);
return NULL;
}
@ -232,7 +233,9 @@ reassign_tasks(connection_t *oldconn)
free_pdu(opdu);
/* put ready CCB into waiting list of new connection */
mutex_enter(&conn->lock);
suspend_ccb(ccb, TRUE);
mutex_exit(&conn->lock);
}
if (pdu == NULL) {
@ -255,7 +258,7 @@ reassign_tasks(connection_t *oldconn)
mutex_enter(&sess->lock);
if (ccb->CmdSN < sess->ExpCmdSN) {
pdu = ccb->pdu_waiting;
sn = get_sernum(sess, !(pdu->pdu.Opcode & OP_IMMEDIATE));
sn = get_sernum(sess, pdu);
/* update CmdSN */
DEBC(conn, 1, ("Resend Updating CmdSN - old %d, new %d\n",
@ -312,18 +315,17 @@ iscsi_send_thread(void *par)
if (conn->HeaderDigest)
pdu->pdu.HeaderDigest = gen_digest(&pdu->pdu, BHS_SIZE);
DEBC(conn, 99, ("Transmitting PDU CmdSN = %u\n",
ntohl(pdu->pdu.p.command.CmdSN)));
DEBC(conn, 99, ("Transmitting PDU CmdSN = %u, ExpStatSN = %u\n",
ntohl(pdu->pdu.p.command.CmdSN),
ntohl(pdu->pdu.p.command.ExpStatSN)));
my_soo_write(conn, &pdu->uio);
mutex_enter(&conn->lock);
pdisp = pdu->disp;
if (pdisp <= PDUDISP_FREE)
pdu->disp = PDUDISP_UNUSED;
else
if (pdisp > PDUDISP_FREE)
pdu->flags &= ~PDUF_BUSY;
mutex_exit(&conn->lock);
if (pdisp <= PDUDISP_FREE)
free_pdu(pdu);
@ -459,8 +461,10 @@ send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_disp_t cdisp, pdu_disp_t pdisp)
pdu->disp = pdisp;
DEBC(conn, 10, ("Send_pdu: ccb=%p, pcd=%d, cdsp=%d, pdu=%p, pdsp=%d\n",
ccb, prev_cdisp, cdisp, pdu, pdisp));
DEBC(conn, 10, ("Send_pdu: CmdSN=%u ExpStatSN~%u ccb=%p, pdu=%p\n",
ntohl(pdu->pdu.p.command.CmdSN),
conn->StatSN_buf.ExpSN,
ccb, pdu));
mutex_enter(&conn->lock);
if (pdisp == PDUDISP_WAIT) {
@ -486,10 +490,9 @@ send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_disp_t cdisp, pdu_disp_t pdisp)
if (cdisp != CCBDISP_NOWAIT) {
ccb_timeout_start(ccb, COMMAND_TIMEOUT);
mutex_enter(&conn->lock);
if (prev_cdisp <= CCBDISP_NOWAIT)
suspend_ccb(ccb, TRUE);
mutex_enter(&conn->lock);
while (ccb->disp == CCBDISP_WAIT) {
DEBC(conn, 15, ("Send_pdu: ccb=%p cdisp=%d waiting\n",
ccb, ccb->disp));
@ -528,7 +531,10 @@ resend_pdu(ccb_t *ccb)
pdu->uio = pdu->save_uio;
memcpy(pdu->io_vec, pdu->save_iovec, sizeof(pdu->io_vec));
DEBC(conn, 8, ("ReSend_pdu ccb=%p, pdu=%p\n", ccb, pdu));
DEBC(conn, 8, ("ReSend_pdu: CmdSN=%u ExpStatSN~%u ccb=%p, pdu=%p\n",
ntohl(pdu->pdu.p.command.CmdSN),
conn->StatSN_buf.ExpSN,
ccb, pdu));
mutex_enter(&conn->lock);
/* Enqueue for sending */
@ -632,7 +638,7 @@ init_login_pdu(connection_t *conn, ccb_t *ccb, pdu_t *ppdu, bool next)
pdu->Opcode = IOP_Login_Request | OP_IMMEDIATE;
mutex_enter(&conn->session->lock);
ccb->CmdSN = get_sernum(conn->session, false);
ccb->CmdSN = get_sernum(conn->session, ppdu);
mutex_exit(&conn->session->lock);
if (next) {
@ -746,7 +752,7 @@ init_text_pdu(connection_t *conn, ccb_t *ccb, pdu_t *ppdu, pdu_t *rx_pdu)
pdu->Flags = FLAG_FINAL;
mutex_enter(&conn->session->lock);
ccb->CmdSN = get_sernum(conn->session, false);
ccb->CmdSN = get_sernum(conn->session, ppdu);
mutex_exit(&conn->session->lock);
if (rx_pdu != NULL) {
@ -952,11 +958,11 @@ send_send_targets(session_t *session, uint8_t *key)
int
send_nop_out(connection_t *conn, pdu_t *rx_pdu)
{
session_t *sess;
ccb_t *ccb;
pdu_t *ppdu;
pdu_header_t *pdu;
DEBC(conn, 10, ("Send NOP_Out rx_pdu=%p\n", rx_pdu));
uint32_t sn;
if (rx_pdu != NULL) {
ccb = NULL;
@ -981,18 +987,27 @@ send_nop_out(connection_t *conn, pdu_t *rx_pdu)
pdu->Flags = FLAG_FINAL;
pdu->Opcode = IOP_NOP_Out | OP_IMMEDIATE;
sess = conn->session;
mutex_enter(&sess->lock);
sn = get_sernum(sess, ppdu);
mutex_exit(&sess->lock);
if (rx_pdu != NULL) {
pdu->p.nop_out.TargetTransferTag =
rx_pdu->pdu.p.nop_in.TargetTransferTag;
pdu->InitiatorTaskTag = rx_pdu->pdu.InitiatorTaskTag;
pdu->p.nop_out.CmdSN = htonl(conn->session->CmdSN);
pdu->p.nop_out.CmdSN = htonl(sn);
pdu->LUN = rx_pdu->pdu.LUN;
} else {
pdu->p.nop_out.TargetTransferTag = 0xffffffff;
ccb->CmdSN = ccb->session->CmdSN;
pdu->p.nop_out.CmdSN = htonl(ccb->CmdSN);
pdu->InitiatorTaskTag = 0xffffffff;
ccb->CmdSN = sn;
pdu->p.nop_out.CmdSN = htonl(sn);
}
DEBC(conn, 10, ("Send NOP_Out CmdSN=%d, rx_pdu=%p\n", sn, rx_pdu));
setup_tx_uio(ppdu, 0, NULL, FALSE);
send_pdu(ccb, ppdu, (rx_pdu != NULL) ? CCBDISP_NOWAIT : CCBDISP_FREE,
PDUDISP_FREE);
@ -1364,30 +1379,22 @@ send_command(ccb_t *ccb, ccb_disp_t disp, bool waitok, bool immed)
pdu_header_t *pdu;
mutex_enter(&sess->lock);
while (/*CONSTCOND*/ISCSI_THROTTLING_ENABLED &&
/*CONSTCOND*/!ISCSI_SERVER_TRUSTED &&
!sernum_in_window(sess)) {
while (!sernum_in_window(sess)) {
mutex_exit(&sess->lock);
ccb->disp = disp;
if (waitok)
ccb->flags |= CCBF_WAITING;
throttle_ccb(ccb, TRUE);
if (!waitok) {
mutex_exit(&sess->lock);
DEBC(conn, 10, ("Throttling send_command, ccb = %p\n",ccb));
return;
}
DEBC(conn, 15, ("Wait send_command, ccb = %p\n",ccb));
cv_wait(&sess->ccb_cv, &sess->lock);
DEBC(conn, 15, ("Resuming send_command, ccb = %p\n",ccb));
throttle_ccb(ccb, FALSE);
ccb->flags &= ~CCBF_WAITING;
wake_ccb(ccb, ISCSI_STATUS_QUEUE_FULL);
return;
}
mutex_exit(&sess->lock);
/* Don't confuse targets during (re-)negotations */
if (conn->state != ST_FULL_FEATURE) {
DEBOUT(("Invalid connection for send_command, ccb = %p\n",ccb));
ccb->disp = disp;
wake_ccb(ccb, ISCSI_STATUS_TARGET_BUSY);
return;
}
ppdu = get_pdu(conn, waitok);
if (ppdu == NULL) {
DEBOUT(("No PDU for send_command, ccb = %p\n",ccb));
@ -1432,7 +1439,7 @@ send_command(ccb_t *ccb, ccb_disp_t disp, bool waitok, bool immed)
ccb->flags |= CCBF_REASSIGN;
mutex_enter(&sess->lock);
ccb->CmdSN = get_sernum(sess, !immed);
ccb->CmdSN = get_sernum(sess, ppdu);
mutex_exit(&sess->lock);
pdu->p.command.CmdSN = htonl(ccb->CmdSN);

View File

@ -1,4 +1,4 @@
/* $NetBSD: iscsi_utils.c,v 1.20 2016/06/15 03:51:55 mlelstv Exp $ */
/* $NetBSD: iscsi_utils.c,v 1.21 2016/06/15 04:30:30 mlelstv Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2008 The NetBSD Foundation, Inc.
@ -256,7 +256,6 @@ free_ccb(ccb_t *ccb)
"free_ccb: ccb = %p, usecount = %d\n",
ccb, conn->usecount-1));
KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
atomic_dec_uint(&conn->usecount);
@ -334,44 +333,19 @@ suspend_ccb(ccb_t *ccb, bool yes)
connection_t *conn;
conn = ccb->connection;
KASSERT(mutex_owned(&conn->lock));
if (yes) {
KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
TAILQ_INSERT_TAIL(&conn->ccbs_waiting, ccb, chain);
ccb->flags |= CCBF_WAITQUEUE;
} else if (ccb->flags & CCBF_WAITQUEUE) {
KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
TAILQ_REMOVE(&conn->ccbs_waiting, ccb, chain);
ccb->flags &= ~CCBF_WAITQUEUE;
}
}
/*
* throttle_ccb:
* Put CCB on throttling queue
*/
void
throttle_ccb(ccb_t *ccb, bool yes)
{
session_t *sess;
sess = ccb->session;
KASSERT(mutex_owned(&sess->lock));
if (yes) {
KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
TAILQ_INSERT_TAIL(&sess->ccbs_throttled, ccb, chain);
ccb->flags |= CCBF_THROTTLING;
} else if (ccb->flags & CCBF_THROTTLING) {
KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
TAILQ_REMOVE(&sess->ccbs_throttled, ccb, chain);
ccb->flags &= ~CCBF_THROTTLING;
}
}
/*
* wake_ccb:
* Wake up (or dispose of) a CCB. Depending on the CCB's disposition,
@ -386,15 +360,11 @@ wake_ccb(ccb_t *ccb, uint32_t status)
{
ccb_disp_t disp;
connection_t *conn;
session_t *sess;
conn = ccb->connection;
sess = ccb->session;
#ifdef ISCSI_DEBUG
DEBC(conn, 9, ("CCB done, ccb = %p, disp = %d\n",
ccb, ccb->disp));
#endif
DEBC(conn, 9, ("CCB %d done, ccb = %p, disp = %d\n",
ccb->CmdSN, ccb, ccb->disp));
ccb_timeout_stop(ccb);
@ -413,10 +383,6 @@ wake_ccb(ccb_t *ccb, uint32_t status)
ccb->status = status;
mutex_exit(&conn->lock);
mutex_enter(&sess->lock);
throttle_ccb(ccb, FALSE);
mutex_exit(&sess->lock);
switch (disp) {
case CCBDISP_FREE:
free_ccb(ccb);
@ -467,22 +433,24 @@ get_pdu(connection_t *conn, bool waitok)
if (pdu != NULL)
TAILQ_REMOVE(&conn->pdu_pool, pdu, chain);
DEB(100, ("get_pdu_c: pdu = %p, waitok = %d\n", pdu, waitok));
if (pdu == NULL) {
if (!waitok || conn->terminating) {
mutex_exit(&conn->lock);
DEB(15, ("get_pdu: failed"));
return NULL;
}
cv_wait(&conn->conn_cv, &conn->lock);
cv_wait(&conn->pdu_cv, &conn->lock);
}
} while (pdu == NULL);
atomic_inc_uint(&conn->pducount);
mutex_exit(&conn->lock);
memset(pdu, 0, sizeof(pdu_t));
pdu->connection = conn;
pdu->disp = PDUDISP_FREE;
DEBC(conn, 15, ("get_pdu: pdu = %p, usecount = %d\n", pdu, conn->pducount));
return pdu;
}
@ -499,6 +467,8 @@ free_pdu(pdu_t *pdu)
connection_t *conn = pdu->connection;
pdu_disp_t pdisp;
DEBC(conn, 15, ("free_pdu: pdu = %p, usecount = %d\n", pdu, conn->pducount-1));
KASSERT((pdu->flags & PDUF_INQUEUE) == 0);
if (PDUDISP_UNUSED == (pdisp = pdu->disp))
@ -510,10 +480,11 @@ free_pdu(pdu_t *pdu)
free(pdu->temp_data, M_TEMP);
mutex_enter(&conn->lock);
atomic_dec_uint(&conn->pducount);
TAILQ_INSERT_TAIL(&conn->pdu_pool, pdu, chain);
mutex_exit(&conn->lock);
cv_broadcast(&conn->conn_cv);
cv_broadcast(&conn->pdu_cv);
}
/*
@ -676,14 +647,14 @@ ack_sernum(sernum_buffer_t *buff, uint32_t num)
* and optionally increment it for the next query
*/
uint32_t
get_sernum(session_t *sess, bool bump)
get_sernum(session_t *sess, pdu_t *pdu)
{
uint32_t sn;
KASSERT(mutex_owned(&sess->lock));
sn = sess->CmdSN;
if (bump)
if ((pdu->pdu.Opcode & OP_IMMEDIATE) == 0)
atomic_inc_32(&sess->CmdSN);
return sn;
}
@ -701,3 +672,22 @@ sernum_in_window(session_t *sess)
return sn_a_le_b(sess->CmdSN, sess->MaxCmdSN);
}
/*
* window_size:
* Compute send window size
*/
int
window_size(session_t *sess, int limit)
{
uint32_t win;
KASSERT(mutex_owned(&sess->lock));
win = 0;
if (sn_a_le_b(sess->CmdSN, sess->MaxCmdSN))
win = sess->MaxCmdSN - sess->CmdSN + 1;
if (win > INT_MAX || win > limit)
win = limit;
return win;
}