diff options
author | Neil Horman <nhorman@tuxdriver.com> | 2007-06-18 22:33:20 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-06-18 22:33:20 -0700 |
commit | cc0191aeef73e5aa0c7f9a586d4bb27ed67facbb (patch) | |
tree | c432069053def48651313d2d1f0d206159d8f667 /net | |
parent | 281216177a407f78cfd650ee4391afc487577193 (diff) | |
download | op-kernel-dev-cc0191aeef73e5aa0c7f9a586d4bb27ed67facbb.zip op-kernel-dev-cc0191aeef73e5aa0c7f9a586d4bb27ed67facbb.tar.gz |
[IPVS]: Fix state variable on failure to start ipvs threads
ip_vs currently fails to reset its ip_vs_sync_state variable if the
sync thread fails to start properly. The result is that the kernel
will report a running daemon when their actuall is none.
If you issue the following commands:
1. ipvsadm --start-daemon master --mcast-interface bla
2. ipvsadm -L --daemon
3. ipvsadm --stop-daemon master
Assuming that bla is not an actual interface, step 2 should return no
data, but instead returns:
$ ipvsadm -L --daemon
master sync daemon (mcast=bla, syncid=0)
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/ipvs/ip_vs_sync.c | 41 |
1 files changed, 39 insertions, 2 deletions
diff --git a/net/ipv4/ipvs/ip_vs_sync.c b/net/ipv4/ipvs/ip_vs_sync.c index 7ea2d98..356f067 100644 --- a/net/ipv4/ipvs/ip_vs_sync.c +++ b/net/ipv4/ipvs/ip_vs_sync.c @@ -67,6 +67,11 @@ struct ip_vs_sync_conn_options { struct ip_vs_seq out_seq; /* outgoing seq. struct */ }; +struct ip_vs_sync_thread_data { + struct completion *startup; + int state; +}; + #define IP_VS_SYNC_CONN_TIMEOUT (3*60*HZ) #define SIMPLE_CONN_SIZE (sizeof(struct ip_vs_sync_conn)) #define FULL_CONN_SIZE \ @@ -751,6 +756,7 @@ static int sync_thread(void *startup) mm_segment_t oldmm; int state; const char *name; + struct ip_vs_sync_thread_data *tinfo = startup; /* increase the module use count */ ip_vs_use_count_inc(); @@ -789,7 +795,14 @@ static int sync_thread(void *startup) add_wait_queue(&sync_wait, &wait); set_sync_pid(state, current->pid); - complete((struct completion *)startup); + complete(tinfo->startup); + + /* + * once we call the completion queue above, we should + * null out that reference, since its allocated on the + * stack of the creating kernel thread + */ + tinfo->startup = NULL; /* processing master/backup loop here */ if (state == IP_VS_STATE_MASTER) @@ -801,6 +814,14 @@ static int sync_thread(void *startup) remove_wait_queue(&sync_wait, &wait); /* thread exits */ + + /* + * If we weren't explicitly stopped, then we + * exited in error, and should undo our state + */ + if ((!stop_master_sync) && (!stop_backup_sync)) + ip_vs_sync_state -= tinfo->state; + set_sync_pid(state, 0); IP_VS_INFO("sync thread stopped!\n"); @@ -812,6 +833,11 @@ static int sync_thread(void *startup) set_stop_sync(state, 0); wake_up(&stop_sync_wait); + /* + * we need to free the structure that was allocated + * for us in start_sync_thread + */ + kfree(tinfo); return 0; } @@ -838,11 +864,19 @@ int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) { DECLARE_COMPLETION_ONSTACK(startup); pid_t pid; + struct ip_vs_sync_thread_data *tinfo; if ((state == IP_VS_STATE_MASTER && sync_master_pid) || (state == IP_VS_STATE_BACKUP && sync_backup_pid)) return -EEXIST; + /* + * Note that tinfo will be freed in sync_thread on exit + */ + tinfo = kmalloc(sizeof(struct ip_vs_sync_thread_data), GFP_KERNEL); + if (!tinfo) + return -ENOMEM; + IP_VS_DBG(7, "%s: pid %d\n", __FUNCTION__, current->pid); IP_VS_DBG(7, "Each ip_vs_sync_conn entry need %Zd bytes\n", sizeof(struct ip_vs_sync_conn)); @@ -858,8 +892,11 @@ int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) ip_vs_backup_syncid = syncid; } + tinfo->state = state; + tinfo->startup = &startup; + repeat: - if ((pid = kernel_thread(fork_sync_thread, &startup, 0)) < 0) { + if ((pid = kernel_thread(fork_sync_thread, tinfo, 0)) < 0) { IP_VS_ERR("could not create fork_sync_thread due to %d... " "retrying.\n", pid); msleep_interruptible(1000); |