18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *   fs/cifs/transport.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *   Copyright (C) International Business Machines  Corp., 2002,2008
58c2ecf20Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
68c2ecf20Sopenharmony_ci *   Jeremy Allison (jra@samba.org) 2006.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
98c2ecf20Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as published
108c2ecf20Sopenharmony_ci *   by the Free Software Foundation; either version 2.1 of the License, or
118c2ecf20Sopenharmony_ci *   (at your option) any later version.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *   This library is distributed in the hope that it will be useful,
148c2ecf20Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
158c2ecf20Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
168c2ecf20Sopenharmony_ci *   the GNU Lesser General Public License for more details.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public License
198c2ecf20Sopenharmony_ci *   along with this library; if not, write to the Free Software
208c2ecf20Sopenharmony_ci *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/fs.h>
248c2ecf20Sopenharmony_ci#include <linux/list.h>
258c2ecf20Sopenharmony_ci#include <linux/gfp.h>
268c2ecf20Sopenharmony_ci#include <linux/wait.h>
278c2ecf20Sopenharmony_ci#include <linux/net.h>
288c2ecf20Sopenharmony_ci#include <linux/delay.h>
298c2ecf20Sopenharmony_ci#include <linux/freezer.h>
308c2ecf20Sopenharmony_ci#include <linux/tcp.h>
318c2ecf20Sopenharmony_ci#include <linux/bvec.h>
328c2ecf20Sopenharmony_ci#include <linux/highmem.h>
338c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
348c2ecf20Sopenharmony_ci#include <asm/processor.h>
358c2ecf20Sopenharmony_ci#include <linux/mempool.h>
368c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
378c2ecf20Sopenharmony_ci#include "cifspdu.h"
388c2ecf20Sopenharmony_ci#include "cifsglob.h"
398c2ecf20Sopenharmony_ci#include "cifsproto.h"
408c2ecf20Sopenharmony_ci#include "cifs_debug.h"
418c2ecf20Sopenharmony_ci#include "smb2proto.h"
428c2ecf20Sopenharmony_ci#include "smbdirect.h"
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* Max number of iovectors we can use off the stack when sending requests. */
458c2ecf20Sopenharmony_ci#define CIFS_MAX_IOV_SIZE 8
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_civoid
488c2ecf20Sopenharmony_cicifs_wake_up_task(struct mid_q_entry *mid)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	wake_up_process(mid->callback_data);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct mid_q_entry *
548c2ecf20Sopenharmony_ciAllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct mid_q_entry *temp;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (server == NULL) {
598c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Null TCP session in AllocMidQEntry\n");
608c2ecf20Sopenharmony_ci		return NULL;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
648c2ecf20Sopenharmony_ci	memset(temp, 0, sizeof(struct mid_q_entry));
658c2ecf20Sopenharmony_ci	kref_init(&temp->refcount);
668c2ecf20Sopenharmony_ci	temp->mid = get_mid(smb_buffer);
678c2ecf20Sopenharmony_ci	temp->pid = current->pid;
688c2ecf20Sopenharmony_ci	temp->command = cpu_to_le16(smb_buffer->Command);
698c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
708c2ecf20Sopenharmony_ci	/*	do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
718c2ecf20Sopenharmony_ci	/* when mid allocated can be before when sent */
728c2ecf20Sopenharmony_ci	temp->when_alloc = jiffies;
738c2ecf20Sopenharmony_ci	temp->server = server;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/*
768c2ecf20Sopenharmony_ci	 * The default is for the mid to be synchronous, so the
778c2ecf20Sopenharmony_ci	 * default callback just wakes up the current task.
788c2ecf20Sopenharmony_ci	 */
798c2ecf20Sopenharmony_ci	get_task_struct(current);
808c2ecf20Sopenharmony_ci	temp->creator = current;
818c2ecf20Sopenharmony_ci	temp->callback = cifs_wake_up_task;
828c2ecf20Sopenharmony_ci	temp->callback_data = current;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	atomic_inc(&midCount);
858c2ecf20Sopenharmony_ci	temp->mid_state = MID_REQUEST_ALLOCATED;
868c2ecf20Sopenharmony_ci	return temp;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void _cifs_mid_q_entry_release(struct kref *refcount)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct mid_q_entry *midEntry =
928c2ecf20Sopenharmony_ci			container_of(refcount, struct mid_q_entry, refcount);
938c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_STATS2
948c2ecf20Sopenharmony_ci	__le16 command = midEntry->server->vals->lock_cmd;
958c2ecf20Sopenharmony_ci	__u16 smb_cmd = le16_to_cpu(midEntry->command);
968c2ecf20Sopenharmony_ci	unsigned long now;
978c2ecf20Sopenharmony_ci	unsigned long roundtrip_time;
988c2ecf20Sopenharmony_ci#endif
998c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = midEntry->server;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) &&
1028c2ecf20Sopenharmony_ci	    midEntry->mid_state == MID_RESPONSE_RECEIVED &&
1038c2ecf20Sopenharmony_ci	    server->ops->handle_cancelled_mid)
1048c2ecf20Sopenharmony_ci		server->ops->handle_cancelled_mid(midEntry, server);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	midEntry->mid_state = MID_FREE;
1078c2ecf20Sopenharmony_ci	atomic_dec(&midCount);
1088c2ecf20Sopenharmony_ci	if (midEntry->large_buf)
1098c2ecf20Sopenharmony_ci		cifs_buf_release(midEntry->resp_buf);
1108c2ecf20Sopenharmony_ci	else
1118c2ecf20Sopenharmony_ci		cifs_small_buf_release(midEntry->resp_buf);
1128c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_STATS2
1138c2ecf20Sopenharmony_ci	now = jiffies;
1148c2ecf20Sopenharmony_ci	if (now < midEntry->when_alloc)
1158c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "Invalid mid allocation time\n");
1168c2ecf20Sopenharmony_ci	roundtrip_time = now - midEntry->when_alloc;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (smb_cmd < NUMBER_OF_SMB2_COMMANDS) {
1198c2ecf20Sopenharmony_ci		if (atomic_read(&server->num_cmds[smb_cmd]) == 0) {
1208c2ecf20Sopenharmony_ci			server->slowest_cmd[smb_cmd] = roundtrip_time;
1218c2ecf20Sopenharmony_ci			server->fastest_cmd[smb_cmd] = roundtrip_time;
1228c2ecf20Sopenharmony_ci		} else {
1238c2ecf20Sopenharmony_ci			if (server->slowest_cmd[smb_cmd] < roundtrip_time)
1248c2ecf20Sopenharmony_ci				server->slowest_cmd[smb_cmd] = roundtrip_time;
1258c2ecf20Sopenharmony_ci			else if (server->fastest_cmd[smb_cmd] > roundtrip_time)
1268c2ecf20Sopenharmony_ci				server->fastest_cmd[smb_cmd] = roundtrip_time;
1278c2ecf20Sopenharmony_ci		}
1288c2ecf20Sopenharmony_ci		cifs_stats_inc(&server->num_cmds[smb_cmd]);
1298c2ecf20Sopenharmony_ci		server->time_per_cmd[smb_cmd] += roundtrip_time;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci	/*
1328c2ecf20Sopenharmony_ci	 * commands taking longer than one second (default) can be indications
1338c2ecf20Sopenharmony_ci	 * that something is wrong, unless it is quite a slow link or a very
1348c2ecf20Sopenharmony_ci	 * busy server. Note that this calc is unlikely or impossible to wrap
1358c2ecf20Sopenharmony_ci	 * as long as slow_rsp_threshold is not set way above recommended max
1368c2ecf20Sopenharmony_ci	 * value (32767 ie 9 hours) and is generally harmless even if wrong
1378c2ecf20Sopenharmony_ci	 * since only affects debug counters - so leaving the calc as simple
1388c2ecf20Sopenharmony_ci	 * comparison rather than doing multiple conversions and overflow
1398c2ecf20Sopenharmony_ci	 * checks
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	if ((slow_rsp_threshold != 0) &&
1428c2ecf20Sopenharmony_ci	    time_after(now, midEntry->when_alloc + (slow_rsp_threshold * HZ)) &&
1438c2ecf20Sopenharmony_ci	    (midEntry->command != command)) {
1448c2ecf20Sopenharmony_ci		/*
1458c2ecf20Sopenharmony_ci		 * smb2slowcmd[NUMBER_OF_SMB2_COMMANDS] counts by command
1468c2ecf20Sopenharmony_ci		 * NB: le16_to_cpu returns unsigned so can not be negative below
1478c2ecf20Sopenharmony_ci		 */
1488c2ecf20Sopenharmony_ci		if (smb_cmd < NUMBER_OF_SMB2_COMMANDS)
1498c2ecf20Sopenharmony_ci			cifs_stats_inc(&server->smb2slowcmd[smb_cmd]);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid,
1528c2ecf20Sopenharmony_ci			       midEntry->when_sent, midEntry->when_received);
1538c2ecf20Sopenharmony_ci		if (cifsFYI & CIFS_TIMER) {
1548c2ecf20Sopenharmony_ci			pr_debug("slow rsp: cmd %d mid %llu",
1558c2ecf20Sopenharmony_ci				 midEntry->command, midEntry->mid);
1568c2ecf20Sopenharmony_ci			cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n",
1578c2ecf20Sopenharmony_ci				  now - midEntry->when_alloc,
1588c2ecf20Sopenharmony_ci				  now - midEntry->when_sent,
1598c2ecf20Sopenharmony_ci				  now - midEntry->when_received);
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci#endif
1638c2ecf20Sopenharmony_ci	put_task_struct(midEntry->creator);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	mempool_free(midEntry, cifs_mid_poolp);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_civoid cifs_mid_q_entry_release(struct mid_q_entry *midEntry)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
1718c2ecf20Sopenharmony_ci	kref_put(&midEntry->refcount, _cifs_mid_q_entry_release);
1728c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_civoid DeleteMidQEntry(struct mid_q_entry *midEntry)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	cifs_mid_q_entry_release(midEntry);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_civoid
1818c2ecf20Sopenharmony_cicifs_delete_mid(struct mid_q_entry *mid)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
1848c2ecf20Sopenharmony_ci	if (!(mid->mid_flags & MID_DELETED)) {
1858c2ecf20Sopenharmony_ci		list_del_init(&mid->qhead);
1868c2ecf20Sopenharmony_ci		mid->mid_flags |= MID_DELETED;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	DeleteMidQEntry(mid);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/*
1948c2ecf20Sopenharmony_ci * smb_send_kvec - send an array of kvecs to the server
1958c2ecf20Sopenharmony_ci * @server:	Server to send the data to
1968c2ecf20Sopenharmony_ci * @smb_msg:	Message to send
1978c2ecf20Sopenharmony_ci * @sent:	amount of data sent on socket is stored here
1988c2ecf20Sopenharmony_ci *
1998c2ecf20Sopenharmony_ci * Our basic "send data to server" function. Should be called with srv_mutex
2008c2ecf20Sopenharmony_ci * held. The caller is responsible for handling the results.
2018c2ecf20Sopenharmony_ci */
2028c2ecf20Sopenharmony_cistatic int
2038c2ecf20Sopenharmony_cismb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
2048c2ecf20Sopenharmony_ci	      size_t *sent)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	int rc = 0;
2078c2ecf20Sopenharmony_ci	int retries = 0;
2088c2ecf20Sopenharmony_ci	struct socket *ssocket = server->ssocket;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	*sent = 0;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (server->noblocksnd)
2138c2ecf20Sopenharmony_ci		smb_msg->msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL;
2148c2ecf20Sopenharmony_ci	else
2158c2ecf20Sopenharmony_ci		smb_msg->msg_flags = MSG_NOSIGNAL;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	while (msg_data_left(smb_msg)) {
2188c2ecf20Sopenharmony_ci		/*
2198c2ecf20Sopenharmony_ci		 * If blocking send, we try 3 times, since each can block
2208c2ecf20Sopenharmony_ci		 * for 5 seconds. For nonblocking  we have to try more
2218c2ecf20Sopenharmony_ci		 * but wait increasing amounts of time allowing time for
2228c2ecf20Sopenharmony_ci		 * socket to clear.  The overall time we wait in either
2238c2ecf20Sopenharmony_ci		 * case to send on the socket is about 15 seconds.
2248c2ecf20Sopenharmony_ci		 * Similarly we wait for 15 seconds for a response from
2258c2ecf20Sopenharmony_ci		 * the server in SendReceive[2] for the server to send
2268c2ecf20Sopenharmony_ci		 * a response back for most types of requests (except
2278c2ecf20Sopenharmony_ci		 * SMB Write past end of file which can be slow, and
2288c2ecf20Sopenharmony_ci		 * blocking lock operations). NFS waits slightly longer
2298c2ecf20Sopenharmony_ci		 * than CIFS, but this can make it take longer for
2308c2ecf20Sopenharmony_ci		 * nonresponsive servers to be detected and 15 seconds
2318c2ecf20Sopenharmony_ci		 * is more than enough time for modern networks to
2328c2ecf20Sopenharmony_ci		 * send a packet.  In most cases if we fail to send
2338c2ecf20Sopenharmony_ci		 * after the retries we will kill the socket and
2348c2ecf20Sopenharmony_ci		 * reconnect which may clear the network problem.
2358c2ecf20Sopenharmony_ci		 */
2368c2ecf20Sopenharmony_ci		rc = sock_sendmsg(ssocket, smb_msg);
2378c2ecf20Sopenharmony_ci		if (rc == -EAGAIN) {
2388c2ecf20Sopenharmony_ci			retries++;
2398c2ecf20Sopenharmony_ci			if (retries >= 14 ||
2408c2ecf20Sopenharmony_ci			    (!server->noblocksnd && (retries > 2))) {
2418c2ecf20Sopenharmony_ci				cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n",
2428c2ecf20Sopenharmony_ci					 ssocket);
2438c2ecf20Sopenharmony_ci				return -EAGAIN;
2448c2ecf20Sopenharmony_ci			}
2458c2ecf20Sopenharmony_ci			msleep(1 << retries);
2468c2ecf20Sopenharmony_ci			continue;
2478c2ecf20Sopenharmony_ci		}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		if (rc < 0)
2508c2ecf20Sopenharmony_ci			return rc;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		if (rc == 0) {
2538c2ecf20Sopenharmony_ci			/* should never happen, letting socket clear before
2548c2ecf20Sopenharmony_ci			   retrying is our only obvious option here */
2558c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "tcp sent no data\n");
2568c2ecf20Sopenharmony_ci			msleep(500);
2578c2ecf20Sopenharmony_ci			continue;
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		/* send was at least partially successful */
2618c2ecf20Sopenharmony_ci		*sent += rc;
2628c2ecf20Sopenharmony_ci		retries = 0; /* in case we get ENOSPC on the next send */
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ciunsigned long
2688c2ecf20Sopenharmony_cismb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	unsigned int i;
2718c2ecf20Sopenharmony_ci	struct kvec *iov;
2728c2ecf20Sopenharmony_ci	int nvec;
2738c2ecf20Sopenharmony_ci	unsigned long buflen = 0;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (server->vals->header_preamble_size == 0 &&
2768c2ecf20Sopenharmony_ci	    rqst->rq_nvec >= 2 && rqst->rq_iov[0].iov_len == 4) {
2778c2ecf20Sopenharmony_ci		iov = &rqst->rq_iov[1];
2788c2ecf20Sopenharmony_ci		nvec = rqst->rq_nvec - 1;
2798c2ecf20Sopenharmony_ci	} else {
2808c2ecf20Sopenharmony_ci		iov = rqst->rq_iov;
2818c2ecf20Sopenharmony_ci		nvec = rqst->rq_nvec;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* total up iov array first */
2858c2ecf20Sopenharmony_ci	for (i = 0; i < nvec; i++)
2868c2ecf20Sopenharmony_ci		buflen += iov[i].iov_len;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/*
2898c2ecf20Sopenharmony_ci	 * Add in the page array if there is one. The caller needs to make
2908c2ecf20Sopenharmony_ci	 * sure rq_offset and rq_tailsz are set correctly. If a buffer of
2918c2ecf20Sopenharmony_ci	 * multiple pages ends at page boundary, rq_tailsz needs to be set to
2928c2ecf20Sopenharmony_ci	 * PAGE_SIZE.
2938c2ecf20Sopenharmony_ci	 */
2948c2ecf20Sopenharmony_ci	if (rqst->rq_npages) {
2958c2ecf20Sopenharmony_ci		if (rqst->rq_npages == 1)
2968c2ecf20Sopenharmony_ci			buflen += rqst->rq_tailsz;
2978c2ecf20Sopenharmony_ci		else {
2988c2ecf20Sopenharmony_ci			/*
2998c2ecf20Sopenharmony_ci			 * If there is more than one page, calculate the
3008c2ecf20Sopenharmony_ci			 * buffer length based on rq_offset and rq_tailsz
3018c2ecf20Sopenharmony_ci			 */
3028c2ecf20Sopenharmony_ci			buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) -
3038c2ecf20Sopenharmony_ci					rqst->rq_offset;
3048c2ecf20Sopenharmony_ci			buflen += rqst->rq_tailsz;
3058c2ecf20Sopenharmony_ci		}
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return buflen;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int
3128c2ecf20Sopenharmony_ci__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
3138c2ecf20Sopenharmony_ci		struct smb_rqst *rqst)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	int rc;
3168c2ecf20Sopenharmony_ci	struct kvec *iov;
3178c2ecf20Sopenharmony_ci	int n_vec;
3188c2ecf20Sopenharmony_ci	unsigned int send_length = 0;
3198c2ecf20Sopenharmony_ci	unsigned int i, j;
3208c2ecf20Sopenharmony_ci	sigset_t mask, oldmask;
3218c2ecf20Sopenharmony_ci	size_t total_len = 0, sent, size;
3228c2ecf20Sopenharmony_ci	struct socket *ssocket = server->ssocket;
3238c2ecf20Sopenharmony_ci	struct msghdr smb_msg = {};
3248c2ecf20Sopenharmony_ci	__be32 rfc1002_marker;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	cifs_in_send_inc(server);
3278c2ecf20Sopenharmony_ci	if (cifs_rdma_enabled(server)) {
3288c2ecf20Sopenharmony_ci		/* return -EAGAIN when connecting or reconnecting */
3298c2ecf20Sopenharmony_ci		rc = -EAGAIN;
3308c2ecf20Sopenharmony_ci		if (server->smbd_conn)
3318c2ecf20Sopenharmony_ci			rc = smbd_send(server, num_rqst, rqst);
3328c2ecf20Sopenharmony_ci		goto smbd_done;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	rc = -EAGAIN;
3368c2ecf20Sopenharmony_ci	if (ssocket == NULL)
3378c2ecf20Sopenharmony_ci		goto out;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	rc = -ERESTARTSYS;
3408c2ecf20Sopenharmony_ci	if (fatal_signal_pending(current)) {
3418c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "signal pending before send request\n");
3428c2ecf20Sopenharmony_ci		goto out;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	rc = 0;
3468c2ecf20Sopenharmony_ci	/* cork the socket */
3478c2ecf20Sopenharmony_ci	tcp_sock_set_cork(ssocket->sk, true);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	for (j = 0; j < num_rqst; j++)
3508c2ecf20Sopenharmony_ci		send_length += smb_rqst_len(server, &rqst[j]);
3518c2ecf20Sopenharmony_ci	rfc1002_marker = cpu_to_be32(send_length);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/*
3548c2ecf20Sopenharmony_ci	 * We should not allow signals to interrupt the network send because
3558c2ecf20Sopenharmony_ci	 * any partial send will cause session reconnects thus increasing
3568c2ecf20Sopenharmony_ci	 * latency of system calls and overload a server with unnecessary
3578c2ecf20Sopenharmony_ci	 * requests.
3588c2ecf20Sopenharmony_ci	 */
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	sigfillset(&mask);
3618c2ecf20Sopenharmony_ci	sigprocmask(SIG_BLOCK, &mask, &oldmask);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* Generate a rfc1002 marker for SMB2+ */
3648c2ecf20Sopenharmony_ci	if (server->vals->header_preamble_size == 0) {
3658c2ecf20Sopenharmony_ci		struct kvec hiov = {
3668c2ecf20Sopenharmony_ci			.iov_base = &rfc1002_marker,
3678c2ecf20Sopenharmony_ci			.iov_len  = 4
3688c2ecf20Sopenharmony_ci		};
3698c2ecf20Sopenharmony_ci		iov_iter_kvec(&smb_msg.msg_iter, WRITE, &hiov, 1, 4);
3708c2ecf20Sopenharmony_ci		rc = smb_send_kvec(server, &smb_msg, &sent);
3718c2ecf20Sopenharmony_ci		if (rc < 0)
3728c2ecf20Sopenharmony_ci			goto unmask;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci		total_len += sent;
3758c2ecf20Sopenharmony_ci		send_length += 4;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	for (j = 0; j < num_rqst; j++) {
3818c2ecf20Sopenharmony_ci		iov = rqst[j].rq_iov;
3828c2ecf20Sopenharmony_ci		n_vec = rqst[j].rq_nvec;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		size = 0;
3858c2ecf20Sopenharmony_ci		for (i = 0; i < n_vec; i++) {
3868c2ecf20Sopenharmony_ci			dump_smb(iov[i].iov_base, iov[i].iov_len);
3878c2ecf20Sopenharmony_ci			size += iov[i].iov_len;
3888c2ecf20Sopenharmony_ci		}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci		iov_iter_kvec(&smb_msg.msg_iter, WRITE, iov, n_vec, size);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		rc = smb_send_kvec(server, &smb_msg, &sent);
3938c2ecf20Sopenharmony_ci		if (rc < 0)
3948c2ecf20Sopenharmony_ci			goto unmask;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		total_len += sent;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		/* now walk the page array and send each page in it */
3998c2ecf20Sopenharmony_ci		for (i = 0; i < rqst[j].rq_npages; i++) {
4008c2ecf20Sopenharmony_ci			struct bio_vec bvec;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci			bvec.bv_page = rqst[j].rq_pages[i];
4038c2ecf20Sopenharmony_ci			rqst_page_get_length(&rqst[j], i, &bvec.bv_len,
4048c2ecf20Sopenharmony_ci					     &bvec.bv_offset);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci			iov_iter_bvec(&smb_msg.msg_iter, WRITE,
4078c2ecf20Sopenharmony_ci				      &bvec, 1, bvec.bv_len);
4088c2ecf20Sopenharmony_ci			rc = smb_send_kvec(server, &smb_msg, &sent);
4098c2ecf20Sopenharmony_ci			if (rc < 0)
4108c2ecf20Sopenharmony_ci				break;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci			total_len += sent;
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ciunmask:
4178c2ecf20Sopenharmony_ci	sigprocmask(SIG_SETMASK, &oldmask, NULL);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/*
4208c2ecf20Sopenharmony_ci	 * If signal is pending but we have already sent the whole packet to
4218c2ecf20Sopenharmony_ci	 * the server we need to return success status to allow a corresponding
4228c2ecf20Sopenharmony_ci	 * mid entry to be kept in the pending requests queue thus allowing
4238c2ecf20Sopenharmony_ci	 * to handle responses from the server by the client.
4248c2ecf20Sopenharmony_ci	 *
4258c2ecf20Sopenharmony_ci	 * If only part of the packet has been sent there is no need to hide
4268c2ecf20Sopenharmony_ci	 * interrupt because the session will be reconnected anyway, so there
4278c2ecf20Sopenharmony_ci	 * won't be any response from the server to handle.
4288c2ecf20Sopenharmony_ci	 */
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (signal_pending(current) && (total_len != send_length)) {
4318c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "signal is pending after attempt to send\n");
4328c2ecf20Sopenharmony_ci		rc = -ERESTARTSYS;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* uncork it */
4368c2ecf20Sopenharmony_ci	tcp_sock_set_cork(ssocket->sk, false);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if ((total_len > 0) && (total_len != send_length)) {
4398c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n",
4408c2ecf20Sopenharmony_ci			 send_length, total_len);
4418c2ecf20Sopenharmony_ci		/*
4428c2ecf20Sopenharmony_ci		 * If we have only sent part of an SMB then the next SMB could
4438c2ecf20Sopenharmony_ci		 * be taken as the remainder of this one. We need to kill the
4448c2ecf20Sopenharmony_ci		 * socket so the server throws away the partial SMB
4458c2ecf20Sopenharmony_ci		 */
4468c2ecf20Sopenharmony_ci		server->tcpStatus = CifsNeedReconnect;
4478c2ecf20Sopenharmony_ci		trace_smb3_partial_send_reconnect(server->CurrentMid,
4488c2ecf20Sopenharmony_ci						  server->hostname);
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_cismbd_done:
4518c2ecf20Sopenharmony_ci	if (rc < 0 && rc != -EINTR)
4528c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "Error %d sending data on socket to server\n",
4538c2ecf20Sopenharmony_ci			 rc);
4548c2ecf20Sopenharmony_ci	else if (rc > 0)
4558c2ecf20Sopenharmony_ci		rc = 0;
4568c2ecf20Sopenharmony_ciout:
4578c2ecf20Sopenharmony_ci	cifs_in_send_dec(server);
4588c2ecf20Sopenharmony_ci	return rc;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int
4628c2ecf20Sopenharmony_cismb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
4638c2ecf20Sopenharmony_ci	      struct smb_rqst *rqst, int flags)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct kvec iov;
4668c2ecf20Sopenharmony_ci	struct smb2_transform_hdr *tr_hdr;
4678c2ecf20Sopenharmony_ci	struct smb_rqst cur_rqst[MAX_COMPOUND];
4688c2ecf20Sopenharmony_ci	int rc;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (!(flags & CIFS_TRANSFORM_REQ))
4718c2ecf20Sopenharmony_ci		return __smb_send_rqst(server, num_rqst, rqst);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (num_rqst > MAX_COMPOUND - 1)
4748c2ecf20Sopenharmony_ci		return -ENOMEM;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (!server->ops->init_transform_rq) {
4778c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "Encryption requested but transform callback is missing\n");
4788c2ecf20Sopenharmony_ci		return -EIO;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	tr_hdr = kmalloc(sizeof(*tr_hdr), GFP_NOFS);
4828c2ecf20Sopenharmony_ci	if (!tr_hdr)
4838c2ecf20Sopenharmony_ci		return -ENOMEM;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	memset(&cur_rqst[0], 0, sizeof(cur_rqst));
4868c2ecf20Sopenharmony_ci	memset(&iov, 0, sizeof(iov));
4878c2ecf20Sopenharmony_ci	memset(tr_hdr, 0, sizeof(*tr_hdr));
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	iov.iov_base = tr_hdr;
4908c2ecf20Sopenharmony_ci	iov.iov_len = sizeof(*tr_hdr);
4918c2ecf20Sopenharmony_ci	cur_rqst[0].rq_iov = &iov;
4928c2ecf20Sopenharmony_ci	cur_rqst[0].rq_nvec = 1;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	rc = server->ops->init_transform_rq(server, num_rqst + 1,
4958c2ecf20Sopenharmony_ci					    &cur_rqst[0], rqst);
4968c2ecf20Sopenharmony_ci	if (rc)
4978c2ecf20Sopenharmony_ci		goto out;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]);
5008c2ecf20Sopenharmony_ci	smb3_free_compound_rqst(num_rqst, &cur_rqst[1]);
5018c2ecf20Sopenharmony_ciout:
5028c2ecf20Sopenharmony_ci	kfree(tr_hdr);
5038c2ecf20Sopenharmony_ci	return rc;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ciint
5078c2ecf20Sopenharmony_cismb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
5088c2ecf20Sopenharmony_ci	 unsigned int smb_buf_length)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct kvec iov[2];
5118c2ecf20Sopenharmony_ci	struct smb_rqst rqst = { .rq_iov = iov,
5128c2ecf20Sopenharmony_ci				 .rq_nvec = 2 };
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	iov[0].iov_base = smb_buffer;
5158c2ecf20Sopenharmony_ci	iov[0].iov_len = 4;
5168c2ecf20Sopenharmony_ci	iov[1].iov_base = (char *)smb_buffer + 4;
5178c2ecf20Sopenharmony_ci	iov[1].iov_len = smb_buf_length;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	return __smb_send_rqst(server, 1, &rqst);
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic int
5238c2ecf20Sopenharmony_ciwait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
5248c2ecf20Sopenharmony_ci		      const int timeout, const int flags,
5258c2ecf20Sopenharmony_ci		      unsigned int *instance)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	long rc;
5288c2ecf20Sopenharmony_ci	int *credits;
5298c2ecf20Sopenharmony_ci	int optype;
5308c2ecf20Sopenharmony_ci	long int t;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (timeout < 0)
5338c2ecf20Sopenharmony_ci		t = MAX_JIFFY_OFFSET;
5348c2ecf20Sopenharmony_ci	else
5358c2ecf20Sopenharmony_ci		t = msecs_to_jiffies(timeout);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	optype = flags & CIFS_OP_MASK;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	*instance = 0;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	credits = server->ops->get_credits_field(server, optype);
5428c2ecf20Sopenharmony_ci	/* Since an echo is already inflight, no need to wait to send another */
5438c2ecf20Sopenharmony_ci	if (*credits <= 0 && optype == CIFS_ECHO_OP)
5448c2ecf20Sopenharmony_ci		return -EAGAIN;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	spin_lock(&server->req_lock);
5478c2ecf20Sopenharmony_ci	if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) {
5488c2ecf20Sopenharmony_ci		/* oplock breaks must not be held up */
5498c2ecf20Sopenharmony_ci		server->in_flight++;
5508c2ecf20Sopenharmony_ci		if (server->in_flight > server->max_in_flight)
5518c2ecf20Sopenharmony_ci			server->max_in_flight = server->in_flight;
5528c2ecf20Sopenharmony_ci		*credits -= 1;
5538c2ecf20Sopenharmony_ci		*instance = server->reconnect_instance;
5548c2ecf20Sopenharmony_ci		spin_unlock(&server->req_lock);
5558c2ecf20Sopenharmony_ci		return 0;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	while (1) {
5598c2ecf20Sopenharmony_ci		if (*credits < num_credits) {
5608c2ecf20Sopenharmony_ci			spin_unlock(&server->req_lock);
5618c2ecf20Sopenharmony_ci			cifs_num_waiters_inc(server);
5628c2ecf20Sopenharmony_ci			rc = wait_event_killable_timeout(server->request_q,
5638c2ecf20Sopenharmony_ci				has_credits(server, credits, num_credits), t);
5648c2ecf20Sopenharmony_ci			cifs_num_waiters_dec(server);
5658c2ecf20Sopenharmony_ci			if (!rc) {
5668c2ecf20Sopenharmony_ci				trace_smb3_credit_timeout(server->CurrentMid,
5678c2ecf20Sopenharmony_ci					server->hostname, num_credits, 0);
5688c2ecf20Sopenharmony_ci				cifs_server_dbg(VFS, "wait timed out after %d ms\n",
5698c2ecf20Sopenharmony_ci					 timeout);
5708c2ecf20Sopenharmony_ci				return -ENOTSUPP;
5718c2ecf20Sopenharmony_ci			}
5728c2ecf20Sopenharmony_ci			if (rc == -ERESTARTSYS)
5738c2ecf20Sopenharmony_ci				return -ERESTARTSYS;
5748c2ecf20Sopenharmony_ci			spin_lock(&server->req_lock);
5758c2ecf20Sopenharmony_ci		} else {
5768c2ecf20Sopenharmony_ci			if (server->tcpStatus == CifsExiting) {
5778c2ecf20Sopenharmony_ci				spin_unlock(&server->req_lock);
5788c2ecf20Sopenharmony_ci				return -ENOENT;
5798c2ecf20Sopenharmony_ci			}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci			/*
5828c2ecf20Sopenharmony_ci			 * For normal commands, reserve the last MAX_COMPOUND
5838c2ecf20Sopenharmony_ci			 * credits to compound requests.
5848c2ecf20Sopenharmony_ci			 * Otherwise these compounds could be permanently
5858c2ecf20Sopenharmony_ci			 * starved for credits by single-credit requests.
5868c2ecf20Sopenharmony_ci			 *
5878c2ecf20Sopenharmony_ci			 * To prevent spinning CPU, block this thread until
5888c2ecf20Sopenharmony_ci			 * there are >MAX_COMPOUND credits available.
5898c2ecf20Sopenharmony_ci			 * But only do this is we already have a lot of
5908c2ecf20Sopenharmony_ci			 * credits in flight to avoid triggering this check
5918c2ecf20Sopenharmony_ci			 * for servers that are slow to hand out credits on
5928c2ecf20Sopenharmony_ci			 * new sessions.
5938c2ecf20Sopenharmony_ci			 */
5948c2ecf20Sopenharmony_ci			if (!optype && num_credits == 1 &&
5958c2ecf20Sopenharmony_ci			    server->in_flight > 2 * MAX_COMPOUND &&
5968c2ecf20Sopenharmony_ci			    *credits <= MAX_COMPOUND) {
5978c2ecf20Sopenharmony_ci				spin_unlock(&server->req_lock);
5988c2ecf20Sopenharmony_ci				cifs_num_waiters_inc(server);
5998c2ecf20Sopenharmony_ci				rc = wait_event_killable_timeout(
6008c2ecf20Sopenharmony_ci					server->request_q,
6018c2ecf20Sopenharmony_ci					has_credits(server, credits,
6028c2ecf20Sopenharmony_ci						    MAX_COMPOUND + 1),
6038c2ecf20Sopenharmony_ci					t);
6048c2ecf20Sopenharmony_ci				cifs_num_waiters_dec(server);
6058c2ecf20Sopenharmony_ci				if (!rc) {
6068c2ecf20Sopenharmony_ci					trace_smb3_credit_timeout(
6078c2ecf20Sopenharmony_ci						server->CurrentMid,
6088c2ecf20Sopenharmony_ci						server->hostname, num_credits,
6098c2ecf20Sopenharmony_ci						0);
6108c2ecf20Sopenharmony_ci					cifs_server_dbg(VFS, "wait timed out after %d ms\n",
6118c2ecf20Sopenharmony_ci						 timeout);
6128c2ecf20Sopenharmony_ci					return -ENOTSUPP;
6138c2ecf20Sopenharmony_ci				}
6148c2ecf20Sopenharmony_ci				if (rc == -ERESTARTSYS)
6158c2ecf20Sopenharmony_ci					return -ERESTARTSYS;
6168c2ecf20Sopenharmony_ci				spin_lock(&server->req_lock);
6178c2ecf20Sopenharmony_ci				continue;
6188c2ecf20Sopenharmony_ci			}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci			/*
6218c2ecf20Sopenharmony_ci			 * Can not count locking commands against total
6228c2ecf20Sopenharmony_ci			 * as they are allowed to block on server.
6238c2ecf20Sopenharmony_ci			 */
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci			/* update # of requests on the wire to server */
6268c2ecf20Sopenharmony_ci			if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) {
6278c2ecf20Sopenharmony_ci				*credits -= num_credits;
6288c2ecf20Sopenharmony_ci				server->in_flight += num_credits;
6298c2ecf20Sopenharmony_ci				if (server->in_flight > server->max_in_flight)
6308c2ecf20Sopenharmony_ci					server->max_in_flight = server->in_flight;
6318c2ecf20Sopenharmony_ci				*instance = server->reconnect_instance;
6328c2ecf20Sopenharmony_ci			}
6338c2ecf20Sopenharmony_ci			spin_unlock(&server->req_lock);
6348c2ecf20Sopenharmony_ci			break;
6358c2ecf20Sopenharmony_ci		}
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci	return 0;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cistatic int
6418c2ecf20Sopenharmony_ciwait_for_free_request(struct TCP_Server_Info *server, const int flags,
6428c2ecf20Sopenharmony_ci		      unsigned int *instance)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	return wait_for_free_credits(server, 1, -1, flags,
6458c2ecf20Sopenharmony_ci				     instance);
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic int
6498c2ecf20Sopenharmony_ciwait_for_compound_request(struct TCP_Server_Info *server, int num,
6508c2ecf20Sopenharmony_ci			  const int flags, unsigned int *instance)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	int *credits;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	credits = server->ops->get_credits_field(server, flags & CIFS_OP_MASK);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	spin_lock(&server->req_lock);
6578c2ecf20Sopenharmony_ci	if (*credits < num) {
6588c2ecf20Sopenharmony_ci		/*
6598c2ecf20Sopenharmony_ci		 * If the server is tight on resources or just gives us less
6608c2ecf20Sopenharmony_ci		 * credits for other reasons (e.g. requests are coming out of
6618c2ecf20Sopenharmony_ci		 * order and the server delays granting more credits until it
6628c2ecf20Sopenharmony_ci		 * processes a missing mid) and we exhausted most available
6638c2ecf20Sopenharmony_ci		 * credits there may be situations when we try to send
6648c2ecf20Sopenharmony_ci		 * a compound request but we don't have enough credits. At this
6658c2ecf20Sopenharmony_ci		 * point the client needs to decide if it should wait for
6668c2ecf20Sopenharmony_ci		 * additional credits or fail the request. If at least one
6678c2ecf20Sopenharmony_ci		 * request is in flight there is a high probability that the
6688c2ecf20Sopenharmony_ci		 * server will return enough credits to satisfy this compound
6698c2ecf20Sopenharmony_ci		 * request.
6708c2ecf20Sopenharmony_ci		 *
6718c2ecf20Sopenharmony_ci		 * Return immediately if no requests in flight since we will be
6728c2ecf20Sopenharmony_ci		 * stuck on waiting for credits.
6738c2ecf20Sopenharmony_ci		 */
6748c2ecf20Sopenharmony_ci		if (server->in_flight == 0) {
6758c2ecf20Sopenharmony_ci			spin_unlock(&server->req_lock);
6768c2ecf20Sopenharmony_ci			return -ENOTSUPP;
6778c2ecf20Sopenharmony_ci		}
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	spin_unlock(&server->req_lock);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	return wait_for_free_credits(server, num, 60000, flags,
6828c2ecf20Sopenharmony_ci				     instance);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ciint
6868c2ecf20Sopenharmony_cicifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
6878c2ecf20Sopenharmony_ci		      unsigned int *num, struct cifs_credits *credits)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	*num = size;
6908c2ecf20Sopenharmony_ci	credits->value = 0;
6918c2ecf20Sopenharmony_ci	credits->instance = server->reconnect_instance;
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
6968c2ecf20Sopenharmony_ci			struct mid_q_entry **ppmidQ)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	if (ses->server->tcpStatus == CifsExiting) {
6998c2ecf20Sopenharmony_ci		return -ENOENT;
7008c2ecf20Sopenharmony_ci	}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (ses->server->tcpStatus == CifsNeedReconnect) {
7038c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
7048c2ecf20Sopenharmony_ci		return -EAGAIN;
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if (ses->status == CifsNew) {
7088c2ecf20Sopenharmony_ci		if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
7098c2ecf20Sopenharmony_ci			(in_buf->Command != SMB_COM_NEGOTIATE))
7108c2ecf20Sopenharmony_ci			return -EAGAIN;
7118c2ecf20Sopenharmony_ci		/* else ok - we are setting up session */
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (ses->status == CifsExiting) {
7158c2ecf20Sopenharmony_ci		/* check if SMB session is bad because we are setting it up */
7168c2ecf20Sopenharmony_ci		if (in_buf->Command != SMB_COM_LOGOFF_ANDX)
7178c2ecf20Sopenharmony_ci			return -EAGAIN;
7188c2ecf20Sopenharmony_ci		/* else ok - we are shutting down session */
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	*ppmidQ = AllocMidQEntry(in_buf, ses->server);
7228c2ecf20Sopenharmony_ci	if (*ppmidQ == NULL)
7238c2ecf20Sopenharmony_ci		return -ENOMEM;
7248c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
7258c2ecf20Sopenharmony_ci	list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
7268c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
7278c2ecf20Sopenharmony_ci	return 0;
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic int
7318c2ecf20Sopenharmony_ciwait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	int error;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	error = wait_event_freezekillable_unsafe(server->response_q,
7368c2ecf20Sopenharmony_ci				    midQ->mid_state != MID_REQUEST_SUBMITTED);
7378c2ecf20Sopenharmony_ci	if (error < 0)
7388c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	return 0;
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistruct mid_q_entry *
7448c2ecf20Sopenharmony_cicifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	int rc;
7478c2ecf20Sopenharmony_ci	struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
7488c2ecf20Sopenharmony_ci	struct mid_q_entry *mid;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (rqst->rq_iov[0].iov_len != 4 ||
7518c2ecf20Sopenharmony_ci	    rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
7528c2ecf20Sopenharmony_ci		return ERR_PTR(-EIO);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	/* enable signing if server requires it */
7558c2ecf20Sopenharmony_ci	if (server->sign)
7568c2ecf20Sopenharmony_ci		hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	mid = AllocMidQEntry(hdr, server);
7598c2ecf20Sopenharmony_ci	if (mid == NULL)
7608c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
7638c2ecf20Sopenharmony_ci	if (rc) {
7648c2ecf20Sopenharmony_ci		DeleteMidQEntry(mid);
7658c2ecf20Sopenharmony_ci		return ERR_PTR(rc);
7668c2ecf20Sopenharmony_ci	}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	return mid;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci/*
7728c2ecf20Sopenharmony_ci * Send a SMB request and set the callback function in the mid to handle
7738c2ecf20Sopenharmony_ci * the result. Caller is responsible for dealing with timeouts.
7748c2ecf20Sopenharmony_ci */
7758c2ecf20Sopenharmony_ciint
7768c2ecf20Sopenharmony_cicifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
7778c2ecf20Sopenharmony_ci		mid_receive_t *receive, mid_callback_t *callback,
7788c2ecf20Sopenharmony_ci		mid_handle_t *handle, void *cbdata, const int flags,
7798c2ecf20Sopenharmony_ci		const struct cifs_credits *exist_credits)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	int rc;
7828c2ecf20Sopenharmony_ci	struct mid_q_entry *mid;
7838c2ecf20Sopenharmony_ci	struct cifs_credits credits = { .value = 0, .instance = 0 };
7848c2ecf20Sopenharmony_ci	unsigned int instance;
7858c2ecf20Sopenharmony_ci	int optype;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	optype = flags & CIFS_OP_MASK;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if ((flags & CIFS_HAS_CREDITS) == 0) {
7908c2ecf20Sopenharmony_ci		rc = wait_for_free_request(server, flags, &instance);
7918c2ecf20Sopenharmony_ci		if (rc)
7928c2ecf20Sopenharmony_ci			return rc;
7938c2ecf20Sopenharmony_ci		credits.value = 1;
7948c2ecf20Sopenharmony_ci		credits.instance = instance;
7958c2ecf20Sopenharmony_ci	} else
7968c2ecf20Sopenharmony_ci		instance = exist_credits->instance;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	mutex_lock(&server->srv_mutex);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	/*
8018c2ecf20Sopenharmony_ci	 * We can't use credits obtained from the previous session to send this
8028c2ecf20Sopenharmony_ci	 * request. Check if there were reconnects after we obtained credits and
8038c2ecf20Sopenharmony_ci	 * return -EAGAIN in such cases to let callers handle it.
8048c2ecf20Sopenharmony_ci	 */
8058c2ecf20Sopenharmony_ci	if (instance != server->reconnect_instance) {
8068c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
8078c2ecf20Sopenharmony_ci		add_credits_and_wake_if(server, &credits, optype);
8088c2ecf20Sopenharmony_ci		return -EAGAIN;
8098c2ecf20Sopenharmony_ci	}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	mid = server->ops->setup_async_request(server, rqst);
8128c2ecf20Sopenharmony_ci	if (IS_ERR(mid)) {
8138c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
8148c2ecf20Sopenharmony_ci		add_credits_and_wake_if(server, &credits, optype);
8158c2ecf20Sopenharmony_ci		return PTR_ERR(mid);
8168c2ecf20Sopenharmony_ci	}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	mid->receive = receive;
8198c2ecf20Sopenharmony_ci	mid->callback = callback;
8208c2ecf20Sopenharmony_ci	mid->callback_data = cbdata;
8218c2ecf20Sopenharmony_ci	mid->handle = handle;
8228c2ecf20Sopenharmony_ci	mid->mid_state = MID_REQUEST_SUBMITTED;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	/* put it on the pending_mid_q */
8258c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
8268c2ecf20Sopenharmony_ci	list_add_tail(&mid->qhead, &server->pending_mid_q);
8278c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/*
8308c2ecf20Sopenharmony_ci	 * Need to store the time in mid before calling I/O. For call_async,
8318c2ecf20Sopenharmony_ci	 * I/O response may come back and free the mid entry on another thread.
8328c2ecf20Sopenharmony_ci	 */
8338c2ecf20Sopenharmony_ci	cifs_save_when_sent(mid);
8348c2ecf20Sopenharmony_ci	rc = smb_send_rqst(server, 1, rqst, flags);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (rc < 0) {
8378c2ecf20Sopenharmony_ci		revert_current_mid(server, mid->credits);
8388c2ecf20Sopenharmony_ci		server->sequence_number -= 2;
8398c2ecf20Sopenharmony_ci		cifs_delete_mid(mid);
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	mutex_unlock(&server->srv_mutex);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (rc == 0)
8458c2ecf20Sopenharmony_ci		return 0;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	add_credits_and_wake_if(server, &credits, optype);
8488c2ecf20Sopenharmony_ci	return rc;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci/*
8528c2ecf20Sopenharmony_ci *
8538c2ecf20Sopenharmony_ci * Send an SMB Request.  No response info (other than return code)
8548c2ecf20Sopenharmony_ci * needs to be parsed.
8558c2ecf20Sopenharmony_ci *
8568c2ecf20Sopenharmony_ci * flags indicate the type of request buffer and how long to wait
8578c2ecf20Sopenharmony_ci * and whether to log NT STATUS code (error) before mapping it to POSIX error
8588c2ecf20Sopenharmony_ci *
8598c2ecf20Sopenharmony_ci */
8608c2ecf20Sopenharmony_ciint
8618c2ecf20Sopenharmony_ciSendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
8628c2ecf20Sopenharmony_ci		 char *in_buf, int flags)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	int rc;
8658c2ecf20Sopenharmony_ci	struct kvec iov[1];
8668c2ecf20Sopenharmony_ci	struct kvec rsp_iov;
8678c2ecf20Sopenharmony_ci	int resp_buf_type;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	iov[0].iov_base = in_buf;
8708c2ecf20Sopenharmony_ci	iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
8718c2ecf20Sopenharmony_ci	flags |= CIFS_NO_RSP_BUF;
8728c2ecf20Sopenharmony_ci	rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
8738c2ecf20Sopenharmony_ci	cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	return rc;
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cistatic int
8798c2ecf20Sopenharmony_cicifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
8808c2ecf20Sopenharmony_ci{
8818c2ecf20Sopenharmony_ci	int rc = 0;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n",
8848c2ecf20Sopenharmony_ci		 __func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
8878c2ecf20Sopenharmony_ci	switch (mid->mid_state) {
8888c2ecf20Sopenharmony_ci	case MID_RESPONSE_RECEIVED:
8898c2ecf20Sopenharmony_ci		spin_unlock(&GlobalMid_Lock);
8908c2ecf20Sopenharmony_ci		return rc;
8918c2ecf20Sopenharmony_ci	case MID_RETRY_NEEDED:
8928c2ecf20Sopenharmony_ci		rc = -EAGAIN;
8938c2ecf20Sopenharmony_ci		break;
8948c2ecf20Sopenharmony_ci	case MID_RESPONSE_MALFORMED:
8958c2ecf20Sopenharmony_ci		rc = -EIO;
8968c2ecf20Sopenharmony_ci		break;
8978c2ecf20Sopenharmony_ci	case MID_SHUTDOWN:
8988c2ecf20Sopenharmony_ci		rc = -EHOSTDOWN;
8998c2ecf20Sopenharmony_ci		break;
9008c2ecf20Sopenharmony_ci	default:
9018c2ecf20Sopenharmony_ci		if (!(mid->mid_flags & MID_DELETED)) {
9028c2ecf20Sopenharmony_ci			list_del_init(&mid->qhead);
9038c2ecf20Sopenharmony_ci			mid->mid_flags |= MID_DELETED;
9048c2ecf20Sopenharmony_ci		}
9058c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n",
9068c2ecf20Sopenharmony_ci			 __func__, mid->mid, mid->mid_state);
9078c2ecf20Sopenharmony_ci		rc = -EIO;
9088c2ecf20Sopenharmony_ci	}
9098c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	DeleteMidQEntry(mid);
9128c2ecf20Sopenharmony_ci	return rc;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_cistatic inline int
9168c2ecf20Sopenharmony_cisend_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
9178c2ecf20Sopenharmony_ci	    struct mid_q_entry *mid)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	return server->ops->send_cancel ?
9208c2ecf20Sopenharmony_ci				server->ops->send_cancel(server, rqst, mid) : 0;
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ciint
9248c2ecf20Sopenharmony_cicifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
9258c2ecf20Sopenharmony_ci		   bool log_error)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	dump_smb(mid->resp_buf, min_t(u32, 92, len));
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	/* convert the length into a more usable form */
9328c2ecf20Sopenharmony_ci	if (server->sign) {
9338c2ecf20Sopenharmony_ci		struct kvec iov[2];
9348c2ecf20Sopenharmony_ci		int rc = 0;
9358c2ecf20Sopenharmony_ci		struct smb_rqst rqst = { .rq_iov = iov,
9368c2ecf20Sopenharmony_ci					 .rq_nvec = 2 };
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci		iov[0].iov_base = mid->resp_buf;
9398c2ecf20Sopenharmony_ci		iov[0].iov_len = 4;
9408c2ecf20Sopenharmony_ci		iov[1].iov_base = (char *)mid->resp_buf + 4;
9418c2ecf20Sopenharmony_ci		iov[1].iov_len = len - 4;
9428c2ecf20Sopenharmony_ci		/* FIXME: add code to kill session */
9438c2ecf20Sopenharmony_ci		rc = cifs_verify_signature(&rqst, server,
9448c2ecf20Sopenharmony_ci					   mid->sequence_number);
9458c2ecf20Sopenharmony_ci		if (rc)
9468c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
9478c2ecf20Sopenharmony_ci				 rc);
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	/* BB special case reconnect tid and uid here? */
9518c2ecf20Sopenharmony_ci	return map_and_check_smb_error(mid, log_error);
9528c2ecf20Sopenharmony_ci}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_cistruct mid_q_entry *
9558c2ecf20Sopenharmony_cicifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
9568c2ecf20Sopenharmony_ci		   struct smb_rqst *rqst)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	int rc;
9598c2ecf20Sopenharmony_ci	struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
9608c2ecf20Sopenharmony_ci	struct mid_q_entry *mid;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	if (rqst->rq_iov[0].iov_len != 4 ||
9638c2ecf20Sopenharmony_ci	    rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
9648c2ecf20Sopenharmony_ci		return ERR_PTR(-EIO);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	rc = allocate_mid(ses, hdr, &mid);
9678c2ecf20Sopenharmony_ci	if (rc)
9688c2ecf20Sopenharmony_ci		return ERR_PTR(rc);
9698c2ecf20Sopenharmony_ci	rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
9708c2ecf20Sopenharmony_ci	if (rc) {
9718c2ecf20Sopenharmony_ci		cifs_delete_mid(mid);
9728c2ecf20Sopenharmony_ci		return ERR_PTR(rc);
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci	return mid;
9758c2ecf20Sopenharmony_ci}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic void
9788c2ecf20Sopenharmony_cicifs_compound_callback(struct mid_q_entry *mid)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = mid->server;
9818c2ecf20Sopenharmony_ci	struct cifs_credits credits;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	credits.value = server->ops->get_credits(mid);
9848c2ecf20Sopenharmony_ci	credits.instance = server->reconnect_instance;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	add_credits(server, &credits, mid->optype);
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic void
9908c2ecf20Sopenharmony_cicifs_compound_last_callback(struct mid_q_entry *mid)
9918c2ecf20Sopenharmony_ci{
9928c2ecf20Sopenharmony_ci	cifs_compound_callback(mid);
9938c2ecf20Sopenharmony_ci	cifs_wake_up_task(mid);
9948c2ecf20Sopenharmony_ci}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_cistatic void
9978c2ecf20Sopenharmony_cicifs_cancelled_callback(struct mid_q_entry *mid)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	cifs_compound_callback(mid);
10008c2ecf20Sopenharmony_ci	DeleteMidQEntry(mid);
10018c2ecf20Sopenharmony_ci}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci/*
10048c2ecf20Sopenharmony_ci * Return a channel (master if none) of @ses that can be used to send
10058c2ecf20Sopenharmony_ci * regular requests.
10068c2ecf20Sopenharmony_ci *
10078c2ecf20Sopenharmony_ci * If we are currently binding a new channel (negprot/sess.setup),
10088c2ecf20Sopenharmony_ci * return the new incomplete channel.
10098c2ecf20Sopenharmony_ci */
10108c2ecf20Sopenharmony_cistruct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
10118c2ecf20Sopenharmony_ci{
10128c2ecf20Sopenharmony_ci	uint index = 0;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (!ses)
10158c2ecf20Sopenharmony_ci		return NULL;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (!ses->binding) {
10188c2ecf20Sopenharmony_ci		/* round robin */
10198c2ecf20Sopenharmony_ci		if (ses->chan_count > 1) {
10208c2ecf20Sopenharmony_ci			index = (uint)atomic_inc_return(&ses->chan_seq);
10218c2ecf20Sopenharmony_ci			index %= ses->chan_count;
10228c2ecf20Sopenharmony_ci		}
10238c2ecf20Sopenharmony_ci		return ses->chans[index].server;
10248c2ecf20Sopenharmony_ci	} else {
10258c2ecf20Sopenharmony_ci		return cifs_ses_server(ses);
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ciint
10308c2ecf20Sopenharmony_cicompound_send_recv(const unsigned int xid, struct cifs_ses *ses,
10318c2ecf20Sopenharmony_ci		   struct TCP_Server_Info *server,
10328c2ecf20Sopenharmony_ci		   const int flags, const int num_rqst, struct smb_rqst *rqst,
10338c2ecf20Sopenharmony_ci		   int *resp_buf_type, struct kvec *resp_iov)
10348c2ecf20Sopenharmony_ci{
10358c2ecf20Sopenharmony_ci	int i, j, optype, rc = 0;
10368c2ecf20Sopenharmony_ci	struct mid_q_entry *midQ[MAX_COMPOUND];
10378c2ecf20Sopenharmony_ci	bool cancelled_mid[MAX_COMPOUND] = {false};
10388c2ecf20Sopenharmony_ci	struct cifs_credits credits[MAX_COMPOUND] = {
10398c2ecf20Sopenharmony_ci		{ .value = 0, .instance = 0 }
10408c2ecf20Sopenharmony_ci	};
10418c2ecf20Sopenharmony_ci	unsigned int instance;
10428c2ecf20Sopenharmony_ci	char *buf;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	optype = flags & CIFS_OP_MASK;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	for (i = 0; i < num_rqst; i++)
10478c2ecf20Sopenharmony_ci		resp_buf_type[i] = CIFS_NO_BUFFER;  /* no response buf yet */
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	if (!ses || !ses->server || !server) {
10508c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Null session\n");
10518c2ecf20Sopenharmony_ci		return -EIO;
10528c2ecf20Sopenharmony_ci	}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsExiting)
10558c2ecf20Sopenharmony_ci		return -ENOENT;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	/*
10588c2ecf20Sopenharmony_ci	 * Wait for all the requests to become available.
10598c2ecf20Sopenharmony_ci	 * This approach still leaves the possibility to be stuck waiting for
10608c2ecf20Sopenharmony_ci	 * credits if the server doesn't grant credits to the outstanding
10618c2ecf20Sopenharmony_ci	 * requests and if the client is completely idle, not generating any
10628c2ecf20Sopenharmony_ci	 * other requests.
10638c2ecf20Sopenharmony_ci	 * This can be handled by the eventual session reconnect.
10648c2ecf20Sopenharmony_ci	 */
10658c2ecf20Sopenharmony_ci	rc = wait_for_compound_request(server, num_rqst, flags,
10668c2ecf20Sopenharmony_ci				       &instance);
10678c2ecf20Sopenharmony_ci	if (rc)
10688c2ecf20Sopenharmony_ci		return rc;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	for (i = 0; i < num_rqst; i++) {
10718c2ecf20Sopenharmony_ci		credits[i].value = 1;
10728c2ecf20Sopenharmony_ci		credits[i].instance = instance;
10738c2ecf20Sopenharmony_ci	}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	/*
10768c2ecf20Sopenharmony_ci	 * Make sure that we sign in the same order that we send on this socket
10778c2ecf20Sopenharmony_ci	 * and avoid races inside tcp sendmsg code that could cause corruption
10788c2ecf20Sopenharmony_ci	 * of smb data.
10798c2ecf20Sopenharmony_ci	 */
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	mutex_lock(&server->srv_mutex);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	/*
10848c2ecf20Sopenharmony_ci	 * All the parts of the compound chain belong obtained credits from the
10858c2ecf20Sopenharmony_ci	 * same session. We can not use credits obtained from the previous
10868c2ecf20Sopenharmony_ci	 * session to send this request. Check if there were reconnects after
10878c2ecf20Sopenharmony_ci	 * we obtained credits and return -EAGAIN in such cases to let callers
10888c2ecf20Sopenharmony_ci	 * handle it.
10898c2ecf20Sopenharmony_ci	 */
10908c2ecf20Sopenharmony_ci	if (instance != server->reconnect_instance) {
10918c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
10928c2ecf20Sopenharmony_ci		for (j = 0; j < num_rqst; j++)
10938c2ecf20Sopenharmony_ci			add_credits(server, &credits[j], optype);
10948c2ecf20Sopenharmony_ci		return -EAGAIN;
10958c2ecf20Sopenharmony_ci	}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	for (i = 0; i < num_rqst; i++) {
10988c2ecf20Sopenharmony_ci		midQ[i] = server->ops->setup_request(ses, server, &rqst[i]);
10998c2ecf20Sopenharmony_ci		if (IS_ERR(midQ[i])) {
11008c2ecf20Sopenharmony_ci			revert_current_mid(server, i);
11018c2ecf20Sopenharmony_ci			for (j = 0; j < i; j++)
11028c2ecf20Sopenharmony_ci				cifs_delete_mid(midQ[j]);
11038c2ecf20Sopenharmony_ci			mutex_unlock(&server->srv_mutex);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci			/* Update # of requests on wire to server */
11068c2ecf20Sopenharmony_ci			for (j = 0; j < num_rqst; j++)
11078c2ecf20Sopenharmony_ci				add_credits(server, &credits[j], optype);
11088c2ecf20Sopenharmony_ci			return PTR_ERR(midQ[i]);
11098c2ecf20Sopenharmony_ci		}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci		midQ[i]->mid_state = MID_REQUEST_SUBMITTED;
11128c2ecf20Sopenharmony_ci		midQ[i]->optype = optype;
11138c2ecf20Sopenharmony_ci		/*
11148c2ecf20Sopenharmony_ci		 * Invoke callback for every part of the compound chain
11158c2ecf20Sopenharmony_ci		 * to calculate credits properly. Wake up this thread only when
11168c2ecf20Sopenharmony_ci		 * the last element is received.
11178c2ecf20Sopenharmony_ci		 */
11188c2ecf20Sopenharmony_ci		if (i < num_rqst - 1)
11198c2ecf20Sopenharmony_ci			midQ[i]->callback = cifs_compound_callback;
11208c2ecf20Sopenharmony_ci		else
11218c2ecf20Sopenharmony_ci			midQ[i]->callback = cifs_compound_last_callback;
11228c2ecf20Sopenharmony_ci	}
11238c2ecf20Sopenharmony_ci	rc = smb_send_rqst(server, num_rqst, rqst, flags);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	for (i = 0; i < num_rqst; i++)
11268c2ecf20Sopenharmony_ci		cifs_save_when_sent(midQ[i]);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	if (rc < 0) {
11298c2ecf20Sopenharmony_ci		revert_current_mid(server, num_rqst);
11308c2ecf20Sopenharmony_ci		server->sequence_number -= 2;
11318c2ecf20Sopenharmony_ci	}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	mutex_unlock(&server->srv_mutex);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	/*
11368c2ecf20Sopenharmony_ci	 * If sending failed for some reason or it is an oplock break that we
11378c2ecf20Sopenharmony_ci	 * will not receive a response to - return credits back
11388c2ecf20Sopenharmony_ci	 */
11398c2ecf20Sopenharmony_ci	if (rc < 0 || (flags & CIFS_NO_SRV_RSP)) {
11408c2ecf20Sopenharmony_ci		for (i = 0; i < num_rqst; i++)
11418c2ecf20Sopenharmony_ci			add_credits(server, &credits[i], optype);
11428c2ecf20Sopenharmony_ci		goto out;
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	/*
11468c2ecf20Sopenharmony_ci	 * At this point the request is passed to the network stack - we assume
11478c2ecf20Sopenharmony_ci	 * that any credits taken from the server structure on the client have
11488c2ecf20Sopenharmony_ci	 * been spent and we can't return them back. Once we receive responses
11498c2ecf20Sopenharmony_ci	 * we will collect credits granted by the server in the mid callbacks
11508c2ecf20Sopenharmony_ci	 * and add those credits to the server structure.
11518c2ecf20Sopenharmony_ci	 */
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	/*
11548c2ecf20Sopenharmony_ci	 * Compounding is never used during session establish.
11558c2ecf20Sopenharmony_ci	 */
11568c2ecf20Sopenharmony_ci	if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) {
11578c2ecf20Sopenharmony_ci		mutex_lock(&server->srv_mutex);
11588c2ecf20Sopenharmony_ci		smb311_update_preauth_hash(ses, rqst[0].rq_iov,
11598c2ecf20Sopenharmony_ci					   rqst[0].rq_nvec);
11608c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	for (i = 0; i < num_rqst; i++) {
11648c2ecf20Sopenharmony_ci		rc = wait_for_response(server, midQ[i]);
11658c2ecf20Sopenharmony_ci		if (rc != 0)
11668c2ecf20Sopenharmony_ci			break;
11678c2ecf20Sopenharmony_ci	}
11688c2ecf20Sopenharmony_ci	if (rc != 0) {
11698c2ecf20Sopenharmony_ci		for (; i < num_rqst; i++) {
11708c2ecf20Sopenharmony_ci			cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n",
11718c2ecf20Sopenharmony_ci				 midQ[i]->mid, le16_to_cpu(midQ[i]->command));
11728c2ecf20Sopenharmony_ci			send_cancel(server, &rqst[i], midQ[i]);
11738c2ecf20Sopenharmony_ci			spin_lock(&GlobalMid_Lock);
11748c2ecf20Sopenharmony_ci			midQ[i]->mid_flags |= MID_WAIT_CANCELLED;
11758c2ecf20Sopenharmony_ci			if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) {
11768c2ecf20Sopenharmony_ci				midQ[i]->callback = cifs_cancelled_callback;
11778c2ecf20Sopenharmony_ci				cancelled_mid[i] = true;
11788c2ecf20Sopenharmony_ci				credits[i].value = 0;
11798c2ecf20Sopenharmony_ci			}
11808c2ecf20Sopenharmony_ci			spin_unlock(&GlobalMid_Lock);
11818c2ecf20Sopenharmony_ci		}
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	for (i = 0; i < num_rqst; i++) {
11858c2ecf20Sopenharmony_ci		if (rc < 0)
11868c2ecf20Sopenharmony_ci			goto out;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci		rc = cifs_sync_mid_result(midQ[i], server);
11898c2ecf20Sopenharmony_ci		if (rc != 0) {
11908c2ecf20Sopenharmony_ci			/* mark this mid as cancelled to not free it below */
11918c2ecf20Sopenharmony_ci			cancelled_mid[i] = true;
11928c2ecf20Sopenharmony_ci			goto out;
11938c2ecf20Sopenharmony_ci		}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci		if (!midQ[i]->resp_buf ||
11968c2ecf20Sopenharmony_ci		    midQ[i]->mid_state != MID_RESPONSE_RECEIVED) {
11978c2ecf20Sopenharmony_ci			rc = -EIO;
11988c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Bad MID state?\n");
11998c2ecf20Sopenharmony_ci			goto out;
12008c2ecf20Sopenharmony_ci		}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci		buf = (char *)midQ[i]->resp_buf;
12038c2ecf20Sopenharmony_ci		resp_iov[i].iov_base = buf;
12048c2ecf20Sopenharmony_ci		resp_iov[i].iov_len = midQ[i]->resp_buf_size +
12058c2ecf20Sopenharmony_ci			server->vals->header_preamble_size;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci		if (midQ[i]->large_buf)
12088c2ecf20Sopenharmony_ci			resp_buf_type[i] = CIFS_LARGE_BUFFER;
12098c2ecf20Sopenharmony_ci		else
12108c2ecf20Sopenharmony_ci			resp_buf_type[i] = CIFS_SMALL_BUFFER;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci		rc = server->ops->check_receive(midQ[i], server,
12138c2ecf20Sopenharmony_ci						     flags & CIFS_LOG_ERROR);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci		/* mark it so buf will not be freed by cifs_delete_mid */
12168c2ecf20Sopenharmony_ci		if ((flags & CIFS_NO_RSP_BUF) == 0)
12178c2ecf20Sopenharmony_ci			midQ[i]->resp_buf = NULL;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	}
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	/*
12228c2ecf20Sopenharmony_ci	 * Compounding is never used during session establish.
12238c2ecf20Sopenharmony_ci	 */
12248c2ecf20Sopenharmony_ci	if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) {
12258c2ecf20Sopenharmony_ci		struct kvec iov = {
12268c2ecf20Sopenharmony_ci			.iov_base = resp_iov[0].iov_base,
12278c2ecf20Sopenharmony_ci			.iov_len = resp_iov[0].iov_len
12288c2ecf20Sopenharmony_ci		};
12298c2ecf20Sopenharmony_ci		mutex_lock(&server->srv_mutex);
12308c2ecf20Sopenharmony_ci		smb311_update_preauth_hash(ses, &iov, 1);
12318c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
12328c2ecf20Sopenharmony_ci	}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ciout:
12358c2ecf20Sopenharmony_ci	/*
12368c2ecf20Sopenharmony_ci	 * This will dequeue all mids. After this it is important that the
12378c2ecf20Sopenharmony_ci	 * demultiplex_thread will not process any of these mids any futher.
12388c2ecf20Sopenharmony_ci	 * This is prevented above by using a noop callback that will not
12398c2ecf20Sopenharmony_ci	 * wake this thread except for the very last PDU.
12408c2ecf20Sopenharmony_ci	 */
12418c2ecf20Sopenharmony_ci	for (i = 0; i < num_rqst; i++) {
12428c2ecf20Sopenharmony_ci		if (!cancelled_mid[i])
12438c2ecf20Sopenharmony_ci			cifs_delete_mid(midQ[i]);
12448c2ecf20Sopenharmony_ci	}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	return rc;
12478c2ecf20Sopenharmony_ci}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ciint
12508c2ecf20Sopenharmony_cicifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
12518c2ecf20Sopenharmony_ci	       struct TCP_Server_Info *server,
12528c2ecf20Sopenharmony_ci	       struct smb_rqst *rqst, int *resp_buf_type, const int flags,
12538c2ecf20Sopenharmony_ci	       struct kvec *resp_iov)
12548c2ecf20Sopenharmony_ci{
12558c2ecf20Sopenharmony_ci	return compound_send_recv(xid, ses, server, flags, 1,
12568c2ecf20Sopenharmony_ci				  rqst, resp_buf_type, resp_iov);
12578c2ecf20Sopenharmony_ci}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ciint
12608c2ecf20Sopenharmony_ciSendReceive2(const unsigned int xid, struct cifs_ses *ses,
12618c2ecf20Sopenharmony_ci	     struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
12628c2ecf20Sopenharmony_ci	     const int flags, struct kvec *resp_iov)
12638c2ecf20Sopenharmony_ci{
12648c2ecf20Sopenharmony_ci	struct smb_rqst rqst;
12658c2ecf20Sopenharmony_ci	struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov;
12668c2ecf20Sopenharmony_ci	int rc;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
12698c2ecf20Sopenharmony_ci		new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec),
12708c2ecf20Sopenharmony_ci					GFP_KERNEL);
12718c2ecf20Sopenharmony_ci		if (!new_iov) {
12728c2ecf20Sopenharmony_ci			/* otherwise cifs_send_recv below sets resp_buf_type */
12738c2ecf20Sopenharmony_ci			*resp_buf_type = CIFS_NO_BUFFER;
12748c2ecf20Sopenharmony_ci			return -ENOMEM;
12758c2ecf20Sopenharmony_ci		}
12768c2ecf20Sopenharmony_ci	} else
12778c2ecf20Sopenharmony_ci		new_iov = s_iov;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	/* 1st iov is a RFC1001 length followed by the rest of the packet */
12808c2ecf20Sopenharmony_ci	memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	new_iov[0].iov_base = new_iov[1].iov_base;
12838c2ecf20Sopenharmony_ci	new_iov[0].iov_len = 4;
12848c2ecf20Sopenharmony_ci	new_iov[1].iov_base += 4;
12858c2ecf20Sopenharmony_ci	new_iov[1].iov_len -= 4;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
12888c2ecf20Sopenharmony_ci	rqst.rq_iov = new_iov;
12898c2ecf20Sopenharmony_ci	rqst.rq_nvec = n_vec + 1;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	rc = cifs_send_recv(xid, ses, ses->server,
12928c2ecf20Sopenharmony_ci			    &rqst, resp_buf_type, flags, resp_iov);
12938c2ecf20Sopenharmony_ci	if (n_vec + 1 > CIFS_MAX_IOV_SIZE)
12948c2ecf20Sopenharmony_ci		kfree(new_iov);
12958c2ecf20Sopenharmony_ci	return rc;
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ciint
12998c2ecf20Sopenharmony_ciSendReceive(const unsigned int xid, struct cifs_ses *ses,
13008c2ecf20Sopenharmony_ci	    struct smb_hdr *in_buf, struct smb_hdr *out_buf,
13018c2ecf20Sopenharmony_ci	    int *pbytes_returned, const int flags)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	int rc = 0;
13048c2ecf20Sopenharmony_ci	struct mid_q_entry *midQ;
13058c2ecf20Sopenharmony_ci	unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
13068c2ecf20Sopenharmony_ci	struct kvec iov = { .iov_base = in_buf, .iov_len = len };
13078c2ecf20Sopenharmony_ci	struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
13088c2ecf20Sopenharmony_ci	struct cifs_credits credits = { .value = 1, .instance = 0 };
13098c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	if (ses == NULL) {
13128c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Null smb session\n");
13138c2ecf20Sopenharmony_ci		return -EIO;
13148c2ecf20Sopenharmony_ci	}
13158c2ecf20Sopenharmony_ci	server = ses->server;
13168c2ecf20Sopenharmony_ci	if (server == NULL) {
13178c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Null tcp session\n");
13188c2ecf20Sopenharmony_ci		return -EIO;
13198c2ecf20Sopenharmony_ci	}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsExiting)
13228c2ecf20Sopenharmony_ci		return -ENOENT;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	/* Ensure that we do not send more than 50 overlapping requests
13258c2ecf20Sopenharmony_ci	   to the same server. We may make this configurable later or
13268c2ecf20Sopenharmony_ci	   use ses->maxReq */
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
13298c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
13308c2ecf20Sopenharmony_ci				len);
13318c2ecf20Sopenharmony_ci		return -EIO;
13328c2ecf20Sopenharmony_ci	}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci	rc = wait_for_free_request(server, flags, &credits.instance);
13358c2ecf20Sopenharmony_ci	if (rc)
13368c2ecf20Sopenharmony_ci		return rc;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	/* make sure that we sign in the same order that we send on this socket
13398c2ecf20Sopenharmony_ci	   and avoid races inside tcp sendmsg code that could cause corruption
13408c2ecf20Sopenharmony_ci	   of smb data */
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	mutex_lock(&server->srv_mutex);
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	rc = allocate_mid(ses, in_buf, &midQ);
13458c2ecf20Sopenharmony_ci	if (rc) {
13468c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
13478c2ecf20Sopenharmony_ci		/* Update # of requests on wire to server */
13488c2ecf20Sopenharmony_ci		add_credits(server, &credits, 0);
13498c2ecf20Sopenharmony_ci		return rc;
13508c2ecf20Sopenharmony_ci	}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
13538c2ecf20Sopenharmony_ci	if (rc) {
13548c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
13558c2ecf20Sopenharmony_ci		goto out;
13568c2ecf20Sopenharmony_ci	}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	midQ->mid_state = MID_REQUEST_SUBMITTED;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	rc = smb_send(server, in_buf, len);
13618c2ecf20Sopenharmony_ci	cifs_save_when_sent(midQ);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	if (rc < 0)
13648c2ecf20Sopenharmony_ci		server->sequence_number -= 2;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	mutex_unlock(&server->srv_mutex);
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	if (rc < 0)
13698c2ecf20Sopenharmony_ci		goto out;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	rc = wait_for_response(server, midQ);
13728c2ecf20Sopenharmony_ci	if (rc != 0) {
13738c2ecf20Sopenharmony_ci		send_cancel(server, &rqst, midQ);
13748c2ecf20Sopenharmony_ci		spin_lock(&GlobalMid_Lock);
13758c2ecf20Sopenharmony_ci		if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
13768c2ecf20Sopenharmony_ci			/* no longer considered to be "in-flight" */
13778c2ecf20Sopenharmony_ci			midQ->callback = DeleteMidQEntry;
13788c2ecf20Sopenharmony_ci			spin_unlock(&GlobalMid_Lock);
13798c2ecf20Sopenharmony_ci			add_credits(server, &credits, 0);
13808c2ecf20Sopenharmony_ci			return rc;
13818c2ecf20Sopenharmony_ci		}
13828c2ecf20Sopenharmony_ci		spin_unlock(&GlobalMid_Lock);
13838c2ecf20Sopenharmony_ci	}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	rc = cifs_sync_mid_result(midQ, server);
13868c2ecf20Sopenharmony_ci	if (rc != 0) {
13878c2ecf20Sopenharmony_ci		add_credits(server, &credits, 0);
13888c2ecf20Sopenharmony_ci		return rc;
13898c2ecf20Sopenharmony_ci	}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	if (!midQ->resp_buf || !out_buf ||
13928c2ecf20Sopenharmony_ci	    midQ->mid_state != MID_RESPONSE_RECEIVED) {
13938c2ecf20Sopenharmony_ci		rc = -EIO;
13948c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "Bad MID state?\n");
13958c2ecf20Sopenharmony_ci		goto out;
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	*pbytes_returned = get_rfc1002_length(midQ->resp_buf);
13998c2ecf20Sopenharmony_ci	memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
14008c2ecf20Sopenharmony_ci	rc = cifs_check_receive(midQ, server, 0);
14018c2ecf20Sopenharmony_ciout:
14028c2ecf20Sopenharmony_ci	cifs_delete_mid(midQ);
14038c2ecf20Sopenharmony_ci	add_credits(server, &credits, 0);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	return rc;
14068c2ecf20Sopenharmony_ci}
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
14098c2ecf20Sopenharmony_ci   blocking lock to return. */
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_cistatic int
14128c2ecf20Sopenharmony_cisend_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
14138c2ecf20Sopenharmony_ci			struct smb_hdr *in_buf,
14148c2ecf20Sopenharmony_ci			struct smb_hdr *out_buf)
14158c2ecf20Sopenharmony_ci{
14168c2ecf20Sopenharmony_ci	int bytes_returned;
14178c2ecf20Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
14188c2ecf20Sopenharmony_ci	LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	/* We just modify the current in_buf to change
14218c2ecf20Sopenharmony_ci	   the type of lock from LOCKING_ANDX_SHARED_LOCK
14228c2ecf20Sopenharmony_ci	   or LOCKING_ANDX_EXCLUSIVE_LOCK to
14238c2ecf20Sopenharmony_ci	   LOCKING_ANDX_CANCEL_LOCK. */
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
14268c2ecf20Sopenharmony_ci	pSMB->Timeout = 0;
14278c2ecf20Sopenharmony_ci	pSMB->hdr.Mid = get_next_mid(ses->server);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	return SendReceive(xid, ses, in_buf, out_buf,
14308c2ecf20Sopenharmony_ci			&bytes_returned, 0);
14318c2ecf20Sopenharmony_ci}
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ciint
14348c2ecf20Sopenharmony_ciSendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
14358c2ecf20Sopenharmony_ci	    struct smb_hdr *in_buf, struct smb_hdr *out_buf,
14368c2ecf20Sopenharmony_ci	    int *pbytes_returned)
14378c2ecf20Sopenharmony_ci{
14388c2ecf20Sopenharmony_ci	int rc = 0;
14398c2ecf20Sopenharmony_ci	int rstart = 0;
14408c2ecf20Sopenharmony_ci	struct mid_q_entry *midQ;
14418c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
14428c2ecf20Sopenharmony_ci	unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
14438c2ecf20Sopenharmony_ci	struct kvec iov = { .iov_base = in_buf, .iov_len = len };
14448c2ecf20Sopenharmony_ci	struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
14458c2ecf20Sopenharmony_ci	unsigned int instance;
14468c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	if (tcon == NULL || tcon->ses == NULL) {
14498c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Null smb session\n");
14508c2ecf20Sopenharmony_ci		return -EIO;
14518c2ecf20Sopenharmony_ci	}
14528c2ecf20Sopenharmony_ci	ses = tcon->ses;
14538c2ecf20Sopenharmony_ci	server = ses->server;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	if (server == NULL) {
14568c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Null tcp session\n");
14578c2ecf20Sopenharmony_ci		return -EIO;
14588c2ecf20Sopenharmony_ci	}
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsExiting)
14618c2ecf20Sopenharmony_ci		return -ENOENT;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	/* Ensure that we do not send more than 50 overlapping requests
14648c2ecf20Sopenharmony_ci	   to the same server. We may make this configurable later or
14658c2ecf20Sopenharmony_ci	   use ses->maxReq */
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
14688c2ecf20Sopenharmony_ci		cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
14698c2ecf20Sopenharmony_ci			      len);
14708c2ecf20Sopenharmony_ci		return -EIO;
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
14748c2ecf20Sopenharmony_ci	if (rc)
14758c2ecf20Sopenharmony_ci		return rc;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	/* make sure that we sign in the same order that we send on this socket
14788c2ecf20Sopenharmony_ci	   and avoid races inside tcp sendmsg code that could cause corruption
14798c2ecf20Sopenharmony_ci	   of smb data */
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	mutex_lock(&server->srv_mutex);
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	rc = allocate_mid(ses, in_buf, &midQ);
14848c2ecf20Sopenharmony_ci	if (rc) {
14858c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
14868c2ecf20Sopenharmony_ci		return rc;
14878c2ecf20Sopenharmony_ci	}
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
14908c2ecf20Sopenharmony_ci	if (rc) {
14918c2ecf20Sopenharmony_ci		cifs_delete_mid(midQ);
14928c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
14938c2ecf20Sopenharmony_ci		return rc;
14948c2ecf20Sopenharmony_ci	}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	midQ->mid_state = MID_REQUEST_SUBMITTED;
14978c2ecf20Sopenharmony_ci	rc = smb_send(server, in_buf, len);
14988c2ecf20Sopenharmony_ci	cifs_save_when_sent(midQ);
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	if (rc < 0)
15018c2ecf20Sopenharmony_ci		server->sequence_number -= 2;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	mutex_unlock(&server->srv_mutex);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	if (rc < 0) {
15068c2ecf20Sopenharmony_ci		cifs_delete_mid(midQ);
15078c2ecf20Sopenharmony_ci		return rc;
15088c2ecf20Sopenharmony_ci	}
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	/* Wait for a reply - allow signals to interrupt. */
15118c2ecf20Sopenharmony_ci	rc = wait_event_interruptible(server->response_q,
15128c2ecf20Sopenharmony_ci		(!(midQ->mid_state == MID_REQUEST_SUBMITTED)) ||
15138c2ecf20Sopenharmony_ci		((server->tcpStatus != CifsGood) &&
15148c2ecf20Sopenharmony_ci		 (server->tcpStatus != CifsNew)));
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	/* Were we interrupted by a signal ? */
15178c2ecf20Sopenharmony_ci	if ((rc == -ERESTARTSYS) &&
15188c2ecf20Sopenharmony_ci		(midQ->mid_state == MID_REQUEST_SUBMITTED) &&
15198c2ecf20Sopenharmony_ci		((server->tcpStatus == CifsGood) ||
15208c2ecf20Sopenharmony_ci		 (server->tcpStatus == CifsNew))) {
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci		if (in_buf->Command == SMB_COM_TRANSACTION2) {
15238c2ecf20Sopenharmony_ci			/* POSIX lock. We send a NT_CANCEL SMB to cause the
15248c2ecf20Sopenharmony_ci			   blocking lock to return. */
15258c2ecf20Sopenharmony_ci			rc = send_cancel(server, &rqst, midQ);
15268c2ecf20Sopenharmony_ci			if (rc) {
15278c2ecf20Sopenharmony_ci				cifs_delete_mid(midQ);
15288c2ecf20Sopenharmony_ci				return rc;
15298c2ecf20Sopenharmony_ci			}
15308c2ecf20Sopenharmony_ci		} else {
15318c2ecf20Sopenharmony_ci			/* Windows lock. We send a LOCKINGX_CANCEL_LOCK
15328c2ecf20Sopenharmony_ci			   to cause the blocking lock to return. */
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci			rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci			/* If we get -ENOLCK back the lock may have
15378c2ecf20Sopenharmony_ci			   already been removed. Don't exit in this case. */
15388c2ecf20Sopenharmony_ci			if (rc && rc != -ENOLCK) {
15398c2ecf20Sopenharmony_ci				cifs_delete_mid(midQ);
15408c2ecf20Sopenharmony_ci				return rc;
15418c2ecf20Sopenharmony_ci			}
15428c2ecf20Sopenharmony_ci		}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci		rc = wait_for_response(server, midQ);
15458c2ecf20Sopenharmony_ci		if (rc) {
15468c2ecf20Sopenharmony_ci			send_cancel(server, &rqst, midQ);
15478c2ecf20Sopenharmony_ci			spin_lock(&GlobalMid_Lock);
15488c2ecf20Sopenharmony_ci			if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
15498c2ecf20Sopenharmony_ci				/* no longer considered to be "in-flight" */
15508c2ecf20Sopenharmony_ci				midQ->callback = DeleteMidQEntry;
15518c2ecf20Sopenharmony_ci				spin_unlock(&GlobalMid_Lock);
15528c2ecf20Sopenharmony_ci				return rc;
15538c2ecf20Sopenharmony_ci			}
15548c2ecf20Sopenharmony_ci			spin_unlock(&GlobalMid_Lock);
15558c2ecf20Sopenharmony_ci		}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci		/* We got the response - restart system call. */
15588c2ecf20Sopenharmony_ci		rstart = 1;
15598c2ecf20Sopenharmony_ci	}
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	rc = cifs_sync_mid_result(midQ, server);
15628c2ecf20Sopenharmony_ci	if (rc != 0)
15638c2ecf20Sopenharmony_ci		return rc;
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	/* rcvd frame is ok */
15668c2ecf20Sopenharmony_ci	if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) {
15678c2ecf20Sopenharmony_ci		rc = -EIO;
15688c2ecf20Sopenharmony_ci		cifs_tcon_dbg(VFS, "Bad MID state?\n");
15698c2ecf20Sopenharmony_ci		goto out;
15708c2ecf20Sopenharmony_ci	}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	*pbytes_returned = get_rfc1002_length(midQ->resp_buf);
15738c2ecf20Sopenharmony_ci	memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
15748c2ecf20Sopenharmony_ci	rc = cifs_check_receive(midQ, server, 0);
15758c2ecf20Sopenharmony_ciout:
15768c2ecf20Sopenharmony_ci	cifs_delete_mid(midQ);
15778c2ecf20Sopenharmony_ci	if (rstart && rc == -EACCES)
15788c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
15798c2ecf20Sopenharmony_ci	return rc;
15808c2ecf20Sopenharmony_ci}
1581