// SPDX-License-Identifier: LGPL-2.1 /* * * Copyright (C) International Business Machines Corp., 2002,2008 * Author(s): Steve French (sfrench@us.ibm.com) * Jeremy Allison (jra@samba.org) 2006. *
*/
midEntry->mid_state = MID_FREE;
atomic_dec(&mid_count); if (midEntry->large_buf)
cifs_buf_release(midEntry->resp_buf); else
cifs_small_buf_release(midEntry->resp_buf); #ifdef CONFIG_CIFS_STATS2
now = jiffies; if (now < midEntry->when_alloc)
cifs_server_dbg(VFS, "Invalid mid allocation time\n");
roundtrip_time = now - midEntry->when_alloc;
if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) { if (atomic_read(&server->num_cmds[smb_cmd]) == 0) {
server->slowest_cmd[smb_cmd] = roundtrip_time;
server->fastest_cmd[smb_cmd] = roundtrip_time;
} else { if (server->slowest_cmd[smb_cmd] < roundtrip_time)
server->slowest_cmd[smb_cmd] = roundtrip_time; elseif (server->fastest_cmd[smb_cmd] > roundtrip_time)
server->fastest_cmd[smb_cmd] = roundtrip_time;
}
cifs_stats_inc(&server->num_cmds[smb_cmd]);
server->time_per_cmd[smb_cmd] += roundtrip_time;
} /* * commands taking longer than one second (default) can be indications * that something is wrong, unless it is quite a slow link or a very * busy server. Note that this calc is unlikely or impossible to wrap * as long as slow_rsp_threshold is not set way above recommended max * value (32767 ie 9 hours) and is generally harmless even if wrong * since only affects debug counters - so leaving the calc as simple * comparison rather than doing multiple conversions and overflow * checks
*/ if ((slow_rsp_threshold != 0) &&
time_after(now, midEntry->when_alloc + (slow_rsp_threshold * HZ)) &&
(midEntry->command != command)) { /* * smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command * NB: le16_to_cpu returns unsigned so can not be negative below
*/ if (smb_cmd < NUMBER_OF_SMB2_COMMANDS)
cifs_stats_inc(&server->smb2slowcmd[smb_cmd]);
trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid,
midEntry->when_sent, midEntry->when_received); if (cifsFYI & CIFS_TIMER) {
pr_debug("slow rsp: cmd %d mid %llu",
midEntry->command, midEntry->mid);
cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n",
now - midEntry->when_alloc,
now - midEntry->when_sent,
now - midEntry->when_received);
}
} #endif
put_task_struct(midEntry->creator);
/* * smb_send_kvec - send an array of kvecs to the server * @server: Server to send the data to * @smb_msg: Message to send * @sent: amount of data sent on socket is stored here * * Our basic "send data to server" function. Should be called with srv_mutex * held. The caller is responsible for handling the results.
*/ int
smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
size_t *sent)
{ int rc = 0; int retries = 0; struct socket *ssocket = server->ssocket;
while (msg_data_left(smb_msg)) { /* * If blocking send, we try 3 times, since each can block * for 5 seconds. For nonblocking we have to try more * but wait increasing amounts of time allowing time for * socket to clear. The overall time we wait in either * case to send on the socket is about 15 seconds. * Similarly we wait for 15 seconds for a response from * the server in SendReceive[2] for the server to send * a response back for most types of requests (except * SMB Write past end of file which can be slow, and * blocking lock operations). NFS waits slightly longer * than CIFS, but this can make it take longer for * nonresponsive servers to be detected and 15 seconds * is more than enough time for modern networks to * send a packet. In most cases if we fail to send * after the retries we will kill the socket and * reconnect which may clear the network problem. * * Even if regular signals are masked, EINTR might be * propagated from sk_stream_wait_memory() to here when * TIF_NOTIFY_SIGNAL is used for task work. For example, * certain io_uring completions will use that. Treat * having EINTR with pending task work the same as EAGAIN * to avoid unnecessary reconnects.
*/
rc = sock_sendmsg(ssocket, smb_msg); if (rc == -EAGAIN || unlikely(rc == -EINTR && task_work_pending(current))) {
retries++; if (retries >= 14 ||
(!server->noblocksnd && (retries > 2))) {
cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n",
ssocket); return -EAGAIN;
}
msleep(1 << retries); continue;
}
if (rc < 0) return rc;
if (rc == 0) { /* should never happen, letting socket clear before
retrying is our only obvious option here */
cifs_server_dbg(VFS, "tcp sent no data\n");
msleep(500); continue;
}
/* send was at least partially successful */
*sent += rc;
retries = 0; /* in case we get ENOSPC on the next send */
} return 0;
}
/* * We should not allow signals to interrupt the network send because * any partial send will cause session reconnects thus increasing * latency of system calls and overload a server with unnecessary * requests.
*/
/* * If signal is pending but we have already sent the whole packet to * the server we need to return success status to allow a corresponding * mid entry to be kept in the pending requests queue thus allowing * to handle responses from the server by the client. * * If only part of the packet has been sent there is no need to hide * interrupt because the session will be reconnected anyway, so there * won't be any response from the server to handle.
*/
if (signal_pending(current) && (total_len != send_length)) {
cifs_dbg(FYI, "signal is pending after attempt to send\n");
rc = -ERESTARTSYS;
}
/* uncork it */
tcp_sock_set_cork(ssocket->sk, false);
if ((total_len > 0) && (total_len != send_length)) {
cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n",
send_length, total_len); /* * If we have only sent part of an SMB then the next SMB could * be taken as the remainder of this one. We need to kill the * socket so the server throws away the partial SMB
*/
cifs_signal_cifsd_for_reconnect(server, false);
trace_smb3_partial_send_reconnect(server->current_mid,
server->conn_id, server->hostname);
}
smbd_done: /* * there's hardly any use for the layers above to know the * actual error code here. All they should do at this point is * to retry the connection and hope it goes away.
*/ if (rc < 0 && rc != -EINTR && rc != -EAGAIN) {
cifs_server_dbg(VFS, "Error %d sending data on socket to server\n",
rc);
rc = -ECONNABORTED;
cifs_signal_cifsd_for_reconnect(server, false);
} elseif (rc > 0)
rc = 0;
out:
cifs_in_send_dec(server); return rc;
}
staticint
wait_for_free_credits(struct TCP_Server_Info *server, constint num_credits, constint timeout, constint flags, unsignedint *instance)
{ long rc; int *credits; int optype; longint t; int scredits, in_flight;
if (timeout < 0)
t = MAX_JIFFY_OFFSET; else
t = msecs_to_jiffies(timeout);
optype = flags & CIFS_OP_MASK;
*instance = 0;
credits = server->ops->get_credits_field(server, optype); /* Since an echo is already inflight, no need to wait to send another */ if (*credits <= 0 && optype == CIFS_ECHO_OP) return -EAGAIN;
spin_lock(&server->req_lock); if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) { /* oplock breaks must not be held up */
server->in_flight++; if (server->in_flight > server->max_in_flight)
server->max_in_flight = server->in_flight;
*credits -= 1;
*instance = server->reconnect_instance;
scredits = *credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_credit_timeout(server->current_mid,
server->conn_id, server->hostname, scredits,
num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
timeout); return -EBUSY;
} if (rc == -ERESTARTSYS) return -ERESTARTSYS;
spin_lock(&server->req_lock);
} else { /* * For normal commands, reserve the last MAX_COMPOUND * credits to compound requests. * Otherwise these compounds could be permanently * starved for credits by single-credit requests. * * To prevent spinning CPU, block this thread until * there are >MAX_COMPOUND credits available. * But only do this is we already have a lot of * credits in flight to avoid triggering this check * for servers that are slow to hand out credits on * new sessions.
*/ if (!optype && num_credits == 1 &&
server->in_flight > 2 * MAX_COMPOUND &&
*credits <= MAX_COMPOUND) {
spin_unlock(&server->req_lock);
if (*credits < num) { /* * If the server is tight on resources or just gives us less * credits for other reasons (e.g. requests are coming out of * order and the server delays granting more credits until it * processes a missing mid) and we exhausted most available * credits there may be situations when we try to send * a compound request but we don't have enough credits. At this * point the client needs to decide if it should wait for * additional credits or fail the request. If at least one * request is in flight there is a high probability that the * server will return enough credits to satisfy this compound * request. * * Return immediately if no requests in flight since we will be * stuck on waiting for credits.
*/ if (server->in_flight == 0) {
spin_unlock(&server->req_lock);
trace_smb3_insufficient_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
num, in_flight);
cifs_dbg(FYI, "%s: %d requests in flight, needed %d total=%d\n",
__func__, in_flight, num, scredits); return -EDEADLK;
}
}
spin_unlock(&server->req_lock);
/* * Send a SMB request and set the callback function in the mid to handle * the result. Caller is responsible for dealing with timeouts.
*/ int
cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
mid_receive_t *receive, mid_callback_t *callback,
mid_handle_t *handle, void *cbdata, constint flags, conststruct cifs_credits *exist_credits)
{ int rc; struct mid_q_entry *mid; struct cifs_credits credits = { .value = 0, .instance = 0 }; unsignedint instance; int optype;
/* * We can't use credits obtained from the previous session to send this * request. Check if there were reconnects after we obtained credits and * return -EAGAIN in such cases to let callers handle it.
*/ if (instance != server->reconnect_instance) {
cifs_server_unlock(server);
add_credits_and_wake_if(server, &credits, optype); return -EAGAIN;
}
/* put it on the pending_mid_q */
spin_lock(&server->mid_queue_lock);
list_add_tail(&mid->qhead, &server->pending_mid_q);
spin_unlock(&server->mid_queue_lock);
/* * Need to store the time in mid before calling I/O. For call_async, * I/O response may come back and free the mid entry on another thread.
*/
cifs_save_when_sent(mid);
rc = smb_send_rqst(server, 1, rqst, flags);
/* * Return a channel (master if none) of @ses that can be used to send * regular requests. * * If we are currently binding a new channel (negprot/sess.setup), * return the new incomplete channel.
*/ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
{
uint index = 0; unsignedint min_in_flight = UINT_MAX, max_in_flight = 0; struct TCP_Server_Info *server = NULL; int i, start, cur;
if (!ses) return NULL;
spin_lock(&ses->chan_lock);
start = atomic_inc_return(&ses->chan_seq); for (i = 0; i < ses->chan_count; i++) {
cur = (start + i) % ses->chan_count;
server = ses->chans[cur].server; if (!server || server->terminate) continue;
if (CIFS_CHAN_NEEDS_RECONNECT(ses, cur)) continue;
/* * strictly speaking, we should pick up req_lock to read * server->in_flight. But it shouldn't matter much here if we * race while reading this data. The worst that can happen is * that we could use a channel that's not least loaded. Avoiding * taking the lock could help reduce wait time, which is * important for this function
*/ if (server->in_flight < min_in_flight) {
min_in_flight = server->in_flight;
index = cur;
} if (server->in_flight > max_in_flight)
max_in_flight = server->in_flight;
}
/* if all channels are equally loaded, fall back to round-robin */ if (min_in_flight == max_in_flight)
index = (uint)start % ses->chan_count;
server = ses->chans[index].server;
spin_unlock(&ses->chan_lock);
/* * Wait for all the requests to become available. * This approach still leaves the possibility to be stuck waiting for * credits if the server doesn't grant credits to the outstanding * requests and if the client is completely idle, not generating any * other requests. * This can be handled by the eventual session reconnect.
*/
rc = wait_for_compound_request(server, num_rqst, flags,
&instance); if (rc) return rc;
for (i = 0; i < num_rqst; i++) {
credits[i].value = 1;
credits[i].instance = instance;
}
/* * Make sure that we sign in the same order that we send on this socket * and avoid races inside tcp sendmsg code that could cause corruption * of smb data.
*/
cifs_server_lock(server);
/* * All the parts of the compound chain belong obtained credits from the * same session. We can not use credits obtained from the previous * session to send this request. Check if there were reconnects after * we obtained credits and return -EAGAIN in such cases to let callers * handle it.
*/ if (instance != server->reconnect_instance) {
cifs_server_unlock(server); for (j = 0; j < num_rqst; j++)
add_credits(server, &credits[j], optype); return -EAGAIN;
}
for (i = 0; i < num_rqst; i++) {
midQ[i] = server->ops->setup_request(ses, server, &rqst[i]); if (IS_ERR(midQ[i])) {
revert_current_mid(server, i); for (j = 0; j < i; j++)
delete_mid(midQ[j]);
cifs_server_unlock(server);
/* Update # of requests on wire to server */ for (j = 0; j < num_rqst; j++)
add_credits(server, &credits[j], optype); return PTR_ERR(midQ[i]);
}
midQ[i]->mid_state = MID_REQUEST_SUBMITTED;
midQ[i]->optype = optype; /* * Invoke callback for every part of the compound chain * to calculate credits properly. Wake up this thread only when * the last element is received.
*/ if (i < num_rqst - 1)
midQ[i]->callback = cifs_compound_callback; else
midQ[i]->callback = cifs_compound_last_callback;
}
rc = smb_send_rqst(server, num_rqst, rqst, flags);
for (i = 0; i < num_rqst; i++)
cifs_save_when_sent(midQ[i]);
/* * If sending failed for some reason or it is an oplock break that we * will not receive a response to - return credits back
*/ if (rc < 0 || (flags & CIFS_NO_SRV_RSP)) { for (i = 0; i < num_rqst; i++)
add_credits(server, &credits[i], optype); goto out;
}
/* * At this point the request is passed to the network stack - we assume * that any credits taken from the server structure on the client have * been spent and we can't return them back. Once we receive responses * we will collect credits granted by the server in the mid callbacks * and add those credits to the server structure.
*/
/* * Compounding is never used during session establish.
*/
spin_lock(&ses->ses_lock); if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
spin_unlock(&ses->ses_lock);
/* mark it so buf will not be freed by delete_mid */ if ((flags & CIFS_NO_RSP_BUF) == 0)
midQ[i]->resp_buf = NULL;
}
/* * Compounding is never used during session establish.
*/
spin_lock(&ses->ses_lock); if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { struct kvec iov = {
.iov_base = resp_iov[0].iov_base,
.iov_len = resp_iov[0].iov_len
};
spin_unlock(&ses->ses_lock);
cifs_server_lock(server);
smb311_update_preauth_hash(ses, server, &iov, 1);
cifs_server_unlock(server);
spin_lock(&ses->ses_lock);
}
spin_unlock(&ses->ses_lock);
out: /* * This will dequeue all mids. After this it is important that the * demultiplex_thread will not process any of these mids any further. * This is prevented above by using a noop callback that will not * wake this thread except for the very last PDU.
*/ for (i = 0; i < num_rqst; i++) { if (!cancelled_mid[i])
delete_mid(midQ[i]);
}
/* * Discard any remaining data in the current SMB. To do this, we borrow the * current bigbuf.
*/ int
cifs_discard_remaining_data(struct TCP_Server_Info *server)
{ unsignedint rfclen = server->pdu_size;
size_t remaining = rfclen + HEADER_PREAMBLE_SIZE(server) -
server->total_read;
/* * read the rest of READ_RSP header (sans Data array), or whatever we * can if there's not enough data. At this point, we've read down to * the Mid.
*/
len = min_t(unsignedint, buflen, server->vals->read_rsp_size) -
HEADER_SIZE(server) + 1;
if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) {
cifs_reconnect(server, true); return -1;
}
if (server->ops->is_status_pending &&
server->ops->is_status_pending(buf, server)) {
cifs_discard_remaining_data(server); return -1;
}
/* set up first two iov for signature check and to get credits */
rdata->iov[0].iov_base = buf;
rdata->iov[0].iov_len = HEADER_PREAMBLE_SIZE(server);
rdata->iov[1].iov_base = buf + HEADER_PREAMBLE_SIZE(server);
rdata->iov[1].iov_len =
server->total_read - HEADER_PREAMBLE_SIZE(server);
cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
rdata->iov[0].iov_base, rdata->iov[0].iov_len);
cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
rdata->iov[1].iov_base, rdata->iov[1].iov_len);
/* Was the SMB read successful? */
rdata->result = server->ops->map_error(buf, false); if (rdata->result != 0) {
cifs_dbg(FYI, "%s: server returned error %d\n",
__func__, rdata->result); /* normal error on read response */ return __cifs_readv_discard(server, mid, false);
}
/* Is there enough to get to the rest of the READ_RSP header? */ if (server->total_read < server->vals->read_rsp_size) {
cifs_dbg(FYI, "%s: server returned short header. got=%u expected=%zu\n",
__func__, server->total_read,
server->vals->read_rsp_size);
rdata->result = -EIO; return cifs_readv_discard(server, mid);
}
data_offset = server->ops->read_data_offset(buf) +
HEADER_PREAMBLE_SIZE(server); if (data_offset < server->total_read) { /* * win2k8 sometimes sends an offset of 0 when the read * is beyond the EOF. Treat it as if the data starts just after * the header.
*/
cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n",
__func__, data_offset);
data_offset = server->total_read;
} elseif (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { /* data_offset is beyond the end of smallbuf */
cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
__func__, data_offset);
rdata->result = -EIO; return cifs_readv_discard(server, mid);
}
len = data_offset - server->total_read; if (len > 0) { /* read any junk before data into the rest of smallbuf */
length = cifs_read_from_socket(server,
buf + server->total_read, len); if (length < 0) return length;
server->total_read += length;
}
/* how much data is in the response? */ #ifdef CONFIG_CIFS_SMB_DIRECT
use_rdma_mr = rdata->mr; #endif
data_len = server->ops->read_data_length(buf, use_rdma_mr); if (!use_rdma_mr && (data_offset + data_len > buflen)) { /* data_len is corrupt -- discard frame */
rdata->result = -EIO; return cifs_readv_discard(server, mid);
}
#ifdef CONFIG_CIFS_SMB_DIRECT if (rdata->mr)
length = data_len; /* An RDMA read is already done. */ else #endif
length = cifs_read_iter_from_socket(server, &rdata->subreq.io_iter,
data_len); if (length > 0)
rdata->got_bytes += length;
server->total_read += length;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.