[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