|
@@ -319,25 +319,328 @@ requeue_echo:
|
|
|
queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL);
|
|
|
}
|
|
|
|
|
|
+static bool
|
|
|
+allocate_buffers(char **bigbuf, char **smallbuf, unsigned int size,
|
|
|
+ bool is_large_buf)
|
|
|
+{
|
|
|
+ char *bbuf = *bigbuf, *sbuf = *smallbuf;
|
|
|
+
|
|
|
+ if (bbuf == NULL) {
|
|
|
+ bbuf = (char *)cifs_buf_get();
|
|
|
+ if (!bbuf) {
|
|
|
+ cERROR(1, "No memory for large SMB response");
|
|
|
+ msleep(3000);
|
|
|
+ /* retry will check if exiting */
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else if (is_large_buf) {
|
|
|
+ /* we are reusing a dirty large buf, clear its start */
|
|
|
+ memset(bbuf, 0, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (sbuf == NULL) {
|
|
|
+ sbuf = (char *)cifs_small_buf_get();
|
|
|
+ if (!sbuf) {
|
|
|
+ cERROR(1, "No memory for SMB response");
|
|
|
+ msleep(1000);
|
|
|
+ /* retry will check if exiting */
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ /* beginning of smb buffer is cleared in our buf_get */
|
|
|
+ } else {
|
|
|
+ /* if existing small buf clear beginning */
|
|
|
+ memset(sbuf, 0, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ *bigbuf = bbuf;
|
|
|
+ *smallbuf = sbuf;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+read_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg,
|
|
|
+ struct kvec *iov, unsigned int to_read,
|
|
|
+ unsigned int *ptotal_read, bool is_header_read)
|
|
|
+{
|
|
|
+ int length, rc = 0;
|
|
|
+ unsigned int total_read;
|
|
|
+ char *buf = iov->iov_base;
|
|
|
+
|
|
|
+ for (total_read = 0; total_read < to_read; total_read += length) {
|
|
|
+ length = kernel_recvmsg(server->ssocket, smb_msg, iov, 1,
|
|
|
+ to_read - total_read, 0);
|
|
|
+ if (server->tcpStatus == CifsExiting) {
|
|
|
+ /* then will exit */
|
|
|
+ rc = 2;
|
|
|
+ break;
|
|
|
+ } else if (server->tcpStatus == CifsNeedReconnect) {
|
|
|
+ cifs_reconnect(server);
|
|
|
+ /* Reconnect wakes up rspns q */
|
|
|
+ /* Now we will reread sock */
|
|
|
+ rc = 1;
|
|
|
+ break;
|
|
|
+ } else if (length == -ERESTARTSYS ||
|
|
|
+ length == -EAGAIN ||
|
|
|
+ length == -EINTR) {
|
|
|
+ /*
|
|
|
+ * Minimum sleep to prevent looping, allowing socket
|
|
|
+ * to clear and app threads to set tcpStatus
|
|
|
+ * CifsNeedReconnect if server hung.
|
|
|
+ */
|
|
|
+ usleep_range(1000, 2000);
|
|
|
+ length = 0;
|
|
|
+ if (!is_header_read)
|
|
|
+ continue;
|
|
|
+ /* Special handling for header read */
|
|
|
+ if (total_read) {
|
|
|
+ iov->iov_base = (to_read - total_read) +
|
|
|
+ buf;
|
|
|
+ iov->iov_len = to_read - total_read;
|
|
|
+ smb_msg->msg_control = NULL;
|
|
|
+ smb_msg->msg_controllen = 0;
|
|
|
+ rc = 3;
|
|
|
+ } else
|
|
|
+ rc = 1;
|
|
|
+ break;
|
|
|
+ } else if (length <= 0) {
|
|
|
+ cERROR(1, "Received no data, expecting %d",
|
|
|
+ to_read - total_read);
|
|
|
+ cifs_reconnect(server);
|
|
|
+ rc = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ *ptotal_read = total_read;
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static bool
|
|
|
+check_rfc1002_header(struct TCP_Server_Info *server, char *buf)
|
|
|
+{
|
|
|
+ char temp = *buf;
|
|
|
+ unsigned int pdu_length = be32_to_cpu(
|
|
|
+ ((struct smb_hdr *)buf)->smb_buf_length);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The first byte big endian of the length field,
|
|
|
+ * is actually not part of the length but the type
|
|
|
+ * with the most common, zero, as regular data.
|
|
|
+ */
|
|
|
+ if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
|
|
|
+ return false;
|
|
|
+ } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
|
|
|
+ cFYI(1, "Good RFC 1002 session rsp");
|
|
|
+ return false;
|
|
|
+ } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
|
|
|
+ /*
|
|
|
+ * We get this from Windows 98 instead of an error on
|
|
|
+ * SMB negprot response.
|
|
|
+ */
|
|
|
+ cFYI(1, "Negative RFC1002 Session Response Error 0x%x)",
|
|
|
+ pdu_length);
|
|
|
+ /* give server a second to clean up */
|
|
|
+ msleep(1000);
|
|
|
+ /*
|
|
|
+ * Always try 445 first on reconnect since we get NACK
|
|
|
+ * on some if we ever connected to port 139 (the NACK
|
|
|
+ * is since we do not begin with RFC1001 session
|
|
|
+ * initialize frame).
|
|
|
+ */
|
|
|
+ cifs_set_port((struct sockaddr *)
|
|
|
+ &server->dstaddr, CIFS_PORT);
|
|
|
+ cifs_reconnect(server);
|
|
|
+ wake_up(&server->response_q);
|
|
|
+ return false;
|
|
|
+ } else if (temp != (char) 0) {
|
|
|
+ cERROR(1, "Unknown RFC 1002 frame");
|
|
|
+ cifs_dump_mem(" Received Data: ", buf, 4);
|
|
|
+ cifs_reconnect(server);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* else we have an SMB response */
|
|
|
+ if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
|
|
|
+ (pdu_length < sizeof(struct smb_hdr) - 1 - 4)) {
|
|
|
+ cERROR(1, "Invalid size SMB length %d pdu_length %d",
|
|
|
+ 4, pdu_length+4);
|
|
|
+ cifs_reconnect(server);
|
|
|
+ wake_up(&server->response_q);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static struct mid_q_entry *
|
|
|
+find_cifs_mid(struct TCP_Server_Info *server, struct smb_hdr *buf,
|
|
|
+ int *length, bool is_large_buf, bool *is_multi_rsp, char **bigbuf)
|
|
|
+{
|
|
|
+ struct mid_q_entry *mid = NULL, *tmp_mid, *ret = NULL;
|
|
|
+
|
|
|
+ spin_lock(&GlobalMid_Lock);
|
|
|
+ list_for_each_entry_safe(mid, tmp_mid, &server->pending_mid_q, qhead) {
|
|
|
+ if (mid->mid != buf->Mid ||
|
|
|
+ mid->midState != MID_REQUEST_SUBMITTED ||
|
|
|
+ mid->command != buf->Command)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (*length == 0 && check2ndT2(buf, server->maxBuf) > 0) {
|
|
|
+ /* We have a multipart transact2 resp */
|
|
|
+ *is_multi_rsp = true;
|
|
|
+ if (mid->resp_buf) {
|
|
|
+ /* merge response - fix up 1st*/
|
|
|
+ *length = coalesce_t2(buf, mid->resp_buf);
|
|
|
+ if (*length > 0) {
|
|
|
+ *length = 0;
|
|
|
+ mid->multiRsp = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /* All parts received or packet is malformed. */
|
|
|
+ mid->multiEnd = true;
|
|
|
+ goto multi_t2_fnd;
|
|
|
+ }
|
|
|
+ if (!is_large_buf) {
|
|
|
+ /*FIXME: switch to already allocated largebuf?*/
|
|
|
+ cERROR(1, "1st trans2 resp needs bigbuf");
|
|
|
+ } else {
|
|
|
+ /* Have first buffer */
|
|
|
+ mid->resp_buf = buf;
|
|
|
+ mid->largeBuf = true;
|
|
|
+ *bigbuf = NULL;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ mid->resp_buf = buf;
|
|
|
+ mid->largeBuf = is_large_buf;
|
|
|
+multi_t2_fnd:
|
|
|
+ if (*length == 0)
|
|
|
+ mid->midState = MID_RESPONSE_RECEIVED;
|
|
|
+ else
|
|
|
+ mid->midState = MID_RESPONSE_MALFORMED;
|
|
|
+#ifdef CONFIG_CIFS_STATS2
|
|
|
+ mid->when_received = jiffies;
|
|
|
+#endif
|
|
|
+ list_del_init(&mid->qhead);
|
|
|
+ ret = mid;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ spin_unlock(&GlobalMid_Lock);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void clean_demultiplex_info(struct TCP_Server_Info *server)
|
|
|
+{
|
|
|
+ int length;
|
|
|
+
|
|
|
+ /* take it off the list, if it's not already */
|
|
|
+ spin_lock(&cifs_tcp_ses_lock);
|
|
|
+ list_del_init(&server->tcp_ses_list);
|
|
|
+ spin_unlock(&cifs_tcp_ses_lock);
|
|
|
+
|
|
|
+ spin_lock(&GlobalMid_Lock);
|
|
|
+ server->tcpStatus = CifsExiting;
|
|
|
+ spin_unlock(&GlobalMid_Lock);
|
|
|
+ wake_up_all(&server->response_q);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check if we have blocked requests that need to free. Note that
|
|
|
+ * cifs_max_pending is normally 50, but can be set at module install
|
|
|
+ * time to as little as two.
|
|
|
+ */
|
|
|
+ spin_lock(&GlobalMid_Lock);
|
|
|
+ if (atomic_read(&server->inFlight) >= cifs_max_pending)
|
|
|
+ atomic_set(&server->inFlight, cifs_max_pending - 1);
|
|
|
+ /*
|
|
|
+ * We do not want to set the max_pending too low or we could end up
|
|
|
+ * with the counter going negative.
|
|
|
+ */
|
|
|
+ spin_unlock(&GlobalMid_Lock);
|
|
|
+ /*
|
|
|
+ * Although there should not be any requests blocked on this queue it
|
|
|
+ * can not hurt to be paranoid and try to wake up requests that may
|
|
|
+ * haven been blocked when more than 50 at time were on the wire to the
|
|
|
+ * same server - they now will see the session is in exit state and get
|
|
|
+ * out of SendReceive.
|
|
|
+ */
|
|
|
+ wake_up_all(&server->request_q);
|
|
|
+ /* give those requests time to exit */
|
|
|
+ msleep(125);
|
|
|
+
|
|
|
+ if (server->ssocket) {
|
|
|
+ sock_release(server->ssocket);
|
|
|
+ server->ssocket = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!list_empty(&server->pending_mid_q)) {
|
|
|
+ struct list_head dispose_list;
|
|
|
+ struct mid_q_entry *mid_entry;
|
|
|
+ struct list_head *tmp, *tmp2;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&dispose_list);
|
|
|
+ spin_lock(&GlobalMid_Lock);
|
|
|
+ list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
|
|
|
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
|
|
|
+ cFYI(1, "Clearing mid 0x%x", mid_entry->mid);
|
|
|
+ mid_entry->midState = MID_SHUTDOWN;
|
|
|
+ list_move(&mid_entry->qhead, &dispose_list);
|
|
|
+ }
|
|
|
+ spin_unlock(&GlobalMid_Lock);
|
|
|
+
|
|
|
+ /* now walk dispose list and issue callbacks */
|
|
|
+ list_for_each_safe(tmp, tmp2, &dispose_list) {
|
|
|
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
|
|
|
+ cFYI(1, "Callback mid 0x%x", mid_entry->mid);
|
|
|
+ list_del_init(&mid_entry->qhead);
|
|
|
+ mid_entry->callback(mid_entry);
|
|
|
+ }
|
|
|
+ /* 1/8th of sec is more than enough time for them to exit */
|
|
|
+ msleep(125);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!list_empty(&server->pending_mid_q)) {
|
|
|
+ /*
|
|
|
+ * mpx threads have not exited yet give them at least the smb
|
|
|
+ * send timeout time for long ops.
|
|
|
+ *
|
|
|
+ * Due to delays on oplock break requests, we need to wait at
|
|
|
+ * least 45 seconds before giving up on a request getting a
|
|
|
+ * response and going ahead and killing cifsd.
|
|
|
+ */
|
|
|
+ cFYI(1, "Wait for exit from demultiplex thread");
|
|
|
+ msleep(46000);
|
|
|
+ /*
|
|
|
+ * If threads still have not exited they are probably never
|
|
|
+ * coming home not much else we can do but free the memory.
|
|
|
+ */
|
|
|
+ }
|
|
|
+
|
|
|
+ kfree(server->hostname);
|
|
|
+ kfree(server);
|
|
|
+
|
|
|
+ length = atomic_dec_return(&tcpSesAllocCount);
|
|
|
+ if (length > 0)
|
|
|
+ mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
|
|
|
+ GFP_KERNEL);
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
cifs_demultiplex_thread(void *p)
|
|
|
{
|
|
|
int length;
|
|
|
struct TCP_Server_Info *server = p;
|
|
|
unsigned int pdu_length, total_read;
|
|
|
+ char *buf = NULL, *bigbuf = NULL, *smallbuf = NULL;
|
|
|
struct smb_hdr *smb_buffer = NULL;
|
|
|
- struct smb_hdr *bigbuf = NULL;
|
|
|
- struct smb_hdr *smallbuf = NULL;
|
|
|
struct msghdr smb_msg;
|
|
|
struct kvec iov;
|
|
|
- struct socket *csocket = server->ssocket;
|
|
|
- struct list_head *tmp, *tmp2;
|
|
|
struct task_struct *task_to_wake = NULL;
|
|
|
struct mid_q_entry *mid_entry;
|
|
|
- char temp;
|
|
|
bool isLargeBuf = false;
|
|
|
- bool isMultiRsp;
|
|
|
- int reconnect;
|
|
|
+ bool isMultiRsp = false;
|
|
|
+ int rc;
|
|
|
|
|
|
current->flags |= PF_MEMALLOC;
|
|
|
cFYI(1, "Demultiplex PID: %d", task_pid_nr(current));
|
|
@@ -351,35 +654,16 @@ cifs_demultiplex_thread(void *p)
|
|
|
while (server->tcpStatus != CifsExiting) {
|
|
|
if (try_to_freeze())
|
|
|
continue;
|
|
|
- if (bigbuf == NULL) {
|
|
|
- bigbuf = cifs_buf_get();
|
|
|
- if (!bigbuf) {
|
|
|
- cERROR(1, "No memory for large SMB response");
|
|
|
- msleep(3000);
|
|
|
- /* retry will check if exiting */
|
|
|
- continue;
|
|
|
- }
|
|
|
- } else if (isLargeBuf) {
|
|
|
- /* we are reusing a dirty large buf, clear its start */
|
|
|
- memset(bigbuf, 0, sizeof(struct smb_hdr));
|
|
|
- }
|
|
|
|
|
|
- if (smallbuf == NULL) {
|
|
|
- smallbuf = cifs_small_buf_get();
|
|
|
- if (!smallbuf) {
|
|
|
- cERROR(1, "No memory for SMB response");
|
|
|
- msleep(1000);
|
|
|
- /* retry will check if exiting */
|
|
|
- continue;
|
|
|
- }
|
|
|
- /* beginning of smb buffer is cleared in our buf_get */
|
|
|
- } else /* if existing small buf clear beginning */
|
|
|
- memset(smallbuf, 0, sizeof(struct smb_hdr));
|
|
|
+ if (!allocate_buffers(&bigbuf, &smallbuf,
|
|
|
+ sizeof(struct smb_hdr), isLargeBuf))
|
|
|
+ continue;
|
|
|
|
|
|
isLargeBuf = false;
|
|
|
isMultiRsp = false;
|
|
|
- smb_buffer = smallbuf;
|
|
|
- iov.iov_base = smb_buffer;
|
|
|
+ smb_buffer = (struct smb_hdr *)smallbuf;
|
|
|
+ buf = smallbuf;
|
|
|
+ iov.iov_base = buf;
|
|
|
iov.iov_len = 4;
|
|
|
smb_msg.msg_control = NULL;
|
|
|
smb_msg.msg_controllen = 0;
|
|
@@ -393,158 +677,50 @@ incomplete_rcv:
|
|
|
"Reconnecting...", server->hostname,
|
|
|
(echo_retries * SMB_ECHO_INTERVAL / HZ));
|
|
|
cifs_reconnect(server);
|
|
|
- csocket = server->ssocket;
|
|
|
wake_up(&server->response_q);
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- length =
|
|
|
- kernel_recvmsg(csocket, &smb_msg,
|
|
|
- &iov, 1, pdu_length, 0 /* BB other flags? */);
|
|
|
-
|
|
|
- if (server->tcpStatus == CifsExiting) {
|
|
|
+ rc = read_from_socket(server, &smb_msg, &iov, pdu_length,
|
|
|
+ &total_read, true /* header read */);
|
|
|
+ if (rc == 3)
|
|
|
+ goto incomplete_rcv;
|
|
|
+ else if (rc == 2)
|
|
|
break;
|
|
|
- } else if (server->tcpStatus == CifsNeedReconnect) {
|
|
|
- cFYI(1, "Reconnect after server stopped responding");
|
|
|
- cifs_reconnect(server);
|
|
|
- cFYI(1, "call to reconnect done");
|
|
|
- csocket = server->ssocket;
|
|
|
- continue;
|
|
|
- } else if (length == -ERESTARTSYS ||
|
|
|
- length == -EAGAIN ||
|
|
|
- length == -EINTR) {
|
|
|
- msleep(1); /* minimum sleep to prevent looping
|
|
|
- allowing socket to clear and app threads to set
|
|
|
- tcpStatus CifsNeedReconnect if server hung */
|
|
|
- if (pdu_length < 4) {
|
|
|
- iov.iov_base = (4 - pdu_length) +
|
|
|
- (char *)smb_buffer;
|
|
|
- iov.iov_len = pdu_length;
|
|
|
- smb_msg.msg_control = NULL;
|
|
|
- smb_msg.msg_controllen = 0;
|
|
|
- goto incomplete_rcv;
|
|
|
- } else
|
|
|
- continue;
|
|
|
- } else if (length <= 0) {
|
|
|
- cFYI(1, "Reconnect after unexpected peek error %d",
|
|
|
- length);
|
|
|
- cifs_reconnect(server);
|
|
|
- csocket = server->ssocket;
|
|
|
- wake_up(&server->response_q);
|
|
|
+ else if (rc == 1)
|
|
|
continue;
|
|
|
- } else if (length < pdu_length) {
|
|
|
- cFYI(1, "requested %d bytes but only got %d bytes",
|
|
|
- pdu_length, length);
|
|
|
- pdu_length -= length;
|
|
|
- msleep(1);
|
|
|
- goto incomplete_rcv;
|
|
|
- }
|
|
|
-
|
|
|
- /* The right amount was read from socket - 4 bytes */
|
|
|
- /* so we can now interpret the length field */
|
|
|
|
|
|
- /* the first byte big endian of the length field,
|
|
|
- is actually not part of the length but the type
|
|
|
- with the most common, zero, as regular data */
|
|
|
- temp = *((char *) smb_buffer);
|
|
|
+ /*
|
|
|
+ * The right amount was read from socket - 4 bytes,
|
|
|
+ * so we can now interpret the length field.
|
|
|
+ */
|
|
|
|
|
|
- /* Note that FC 1001 length is big endian on the wire,
|
|
|
- but we convert it here so it is always manipulated
|
|
|
- as host byte order */
|
|
|
+ /*
|
|
|
+ * Note that RFC 1001 length is big endian on the wire,
|
|
|
+ * but we convert it here so it is always manipulated
|
|
|
+ * as host byte order.
|
|
|
+ */
|
|
|
pdu_length = be32_to_cpu(smb_buffer->smb_buf_length);
|
|
|
|
|
|
cFYI(1, "rfc1002 length 0x%x", pdu_length+4);
|
|
|
-
|
|
|
- if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
|
|
|
- continue;
|
|
|
- } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
|
|
|
- cFYI(1, "Good RFC 1002 session rsp");
|
|
|
- continue;
|
|
|
- } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
|
|
|
- /* we get this from Windows 98 instead of
|
|
|
- an error on SMB negprot response */
|
|
|
- cFYI(1, "Negative RFC1002 Session Response Error 0x%x)",
|
|
|
- pdu_length);
|
|
|
- /* give server a second to clean up */
|
|
|
- msleep(1000);
|
|
|
- /* always try 445 first on reconnect since we get NACK
|
|
|
- * on some if we ever connected to port 139 (the NACK
|
|
|
- * is since we do not begin with RFC1001 session
|
|
|
- * initialize frame)
|
|
|
- */
|
|
|
- cifs_set_port((struct sockaddr *)
|
|
|
- &server->dstaddr, CIFS_PORT);
|
|
|
- cifs_reconnect(server);
|
|
|
- csocket = server->ssocket;
|
|
|
- wake_up(&server->response_q);
|
|
|
- continue;
|
|
|
- } else if (temp != (char) 0) {
|
|
|
- cERROR(1, "Unknown RFC 1002 frame");
|
|
|
- cifs_dump_mem(" Received Data: ", (char *)smb_buffer,
|
|
|
- length);
|
|
|
- cifs_reconnect(server);
|
|
|
- csocket = server->ssocket;
|
|
|
+ if (!check_rfc1002_header(server, buf))
|
|
|
continue;
|
|
|
- }
|
|
|
-
|
|
|
- /* else we have an SMB response */
|
|
|
- if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
|
|
|
- (pdu_length < sizeof(struct smb_hdr) - 1 - 4)) {
|
|
|
- cERROR(1, "Invalid size SMB length %d pdu_length %d",
|
|
|
- length, pdu_length+4);
|
|
|
- cifs_reconnect(server);
|
|
|
- csocket = server->ssocket;
|
|
|
- wake_up(&server->response_q);
|
|
|
- continue;
|
|
|
- }
|
|
|
|
|
|
/* else length ok */
|
|
|
- reconnect = 0;
|
|
|
-
|
|
|
if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
|
|
|
isLargeBuf = true;
|
|
|
memcpy(bigbuf, smallbuf, 4);
|
|
|
- smb_buffer = bigbuf;
|
|
|
+ smb_buffer = (struct smb_hdr *)bigbuf;
|
|
|
+ buf = bigbuf;
|
|
|
}
|
|
|
- length = 0;
|
|
|
- iov.iov_base = 4 + (char *)smb_buffer;
|
|
|
+
|
|
|
+ iov.iov_base = 4 + buf;
|
|
|
iov.iov_len = pdu_length;
|
|
|
- for (total_read = 0; total_read < pdu_length;
|
|
|
- total_read += length) {
|
|
|
- length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
|
|
|
- pdu_length - total_read, 0);
|
|
|
- if (server->tcpStatus == CifsExiting) {
|
|
|
- /* then will exit */
|
|
|
- reconnect = 2;
|
|
|
- break;
|
|
|
- } else if (server->tcpStatus == CifsNeedReconnect) {
|
|
|
- cifs_reconnect(server);
|
|
|
- csocket = server->ssocket;
|
|
|
- /* Reconnect wakes up rspns q */
|
|
|
- /* Now we will reread sock */
|
|
|
- reconnect = 1;
|
|
|
- break;
|
|
|
- } else if (length == -ERESTARTSYS ||
|
|
|
- length == -EAGAIN ||
|
|
|
- length == -EINTR) {
|
|
|
- msleep(1); /* minimum sleep to prevent looping,
|
|
|
- allowing socket to clear and app
|
|
|
- threads to set tcpStatus
|
|
|
- CifsNeedReconnect if server hung*/
|
|
|
- length = 0;
|
|
|
- continue;
|
|
|
- } else if (length <= 0) {
|
|
|
- cERROR(1, "Received no data, expecting %d",
|
|
|
- pdu_length - total_read);
|
|
|
- cifs_reconnect(server);
|
|
|
- csocket = server->ssocket;
|
|
|
- reconnect = 1;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (reconnect == 2)
|
|
|
+ rc = read_from_socket(server, &smb_msg, &iov, pdu_length,
|
|
|
+ &total_read, false);
|
|
|
+ if (rc == 2)
|
|
|
break;
|
|
|
- else if (reconnect == 1)
|
|
|
+ else if (rc == 1)
|
|
|
continue;
|
|
|
|
|
|
total_read += 4; /* account for rfc1002 hdr */
|
|
@@ -562,75 +738,13 @@ incomplete_rcv:
|
|
|
*/
|
|
|
length = checkSMB(smb_buffer, smb_buffer->Mid, total_read);
|
|
|
if (length != 0)
|
|
|
- cifs_dump_mem("Bad SMB: ", smb_buffer,
|
|
|
- min_t(unsigned int, total_read, 48));
|
|
|
+ cifs_dump_mem("Bad SMB: ", buf,
|
|
|
+ min_t(unsigned int, total_read, 48));
|
|
|
|
|
|
- mid_entry = NULL;
|
|
|
server->lstrp = jiffies;
|
|
|
|
|
|
- spin_lock(&GlobalMid_Lock);
|
|
|
- list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
|
|
|
- mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
|
|
|
-
|
|
|
- if (mid_entry->mid != smb_buffer->Mid ||
|
|
|
- mid_entry->midState != MID_REQUEST_SUBMITTED ||
|
|
|
- mid_entry->command != smb_buffer->Command) {
|
|
|
- mid_entry = NULL;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- if (length == 0 &&
|
|
|
- check2ndT2(smb_buffer, server->maxBuf) > 0) {
|
|
|
- /* We have a multipart transact2 resp */
|
|
|
- isMultiRsp = true;
|
|
|
- if (mid_entry->resp_buf) {
|
|
|
- /* merge response - fix up 1st*/
|
|
|
- length = coalesce_t2(smb_buffer,
|
|
|
- mid_entry->resp_buf);
|
|
|
- if (length > 0) {
|
|
|
- length = 0;
|
|
|
- mid_entry->multiRsp = true;
|
|
|
- break;
|
|
|
- } else {
|
|
|
- /* all parts received or
|
|
|
- * packet is malformed
|
|
|
- */
|
|
|
- mid_entry->multiEnd = true;
|
|
|
- goto multi_t2_fnd;
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (!isLargeBuf) {
|
|
|
- /*
|
|
|
- * FIXME: switch to already
|
|
|
- * allocated largebuf?
|
|
|
- */
|
|
|
- cERROR(1, "1st trans2 resp "
|
|
|
- "needs bigbuf");
|
|
|
- } else {
|
|
|
- /* Have first buffer */
|
|
|
- mid_entry->resp_buf =
|
|
|
- smb_buffer;
|
|
|
- mid_entry->largeBuf = true;
|
|
|
- bigbuf = NULL;
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- mid_entry->resp_buf = smb_buffer;
|
|
|
- mid_entry->largeBuf = isLargeBuf;
|
|
|
-multi_t2_fnd:
|
|
|
- if (length == 0)
|
|
|
- mid_entry->midState = MID_RESPONSE_RECEIVED;
|
|
|
- else
|
|
|
- mid_entry->midState = MID_RESPONSE_MALFORMED;
|
|
|
-#ifdef CONFIG_CIFS_STATS2
|
|
|
- mid_entry->when_received = jiffies;
|
|
|
-#endif
|
|
|
- list_del_init(&mid_entry->qhead);
|
|
|
- break;
|
|
|
- }
|
|
|
- spin_unlock(&GlobalMid_Lock);
|
|
|
-
|
|
|
+ mid_entry = find_cifs_mid(server, smb_buffer, &length,
|
|
|
+ isLargeBuf, &isMultiRsp, &bigbuf);
|
|
|
if (mid_entry != NULL) {
|
|
|
mid_entry->callback(mid_entry);
|
|
|
/* Was previous buf put in mpx struct for multi-rsp? */
|
|
@@ -648,7 +762,7 @@ multi_t2_fnd:
|
|
|
!isMultiRsp) {
|
|
|
cERROR(1, "No task to wake, unknown frame received! "
|
|
|
"NumMids %d", atomic_read(&midCount));
|
|
|
- cifs_dump_mem("Received Data is: ", (char *)smb_buffer,
|
|
|
+ cifs_dump_mem("Received Data is: ", buf,
|
|
|
sizeof(struct smb_hdr));
|
|
|
#ifdef CONFIG_CIFS_DEBUG2
|
|
|
cifs_dump_detail(smb_buffer);
|
|
@@ -658,88 +772,13 @@ multi_t2_fnd:
|
|
|
}
|
|
|
} /* end while !EXITING */
|
|
|
|
|
|
- /* take it off the list, if it's not already */
|
|
|
- spin_lock(&cifs_tcp_ses_lock);
|
|
|
- list_del_init(&server->tcp_ses_list);
|
|
|
- spin_unlock(&cifs_tcp_ses_lock);
|
|
|
-
|
|
|
- spin_lock(&GlobalMid_Lock);
|
|
|
- server->tcpStatus = CifsExiting;
|
|
|
- spin_unlock(&GlobalMid_Lock);
|
|
|
- wake_up_all(&server->response_q);
|
|
|
-
|
|
|
- /* check if we have blocked requests that need to free */
|
|
|
- /* Note that cifs_max_pending is normally 50, but
|
|
|
- can be set at module install time to as little as two */
|
|
|
- spin_lock(&GlobalMid_Lock);
|
|
|
- if (atomic_read(&server->inFlight) >= cifs_max_pending)
|
|
|
- atomic_set(&server->inFlight, cifs_max_pending - 1);
|
|
|
- /* We do not want to set the max_pending too low or we
|
|
|
- could end up with the counter going negative */
|
|
|
- spin_unlock(&GlobalMid_Lock);
|
|
|
- /* Although there should not be any requests blocked on
|
|
|
- this queue it can not hurt to be paranoid and try to wake up requests
|
|
|
- that may haven been blocked when more than 50 at time were on the wire
|
|
|
- to the same server - they now will see the session is in exit state
|
|
|
- and get out of SendReceive. */
|
|
|
- wake_up_all(&server->request_q);
|
|
|
- /* give those requests time to exit */
|
|
|
- msleep(125);
|
|
|
-
|
|
|
- if (server->ssocket) {
|
|
|
- sock_release(csocket);
|
|
|
- server->ssocket = NULL;
|
|
|
- }
|
|
|
/* buffer usually freed in free_mid - need to free it here on exit */
|
|
|
cifs_buf_release(bigbuf);
|
|
|
if (smallbuf) /* no sense logging a debug message if NULL */
|
|
|
cifs_small_buf_release(smallbuf);
|
|
|
|
|
|
- if (!list_empty(&server->pending_mid_q)) {
|
|
|
- struct list_head dispose_list;
|
|
|
-
|
|
|
- INIT_LIST_HEAD(&dispose_list);
|
|
|
- spin_lock(&GlobalMid_Lock);
|
|
|
- list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
|
|
|
- mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
|
|
|
- cFYI(1, "Clearing mid 0x%x", mid_entry->mid);
|
|
|
- mid_entry->midState = MID_SHUTDOWN;
|
|
|
- list_move(&mid_entry->qhead, &dispose_list);
|
|
|
- }
|
|
|
- spin_unlock(&GlobalMid_Lock);
|
|
|
-
|
|
|
- /* now walk dispose list and issue callbacks */
|
|
|
- list_for_each_safe(tmp, tmp2, &dispose_list) {
|
|
|
- mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
|
|
|
- cFYI(1, "Callback mid 0x%x", mid_entry->mid);
|
|
|
- list_del_init(&mid_entry->qhead);
|
|
|
- mid_entry->callback(mid_entry);
|
|
|
- }
|
|
|
- /* 1/8th of sec is more than enough time for them to exit */
|
|
|
- msleep(125);
|
|
|
- }
|
|
|
-
|
|
|
- if (!list_empty(&server->pending_mid_q)) {
|
|
|
- /* mpx threads have not exited yet give them
|
|
|
- at least the smb send timeout time for long ops */
|
|
|
- /* due to delays on oplock break requests, we need
|
|
|
- to wait at least 45 seconds before giving up
|
|
|
- on a request getting a response and going ahead
|
|
|
- and killing cifsd */
|
|
|
- cFYI(1, "Wait for exit from demultiplex thread");
|
|
|
- msleep(46000);
|
|
|
- /* if threads still have not exited they are probably never
|
|
|
- coming home not much else we can do but free the memory */
|
|
|
- }
|
|
|
-
|
|
|
- kfree(server->hostname);
|
|
|
task_to_wake = xchg(&server->tsk, NULL);
|
|
|
- kfree(server);
|
|
|
-
|
|
|
- length = atomic_dec_return(&tcpSesAllocCount);
|
|
|
- if (length > 0)
|
|
|
- mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
|
|
|
- GFP_KERNEL);
|
|
|
+ clean_demultiplex_info(server);
|
|
|
|
|
|
/* if server->tsk was NULL then wait for a signal before exiting */
|
|
|
if (!task_to_wake) {
|
|
@@ -3193,15 +3232,9 @@ mount_fail_check:
|
|
|
else
|
|
|
cifs_put_tcp_session(srvTcp);
|
|
|
bdi_destroy(&cifs_sb->bdi);
|
|
|
- goto out;
|
|
|
}
|
|
|
|
|
|
- /* volume_info->password is freed above when existing session found
|
|
|
- (in which case it is not needed anymore) but when new sesion is created
|
|
|
- the password ptr is put in the new session structure (in which case the
|
|
|
- password will be freed at unmount time) */
|
|
|
out:
|
|
|
- /* zero out password before freeing */
|
|
|
FreeXid(xid);
|
|
|
return rc;
|
|
|
}
|