diff options
author | Neil Horman <nhorman@tuxdriver.com> | 2007-06-19 01:33:20 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-06-19 01:33:20 -0400 |
commit | cc0191aeef73e5aa0c7f9a586d4bb27ed67facbb (patch) | |
tree | c432069053def48651313d2d1f0d206159d8f667 | |
parent | 281216177a407f78cfd650ee4391afc487577193 (diff) |
[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>
-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 7ea2d981a932..356f067484e3 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 { | |||
67 | struct ip_vs_seq out_seq; /* outgoing seq. struct */ | 67 | struct ip_vs_seq out_seq; /* outgoing seq. struct */ |
68 | }; | 68 | }; |
69 | 69 | ||
70 | struct ip_vs_sync_thread_data { | ||
71 | struct completion *startup; | ||
72 | int state; | ||
73 | }; | ||
74 | |||
70 | #define IP_VS_SYNC_CONN_TIMEOUT (3*60*HZ) | 75 | #define IP_VS_SYNC_CONN_TIMEOUT (3*60*HZ) |
71 | #define SIMPLE_CONN_SIZE (sizeof(struct ip_vs_sync_conn)) | 76 | #define SIMPLE_CONN_SIZE (sizeof(struct ip_vs_sync_conn)) |
72 | #define FULL_CONN_SIZE \ | 77 | #define FULL_CONN_SIZE \ |
@@ -751,6 +756,7 @@ static int sync_thread(void *startup) | |||
751 | mm_segment_t oldmm; | 756 | mm_segment_t oldmm; |
752 | int state; | 757 | int state; |
753 | const char *name; | 758 | const char *name; |
759 | struct ip_vs_sync_thread_data *tinfo = startup; | ||
754 | 760 | ||
755 | /* increase the module use count */ | 761 | /* increase the module use count */ |
756 | ip_vs_use_count_inc(); | 762 | ip_vs_use_count_inc(); |
@@ -789,7 +795,14 @@ static int sync_thread(void *startup) | |||
789 | add_wait_queue(&sync_wait, &wait); | 795 | add_wait_queue(&sync_wait, &wait); |
790 | 796 | ||
791 | set_sync_pid(state, current->pid); | 797 | set_sync_pid(state, current->pid); |
792 | complete((struct completion *)startup); | 798 | complete(tinfo->startup); |
799 | |||
800 | /* | ||
801 | * once we call the completion queue above, we should | ||
802 | * null out that reference, since its allocated on the | ||
803 | * stack of the creating kernel thread | ||
804 | */ | ||
805 | tinfo->startup = NULL; | ||
793 | 806 | ||
794 | /* processing master/backup loop here */ | 807 | /* processing master/backup loop here */ |
795 | if (state == IP_VS_STATE_MASTER) | 808 | if (state == IP_VS_STATE_MASTER) |
@@ -801,6 +814,14 @@ static int sync_thread(void *startup) | |||
801 | remove_wait_queue(&sync_wait, &wait); | 814 | remove_wait_queue(&sync_wait, &wait); |
802 | 815 | ||
803 | /* thread exits */ | 816 | /* thread exits */ |
817 | |||
818 | /* | ||
819 | * If we weren't explicitly stopped, then we | ||
820 | * exited in error, and should undo our state | ||
821 | */ | ||
822 | if ((!stop_master_sync) && (!stop_backup_sync)) | ||
823 | ip_vs_sync_state -= tinfo->state; | ||
824 | |||
804 | set_sync_pid(state, 0); | 825 | set_sync_pid(state, 0); |
805 | IP_VS_INFO("sync thread stopped!\n"); | 826 | IP_VS_INFO("sync thread stopped!\n"); |
806 | 827 | ||
@@ -812,6 +833,11 @@ static int sync_thread(void *startup) | |||
812 | set_stop_sync(state, 0); | 833 | set_stop_sync(state, 0); |
813 | wake_up(&stop_sync_wait); | 834 | wake_up(&stop_sync_wait); |
814 | 835 | ||
836 | /* | ||
837 | * we need to free the structure that was allocated | ||
838 | * for us in start_sync_thread | ||
839 | */ | ||
840 | kfree(tinfo); | ||
815 | return 0; | 841 | return 0; |
816 | } | 842 | } |
817 | 843 | ||
@@ -838,11 +864,19 @@ int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) | |||
838 | { | 864 | { |
839 | DECLARE_COMPLETION_ONSTACK(startup); | 865 | DECLARE_COMPLETION_ONSTACK(startup); |
840 | pid_t pid; | 866 | pid_t pid; |
867 | struct ip_vs_sync_thread_data *tinfo; | ||
841 | 868 | ||
842 | if ((state == IP_VS_STATE_MASTER && sync_master_pid) || | 869 | if ((state == IP_VS_STATE_MASTER && sync_master_pid) || |
843 | (state == IP_VS_STATE_BACKUP && sync_backup_pid)) | 870 | (state == IP_VS_STATE_BACKUP && sync_backup_pid)) |
844 | return -EEXIST; | 871 | return -EEXIST; |
845 | 872 | ||
873 | /* | ||
874 | * Note that tinfo will be freed in sync_thread on exit | ||
875 | */ | ||
876 | tinfo = kmalloc(sizeof(struct ip_vs_sync_thread_data), GFP_KERNEL); | ||
877 | if (!tinfo) | ||
878 | return -ENOMEM; | ||
879 | |||
846 | IP_VS_DBG(7, "%s: pid %d\n", __FUNCTION__, current->pid); | 880 | IP_VS_DBG(7, "%s: pid %d\n", __FUNCTION__, current->pid); |
847 | IP_VS_DBG(7, "Each ip_vs_sync_conn entry need %Zd bytes\n", | 881 | IP_VS_DBG(7, "Each ip_vs_sync_conn entry need %Zd bytes\n", |
848 | sizeof(struct ip_vs_sync_conn)); | 882 | sizeof(struct ip_vs_sync_conn)); |
@@ -858,8 +892,11 @@ int start_sync_thread(int state, char *mcast_ifn, __u8 syncid) | |||
858 | ip_vs_backup_syncid = syncid; | 892 | ip_vs_backup_syncid = syncid; |
859 | } | 893 | } |
860 | 894 | ||
895 | tinfo->state = state; | ||
896 | tinfo->startup = &startup; | ||
897 | |||
861 | repeat: | 898 | repeat: |
862 | if ((pid = kernel_thread(fork_sync_thread, &startup, 0)) < 0) { | 899 | if ((pid = kernel_thread(fork_sync_thread, tinfo, 0)) < 0) { |
863 | IP_VS_ERR("could not create fork_sync_thread due to %d... " | 900 | IP_VS_ERR("could not create fork_sync_thread due to %d... " |
864 | "retrying.\n", pid); | 901 | "retrying.\n", pid); |
865 | msleep_interruptible(1000); | 902 | msleep_interruptible(1000); |