[linux-cifs-client] [PATCH 2/3] [CIFS] convert cifs_demultiplex_thread to use nonblocking sockets

Jeff Layton jlayton at redhat.com
Thu Apr 17 20:59:05 GMT 2008


Currently, cifs_demultiplex_thread uses blocking calls to
kernel_recvmsg to receive data. These calls do not return immediately
when kthread_stop tries to wake the thread, so cifs_mount and
cifs_umount also signal the thread to make it break out of kernel_recvmsg.

This patch converts cifs_demultiplex_thread to repeatedly check the
socket with a TIOCINQ ioctl to see if there is data waiting. If there
isn't then it sleeps a bit and polls again. If there is data then
we call kernel_recvmsg in non-blocking mode to get it. Since we're
polling like this, we can check kthread_should_stop on each poll and
no longer need to signal when tearing down the thread.

Signed-off-by: Jeff Layton <jlayton at redhat.com>
---
 fs/cifs/connect.c |   75 +++++++++++++++++++++++++++++-----------------------
 1 files changed, 42 insertions(+), 33 deletions(-)

diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 40ffbac..ae46bc4 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -35,6 +35,7 @@
 #include <linux/freezer.h>
 #include <asm/uaccess.h>
 #include <asm/processor.h>
+#include <asm/ioctls.h>
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsproto.h"
@@ -329,6 +330,23 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
 
 }
 
+/* check to see whether there is available data on the socket */
+static inline int
+cifs_wait_for_recv(struct socket *sock)
+{
+	int		avail = 0, err = 0;
+
+	while (!kthread_should_stop()) {
+		try_to_freeze();
+		err = kernel_sock_ioctl(sock, TIOCINQ, (unsigned long) &avail);
+		if (err !=0 || avail != 0)
+			break;
+		msleep(1);
+	}
+
+	return (err >= 0) ? avail : err;
+}
+
 static int
 cifs_demultiplex_thread(struct TCP_Server_Info *server)
 {
@@ -396,11 +414,16 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
 		iov.iov_len = 4;
 		smb_msg.msg_control = NULL;
 		smb_msg.msg_controllen = 0;
+		smb_msg.msg_flags = MSG_DONTWAIT;
 		pdu_length = 4; /* enough to get RFC1001 header */
 incomplete_rcv:
+		cifs_wait_for_recv(csocket);
+		if (kthread_should_stop())
+			break;
+
 		length =
 		    kernel_recvmsg(csocket, &smb_msg,
-				&iov, 1, pdu_length, 0 /* BB other flags? */);
+				&iov, 1, pdu_length, smb_msg.msg_flags);
 
 		if (kthread_should_stop()) {
 			break;
@@ -410,7 +433,9 @@ incomplete_rcv:
 			cFYI(1, ("call to reconnect done"));
 			csocket = server->ssocket;
 			continue;
-		} else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) {
+		} 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 */
@@ -427,10 +452,6 @@ incomplete_rcv:
 				   and so simply return error to mount */
 				break;
 			}
-			if (!try_to_freeze() && (length == -EINTR)) {
-				cFYI(1, ("cifsd thread killed"));
-				break;
-			}
 			cFYI(1, ("Reconnect after unexpected peek error %d",
 				length));
 			cifs_reconnect(server);
@@ -525,10 +546,15 @@ incomplete_rcv:
 		iov.iov_len = pdu_length;
 		for (total_read = 0; total_read < pdu_length;
 		     total_read += length) {
+			cifs_wait_for_recv(csocket);
+			if (kthread_should_stop()) {
+				reconnect = 2;
+				break;
+			}
 			length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
-						pdu_length - total_read, 0);
-			if (kthread_should_stop() ||
-			    (length == -EINTR)) {
+						pdu_length - total_read,
+						smb_msg.msg_flags);
+			if (kthread_should_stop()) {
 				/* then will exit */
 				reconnect = 2;
 				break;
@@ -540,7 +566,8 @@ incomplete_rcv:
 				reconnect = 1;
 				break;
 			} else if ((length == -ERESTARTSYS) ||
-				   (length == -EAGAIN)) {
+				   (length == -EAGAIN) ||
+				   (length == -EINTR)) {
 				msleep(1); /* minimum sleep to prevent looping,
 					      allowing socket to clear and app
 					      threads to set tcpStatus
@@ -2194,14 +2221,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 			spin_lock(&GlobalMid_Lock);
 			srvTcp->tcpStatus = CifsExiting;
 			spin_unlock(&GlobalMid_Lock);
-			if (srvTcp->tsk) {
-				/* If we could verify that kthread_stop would
-				   always wake up processes blocked in
-				   tcp in recv_mesg then we could remove the
-				   send_sig call */
-				force_sig(SIGKILL, srvTcp->tsk);
+			if (srvTcp->tsk)
 				kthread_stop(srvTcp->tsk);
-			}
 		}
 		 /* If find_unc succeeded then rc == 0 so we can not end */
 		if (tcon)  /* up accidently freeing someone elses tcon struct */
@@ -2215,19 +2236,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 					/* if the socketUseCount is now zero */
 					if ((temp_rc == -ESHUTDOWN) &&
 					    (pSesInfo->server) &&
-					    (pSesInfo->server->tsk)) {
-						force_sig(SIGKILL,
-							pSesInfo->server->tsk);
+					    (pSesInfo->server->tsk))
 						kthread_stop(pSesInfo->server->tsk);
-					}
 				} else {
 					cFYI(1, ("No session or bad tcon"));
 					if ((pSesInfo->server) &&
-					    (pSesInfo->server->tsk)) {
-						force_sig(SIGKILL,
-							pSesInfo->server->tsk);
+					    (pSesInfo->server->tsk))
 						kthread_stop(pSesInfo->server->tsk);
-					}
 				}
 				sesInfoFree(pSesInfo);
 				/* pSesInfo = NULL; */
@@ -3512,7 +3527,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
 	int rc = 0;
 	int xid;
 	struct cifsSesInfo *ses = NULL;
-	struct task_struct *cifsd_task;
 	char *tmp;
 
 	xid = GetXid();
@@ -3527,19 +3541,14 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
 		DeleteTconOplockQEntries(cifs_sb->tcon);
 		tconInfoFree(cifs_sb->tcon);
 		if ((ses) && (ses->server)) {
-			/* save off task so we do not refer to ses later */
-			cifsd_task = ses->server->tsk;
 			cFYI(1, ("About to do SMBLogoff "));
 			rc = CIFSSMBLogoff(xid, ses);
 			if (rc == -EBUSY) {
 				FreeXid(xid);
 				return 0;
 			} else if (rc == -ESHUTDOWN) {
-				cFYI(1, ("Waking up socket by sending signal"));
-				if (cifsd_task) {
-					force_sig(SIGKILL, cifsd_task);
-					kthread_stop(cifsd_task);
-				}
+				if (ses->server->tsk)
+					kthread_stop(ses->server->tsk);
 				rc = 0;
 			} /* else - we have an smb session
 				left on this socket do not kill cifsd */
-- 
1.5.3.6



More information about the linux-cifs-client mailing list