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