4 * Copyright (c) International Business Machines Corp., 2002
5 * Author(s): Steve French (sfrench@us.ibm.com)
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15 * the GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <linux/list.h>
24 #include <linux/wait.h>
25 #include <linux/net.h>
26 #include <linux/version.h>
27 #include <asm/uaccess.h>
28 #include <asm/processor.h>
31 #include "cifsproto.h"
32 #include "cifs_debug.h"
34 extern kmem_cache_t *cifs_mid_cachep;
35 extern kmem_cache_t *cifs_oplock_cachep;
38 AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
40 struct mid_q_entry *temp;
41 int timeout = 10 * HZ;
43 /* BB add spinlock to protect midq for each session BB */
45 cERROR(1, ("Null session passed in to AllocMidQEntry "));
48 if (ses->server == NULL) {
49 cERROR(1, ("Null TCP session in AllocMidQEntry"));
53 temp = (struct mid_q_entry *) kmem_cache_alloc(cifs_mid_cachep,
58 memset(temp, 0, sizeof (struct mid_q_entry));
59 temp->mid = smb_buffer->Mid; /* always LE */
60 temp->pid = current->pid;
61 temp->command = smb_buffer->Command;
62 cFYI(1, ("For smb_command %d", temp->command));
63 do_gettimeofday(&temp->when_sent);
68 while ((ses->server->tcpStatus != CifsGood) && (timeout > 0)){
69 /* Give the tcp thread up to 10 seconds to reconnect */
70 /* Should we wake up tcp thread first? BB */
71 timeout = wait_event_interruptible_timeout(ses->server->response_q,
72 (ses->server->tcpStatus == CifsGood), timeout);
75 if (ses->server->tcpStatus == CifsGood) {
76 write_lock(&GlobalMid_Lock);
77 list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
78 atomic_inc(&midCount);
79 temp->midState = MID_REQUEST_ALLOCATED;
80 write_unlock(&GlobalMid_Lock);
82 cERROR(1,("Need to reconnect after session died to server"));
84 kmem_cache_free(cifs_mid_cachep, temp);
91 DeleteMidQEntry(struct mid_q_entry *midEntry)
93 /* BB add spinlock to protect midq for each session BB */
94 write_lock(&GlobalMid_Lock);
95 midEntry->midState = MID_FREE;
96 list_del(&midEntry->qhead);
97 atomic_dec(&midCount);
98 write_unlock(&GlobalMid_Lock);
99 buf_release(midEntry->resp_buf);
100 kmem_cache_free(cifs_mid_cachep, midEntry);
103 struct oplock_q_entry *
104 AllocOplockQEntry(struct file * file, struct cifsTconInfo * tcon)
106 struct oplock_q_entry *temp;
107 if ((file == NULL) || (tcon == NULL)) {
108 cERROR(1, ("Null parms passed to AllocOplockQEntry"));
111 temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
116 temp->file_to_flush = file;
118 write_lock(&GlobalMid_Lock);
119 list_add_tail(&temp->qhead, &GlobalOplock_Q);
120 write_unlock(&GlobalMid_Lock);
126 void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
128 /* BB add spinlock to protect midq for each session BB */
129 write_lock(&GlobalMid_Lock);
130 /* should we check if list empty first? */
131 list_del(&oplockEntry->qhead);
132 write_unlock(&GlobalMid_Lock);
133 kmem_cache_free(cifs_oplock_cachep, oplockEntry);
137 smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
138 unsigned int smb_buf_length, struct sockaddr *sin)
141 struct msghdr smb_msg;
143 mm_segment_t temp_fs;
146 return -ENOTSOCK; /* BB eventually add reconnect code here */
147 /* ssocket->sk->allocation = GFP_BUFFER; *//* BB is this spurious? */
148 iov.iov_base = smb_buffer;
149 iov.iov_len = smb_buf_length + 4;
151 smb_msg.msg_name = sin;
152 smb_msg.msg_namelen = sizeof (struct sockaddr);
153 smb_msg.msg_iov = &iov;
154 smb_msg.msg_iovlen = 1;
155 smb_msg.msg_control = NULL;
156 smb_msg.msg_controllen = 0;
157 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
159 /* smb header is converted in header_assemble. bcc and rest of SMB word
160 area, and byte area if necessary, is converted to littleendian in
161 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
162 Flags2 is converted in SendReceive */
164 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
165 cFYI(1, ("Sending smb of length %d ", smb_buf_length));
166 dump_smb(smb_buffer, smb_buf_length + 4);
168 temp_fs = get_fs(); /* we must turn off socket api parm checking */
170 rc = sock_sendmsg(ssocket, &smb_msg, smb_buf_length + 4);
176 ("Error %d sending data on socket to server.", rc));
184 SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
185 struct smb_hdr *in_buf, struct smb_hdr *out_buf,
186 int *pbytes_returned, const int long_op)
189 unsigned int receive_len;
191 struct mid_q_entry *midQ;
193 midQ = AllocMidQEntry(in_buf, ses);
196 if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) {
198 ("Illegal length, greater than maximum frame, %d ",
199 in_buf->smb_buf_length));
200 DeleteMidQEntry(midQ);
204 if (in_buf->smb_buf_length > 12)
205 in_buf->Flags2 = cpu_to_le16(in_buf->Flags2);
207 rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
209 midQ->midState = MID_REQUEST_SUBMITTED;
210 rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
211 (struct sockaddr *) &(ses->server->sockAddr));
214 goto cifs_no_response_exit;
215 if (long_op > 1) /* writes past end of file can take looooong time */
217 else if (long_op == 1)
221 /* wait for 15 seconds or until woken up due to response arriving or
222 due to last connection to this server being unmounted */
224 timeout = wait_event_interruptible_timeout(ses->server->response_q,
226 midState & MID_RESPONSE_RECEIVED,
228 if (signal_pending(current)) {
229 cERROR(1, ("CIFS: caught signal"));
230 DeleteMidQEntry(midQ);
235 be32_to_cpu(midQ->resp_buf->smb_buf_length);
237 DeleteMidQEntry(midQ);
244 ("Timeout on receive. Assume response SMB is invalid."));
246 } else if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) {
248 ("Frame too large received. Length: %d Xid: %d",
251 } else { /* rcvd frame is ok */
253 if (midQ->resp_buf && out_buf
254 && (midQ->midState == MID_RESPONSE_RECEIVED)) {
255 memcpy(out_buf, midQ->resp_buf,
257 4 /* include 4 byte RFC1001 header */ );
259 dump_smb(out_buf, 92);
260 /* convert the length into a more usable form */
261 out_buf->smb_buf_length =
262 be32_to_cpu(out_buf->smb_buf_length);
263 if((out_buf->smb_buf_length > 24) &&
264 (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
265 rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
267 cFYI(1,("Unexpected signature received from server"));
270 if (out_buf->smb_buf_length > 12)
271 out_buf->Flags2 = le16_to_cpu(out_buf->Flags2);
272 if (out_buf->smb_buf_length > 28)
273 out_buf->Pid = le16_to_cpu(out_buf->Pid);
274 if (out_buf->smb_buf_length > 28)
276 le16_to_cpu(out_buf->PidHigh);
278 *pbytes_returned = out_buf->smb_buf_length;
280 /* BB special case reconnect tid and reconnect uid here? */
281 rc = map_smb_to_linux_error(out_buf);
283 /* convert ByteCount if necessary */
285 sizeof (struct smb_hdr) -
286 4 /* do not count RFC1001 header */ +
287 (2 * out_buf->WordCount) + 2 /* bcc */ )
288 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
292 cifs_no_response_exit:
293 DeleteMidQEntry(midQ); /* BB what if process is killed?
294 - BB add background daemon to clean up Mid entries from
295 killed processes & test killing process with active mid */