Skip to content

Commit

Permalink
[GFS2] GFS2: chmod hung - fix race in thread creation
Browse files Browse the repository at this point in the history
The problem boiled down to a race between the gdlm_init_threads()
function initializing thread1 and its setting of blist = 1.
Essentially, "if (current == ls->thread1)" was checked by the thread
before the thread creator set ls->thread1.

Since thread1 is the only thread who is allowed to work on the
blocking queue, and since neither thread thought it was thread1, no one
was working on the queue.  So everything just sat.

This patch reuses the ls->async_lock spin_lock to fix the race,
and it fixes the problem.  I've done more than 2000 iterations of the
loop that was recreating the failure and it seems to work.

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>

--
  • Loading branch information
Bob Peterson authored and Steven Whitehouse committed Oct 10, 2007
1 parent d66f827 commit 55c0c4a
Showing 1 changed file with 13 additions and 7 deletions.
20 changes: 13 additions & 7 deletions fs/gfs2/locking/dlm/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,20 +268,16 @@ static inline int check_drop(struct gdlm_ls *ls)
return 0;
}

static int gdlm_thread(void *data)
static int gdlm_thread(void *data, int blist)
{
struct gdlm_ls *ls = (struct gdlm_ls *) data;
struct gdlm_lock *lp = NULL;
int blist = 0;
uint8_t complete, blocking, submit, drop;
DECLARE_WAITQUEUE(wait, current);

/* Only thread1 is allowed to do blocking callbacks since gfs
may wait for a completion callback within a blocking cb. */

if (current == ls->thread1)
blist = 1;

while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&ls->thread_wait, &wait);
Expand Down Expand Up @@ -333,20 +329,30 @@ static int gdlm_thread(void *data)
return 0;
}

static int gdlm_thread1(void *data)
{
return gdlm_thread(data, 1);
}

static int gdlm_thread2(void *data)
{
return gdlm_thread(data, 0);
}

int gdlm_init_threads(struct gdlm_ls *ls)
{
struct task_struct *p;
int error;

p = kthread_run(gdlm_thread, ls, "lock_dlm1");
p = kthread_run(gdlm_thread1, ls, "lock_dlm1");
error = IS_ERR(p);
if (error) {
log_error("can't start lock_dlm1 thread %d", error);
return error;
}
ls->thread1 = p;

p = kthread_run(gdlm_thread, ls, "lock_dlm2");
p = kthread_run(gdlm_thread2, ls, "lock_dlm2");
error = IS_ERR(p);
if (error) {
log_error("can't start lock_dlm2 thread %d", error);
Expand Down

0 comments on commit 55c0c4a

Please sign in to comment.