Commit c17acb1f authored by Urban Widmark's avatar Urban Widmark Committed by Linus Torvalds

[PATCH] might_sleep fixes

+ Fixes 2 cases caught by might_sleep testing.
+ Replace sleep_on with wait_event.
+ MOD_INC_USE_COUNT to prevent module unload vs smbiod thread exit race.
parent 77728a1c
...@@ -449,10 +449,9 @@ smb_put_super(struct super_block *sb) ...@@ -449,10 +449,9 @@ smb_put_super(struct super_block *sb)
{ {
struct smb_sb_info *server = SMB_SB(sb); struct smb_sb_info *server = SMB_SB(sb);
smbiod_unregister_server(server);
smb_lock_server(server); smb_lock_server(server);
server->state = CONN_INVALID; server->state = CONN_INVALID;
smbiod_unregister_server(server);
smb_close_socket(server); smb_close_socket(server);
......
...@@ -1127,6 +1127,7 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) ...@@ -1127,6 +1127,7 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n", VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n",
DENTRY_PATH(dentry), res); DENTRY_PATH(dentry), res);
mode = read_only; mode = read_only;
req->rq_flags = 0;
goto retry; goto retry;
} }
goto out_free; goto out_free;
...@@ -1701,6 +1702,7 @@ smb_proc_unlink(struct dentry *dentry) ...@@ -1701,6 +1702,7 @@ smb_proc_unlink(struct dentry *dentry)
result = smb_set_rw(dentry,server); result = smb_set_rw(dentry,server);
if (result == 0) { if (result == 0) {
flag = 1; flag = 1;
req->rq_flags = 0;
goto retry; goto retry;
} }
} }
......
...@@ -332,8 +332,8 @@ int smb_add_request(struct smb_request *req) ...@@ -332,8 +332,8 @@ int smb_add_request(struct smb_request *req)
smbiod_wake_up(); smbiod_wake_up();
/* FIXME: replace with a timeout-able wake_event_interruptible */ timeleft = wait_event_interruptible_timeout(req->rq_wait,
timeleft = interruptible_sleep_on_timeout(&req->rq_wait, 30*HZ); req->rq_flags & SMB_REQ_RECEIVED, 30*HZ);
if (!timeleft || signal_pending(current)) { if (!timeleft || signal_pending(current)) {
/* /*
* On timeout or on interrupt we want to try and remove the * On timeout or on interrupt we want to try and remove the
...@@ -777,7 +777,7 @@ int smb_request_recv(struct smb_sb_info *server) ...@@ -777,7 +777,7 @@ int smb_request_recv(struct smb_sb_info *server)
/* /*
* Response completely read. Drop any extra bytes sent by the server. * Response completely read. Drop any extra bytes sent by the server.
* (Yes, servers sometimes add extra bytes to requests) * (Yes, servers sometimes add extra bytes to responses)
*/ */
VERBOSE("smb_len: %d smb_read: %d\n", VERBOSE("smb_len: %d smb_read: %d\n",
server->smb_len, server->smb_read); server->smb_len, server->smb_read);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/file.h> #include <linux/file.h>
#include <linux/dcache.h> #include <linux/dcache.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/module.h>
#include <net/ip.h> #include <net/ip.h>
#include <linux/smb_fs.h> #include <linux/smb_fs.h>
...@@ -31,7 +32,14 @@ ...@@ -31,7 +32,14 @@
#include "request.h" #include "request.h"
#include "proto.h" #include "proto.h"
static int smbiod_pid = -1; enum smbiod_state {
SMBIOD_DEAD,
SMBIOD_STARTING,
SMBIOD_RUNNING,
};
static enum smbiod_state smbiod_state = SMBIOD_DEAD;
static pid_t smbiod_pid;
static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait); static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait);
static LIST_HEAD(smb_servers); static LIST_HEAD(smb_servers);
static spinlock_t servers_lock = SPIN_LOCK_UNLOCKED; static spinlock_t servers_lock = SPIN_LOCK_UNLOCKED;
...@@ -41,14 +49,13 @@ static long smbiod_flags; ...@@ -41,14 +49,13 @@ static long smbiod_flags;
static int smbiod(void *); static int smbiod(void *);
static void smbiod_start(void); static void smbiod_start(void);
static void smbiod_stop(void);
/* /*
* called when there's work for us to do * called when there's work for us to do
*/ */
void smbiod_wake_up() void smbiod_wake_up()
{ {
if (smbiod_pid == -1) if (smbiod_state == SMBIOD_DEAD)
return; return;
set_bit(SMBIOD_DATA_READY, &smbiod_flags); set_bit(SMBIOD_DATA_READY, &smbiod_flags);
wake_up_interruptible(&smbiod_wait); wake_up_interruptible(&smbiod_wait);
...@@ -59,18 +66,16 @@ void smbiod_wake_up() ...@@ -59,18 +66,16 @@ void smbiod_wake_up()
*/ */
static void smbiod_start() static void smbiod_start()
{ {
if (smbiod_pid != -1) pid_t pid;
if (smbiod_state != SMBIOD_DEAD)
return; return;
smbiod_pid = kernel_thread(smbiod, NULL, 0); smbiod_state = SMBIOD_STARTING;
} spin_unlock(&servers_lock);
pid = kernel_thread(smbiod, NULL, 0);
/* spin_lock(&servers_lock);
* stop smbiod if there are no open connections smbiod_state = SMBIOD_RUNNING;
*/ smbiod_pid = pid;
static void smbiod_stop()
{
if (smbiod_pid != -1 && list_empty(&smb_servers))
kill_proc(smbiod_pid, SIGKILL, 1);
} }
/* /*
...@@ -86,19 +91,18 @@ void smbiod_register_server(struct smb_sb_info *server) ...@@ -86,19 +91,18 @@ void smbiod_register_server(struct smb_sb_info *server)
} }
/* /*
* unregister a server & stop smbiod if necessary * Unregister a server
* Must be called with the server lock held.
*/ */
void smbiod_unregister_server(struct smb_sb_info *server) void smbiod_unregister_server(struct smb_sb_info *server)
{ {
spin_lock(&servers_lock); spin_lock(&servers_lock);
list_del_init(&server->entry); list_del_init(&server->entry);
VERBOSE("%p\n", server); VERBOSE("%p\n", server);
smbiod_stop();
spin_unlock(&servers_lock); spin_unlock(&servers_lock);
smb_lock_server(server); smbiod_wake_up();
smbiod_flush(server); smbiod_flush(server);
smb_unlock_server(server);
} }
void smbiod_flush(struct smb_sb_info *server) void smbiod_flush(struct smb_sb_info *server)
...@@ -277,6 +281,7 @@ static void smbiod_doio(struct smb_sb_info *server) ...@@ -277,6 +281,7 @@ static void smbiod_doio(struct smb_sb_info *server)
*/ */
static int smbiod(void *unused) static int smbiod(void *unused)
{ {
MOD_INC_USE_COUNT;
daemonize(); daemonize();
spin_lock_irq(&current->sig->siglock); spin_lock_irq(&current->sig->siglock);
...@@ -295,32 +300,40 @@ static int smbiod(void *unused) ...@@ -295,32 +300,40 @@ static int smbiod(void *unused)
/* FIXME: Use poll? */ /* FIXME: Use poll? */
wait_event_interruptible(smbiod_wait, wait_event_interruptible(smbiod_wait,
test_bit(SMBIOD_DATA_READY, &smbiod_flags)); test_bit(SMBIOD_DATA_READY, &smbiod_flags));
if (signal_pending(current)) if (signal_pending(current)) {
spin_lock(&servers_lock);
smbiod_state = SMBIOD_DEAD;
spin_unlock(&servers_lock);
break; break;
}
clear_bit(SMBIOD_DATA_READY, &smbiod_flags); clear_bit(SMBIOD_DATA_READY, &smbiod_flags);
/*
* We must hold the servers_lock while looking for servers
* to check or else we have a race with put_super.
*/
spin_lock(&servers_lock); spin_lock(&servers_lock);
if (list_empty(&smb_servers)) {
smbiod_state = SMBIOD_DEAD;
spin_unlock(&servers_lock);
break;
}
list_for_each_safe(pos, n, &smb_servers) { list_for_each_safe(pos, n, &smb_servers) {
server = list_entry(pos, struct smb_sb_info, entry); server = list_entry(pos, struct smb_sb_info, entry);
VERBOSE("checking server %p\n", server); VERBOSE("checking server %p\n", server);
smb_lock_server(server);
if (server->state == CONN_VALID) {
spin_unlock(&servers_lock); spin_unlock(&servers_lock);
smb_lock_server(server);
smbiod_doio(server); smbiod_doio(server);
smb_unlock_server(server); smb_unlock_server(server);
spin_lock(&servers_lock); spin_lock(&servers_lock);
} }
}
spin_unlock(&servers_lock); spin_unlock(&servers_lock);
} }
VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid); VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid);
smbiod_pid = -1; MOD_DEC_USE_COUNT;
return 0; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment