diff options
author | Julian Anastasov <ja@ssi.bg> | 2010-09-21 12:12:30 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-09-21 12:12:30 -0400 |
commit | 26c15cfd291f8b4ee40b4bbdf5e3772adfd704f5 (patch) | |
tree | 7ac3dc5c55b30ab85d599e945abd5f718a97c3c3 /net/netfilter | |
parent | 99f07e91bef34db0fc8b1a224096e97f02dc0d56 (diff) |
ipvs: changes related to service usecnt
Change the usage of svc usecnt during command execution:
- we check if svc is registered but we do not need to hold usecnt
reference while under __ip_vs_mutex, only the packet handling needs
it during scheduling
- change __ip_vs_service_get to __ip_vs_service_find and
__ip_vs_svc_fwm_get to __ip_vs_svc_fwm_find because now caller
will increase svc->usecnt
- put common code that calls update_service in __ip_vs_update_dest
- put common code in ip_vs_unlink_service() and use it to unregister
the service
- add comment that svc should not be accessed after ip_vs_del_service
anymore
- all IP_VS_WAIT_WHILE calls are now unified: usecnt > 0
- Properly log the app ports
As result, some problems are fixed:
- possible use-after-free of svc in ip_vs_genl_set_cmd after
ip_vs_del_service because our usecnt reference does not guarantee that
svc is not freed on refcnt==0, eg. when no dests are moved to trash
- possible usecnt leak in do_ip_vs_set_ctl after ip_vs_del_service
when the service is not freed now, for example, when some
destionations are moved into trash and svc->refcnt remains above 0.
It is harmless because svc is not in hash anymore.
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Acked-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/ipvs/ip_vs_app.c | 6 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_ctl.c | 250 |
2 files changed, 102 insertions, 154 deletions
diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index e76f87f4aca..a475edee091 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c | |||
@@ -103,8 +103,8 @@ ip_vs_app_inc_new(struct ip_vs_app *app, __u16 proto, __u16 port) | |||
103 | goto out; | 103 | goto out; |
104 | 104 | ||
105 | list_add(&inc->a_list, &app->incs_list); | 105 | list_add(&inc->a_list, &app->incs_list); |
106 | IP_VS_DBG(9, "%s application %s:%u registered\n", | 106 | IP_VS_DBG(9, "%s App %s:%u registered\n", |
107 | pp->name, inc->name, inc->port); | 107 | pp->name, inc->name, ntohs(inc->port)); |
108 | 108 | ||
109 | return 0; | 109 | return 0; |
110 | 110 | ||
@@ -130,7 +130,7 @@ ip_vs_app_inc_release(struct ip_vs_app *inc) | |||
130 | pp->unregister_app(inc); | 130 | pp->unregister_app(inc); |
131 | 131 | ||
132 | IP_VS_DBG(9, "%s App %s:%u unregistered\n", | 132 | IP_VS_DBG(9, "%s App %s:%u unregistered\n", |
133 | pp->name, inc->name, inc->port); | 133 | pp->name, inc->name, ntohs(inc->port)); |
134 | 134 | ||
135 | list_del(&inc->a_list); | 135 | list_del(&inc->a_list); |
136 | 136 | ||
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index e637cd0384b..e4ec8f364f8 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c | |||
@@ -405,7 +405,7 @@ static int ip_vs_svc_unhash(struct ip_vs_service *svc) | |||
405 | * Get service by {proto,addr,port} in the service table. | 405 | * Get service by {proto,addr,port} in the service table. |
406 | */ | 406 | */ |
407 | static inline struct ip_vs_service * | 407 | static inline struct ip_vs_service * |
408 | __ip_vs_service_get(int af, __u16 protocol, const union nf_inet_addr *vaddr, | 408 | __ip_vs_service_find(int af, __u16 protocol, const union nf_inet_addr *vaddr, |
409 | __be16 vport) | 409 | __be16 vport) |
410 | { | 410 | { |
411 | unsigned hash; | 411 | unsigned hash; |
@@ -420,7 +420,6 @@ __ip_vs_service_get(int af, __u16 protocol, const union nf_inet_addr *vaddr, | |||
420 | && (svc->port == vport) | 420 | && (svc->port == vport) |
421 | && (svc->protocol == protocol)) { | 421 | && (svc->protocol == protocol)) { |
422 | /* HIT */ | 422 | /* HIT */ |
423 | atomic_inc(&svc->usecnt); | ||
424 | return svc; | 423 | return svc; |
425 | } | 424 | } |
426 | } | 425 | } |
@@ -433,7 +432,7 @@ __ip_vs_service_get(int af, __u16 protocol, const union nf_inet_addr *vaddr, | |||
433 | * Get service by {fwmark} in the service table. | 432 | * Get service by {fwmark} in the service table. |
434 | */ | 433 | */ |
435 | static inline struct ip_vs_service * | 434 | static inline struct ip_vs_service * |
436 | __ip_vs_svc_fwm_get(int af, __u32 fwmark) | 435 | __ip_vs_svc_fwm_find(int af, __u32 fwmark) |
437 | { | 436 | { |
438 | unsigned hash; | 437 | unsigned hash; |
439 | struct ip_vs_service *svc; | 438 | struct ip_vs_service *svc; |
@@ -444,7 +443,6 @@ __ip_vs_svc_fwm_get(int af, __u32 fwmark) | |||
444 | list_for_each_entry(svc, &ip_vs_svc_fwm_table[hash], f_list) { | 443 | list_for_each_entry(svc, &ip_vs_svc_fwm_table[hash], f_list) { |
445 | if (svc->fwmark == fwmark && svc->af == af) { | 444 | if (svc->fwmark == fwmark && svc->af == af) { |
446 | /* HIT */ | 445 | /* HIT */ |
447 | atomic_inc(&svc->usecnt); | ||
448 | return svc; | 446 | return svc; |
449 | } | 447 | } |
450 | } | 448 | } |
@@ -463,14 +461,14 @@ ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, | |||
463 | /* | 461 | /* |
464 | * Check the table hashed by fwmark first | 462 | * Check the table hashed by fwmark first |
465 | */ | 463 | */ |
466 | if (fwmark && (svc = __ip_vs_svc_fwm_get(af, fwmark))) | 464 | if (fwmark && (svc = __ip_vs_svc_fwm_find(af, fwmark))) |
467 | goto out; | 465 | goto out; |
468 | 466 | ||
469 | /* | 467 | /* |
470 | * Check the table hashed by <protocol,addr,port> | 468 | * Check the table hashed by <protocol,addr,port> |
471 | * for "full" addressed entries | 469 | * for "full" addressed entries |
472 | */ | 470 | */ |
473 | svc = __ip_vs_service_get(af, protocol, vaddr, vport); | 471 | svc = __ip_vs_service_find(af, protocol, vaddr, vport); |
474 | 472 | ||
475 | if (svc == NULL | 473 | if (svc == NULL |
476 | && protocol == IPPROTO_TCP | 474 | && protocol == IPPROTO_TCP |
@@ -480,7 +478,7 @@ ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, | |||
480 | * Check if ftp service entry exists, the packet | 478 | * Check if ftp service entry exists, the packet |
481 | * might belong to FTP data connections. | 479 | * might belong to FTP data connections. |
482 | */ | 480 | */ |
483 | svc = __ip_vs_service_get(af, protocol, vaddr, FTPPORT); | 481 | svc = __ip_vs_service_find(af, protocol, vaddr, FTPPORT); |
484 | } | 482 | } |
485 | 483 | ||
486 | if (svc == NULL | 484 | if (svc == NULL |
@@ -488,10 +486,12 @@ ip_vs_service_get(int af, __u32 fwmark, __u16 protocol, | |||
488 | /* | 486 | /* |
489 | * Check if the catch-all port (port zero) exists | 487 | * Check if the catch-all port (port zero) exists |
490 | */ | 488 | */ |
491 | svc = __ip_vs_service_get(af, protocol, vaddr, 0); | 489 | svc = __ip_vs_service_find(af, protocol, vaddr, 0); |
492 | } | 490 | } |
493 | 491 | ||
494 | out: | 492 | out: |
493 | if (svc) | ||
494 | atomic_inc(&svc->usecnt); | ||
495 | read_unlock(&__ip_vs_svc_lock); | 495 | read_unlock(&__ip_vs_svc_lock); |
496 | 496 | ||
497 | IP_VS_DBG_BUF(9, "lookup service: fwm %u %s %s:%u %s\n", | 497 | IP_VS_DBG_BUF(9, "lookup service: fwm %u %s %s:%u %s\n", |
@@ -510,14 +510,19 @@ __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc) | |||
510 | dest->svc = svc; | 510 | dest->svc = svc; |
511 | } | 511 | } |
512 | 512 | ||
513 | static inline void | 513 | static void |
514 | __ip_vs_unbind_svc(struct ip_vs_dest *dest) | 514 | __ip_vs_unbind_svc(struct ip_vs_dest *dest) |
515 | { | 515 | { |
516 | struct ip_vs_service *svc = dest->svc; | 516 | struct ip_vs_service *svc = dest->svc; |
517 | 517 | ||
518 | dest->svc = NULL; | 518 | dest->svc = NULL; |
519 | if (atomic_dec_and_test(&svc->refcnt)) | 519 | if (atomic_dec_and_test(&svc->refcnt)) { |
520 | IP_VS_DBG_BUF(3, "Removing service %u/%s:%u usecnt=%d\n", | ||
521 | svc->fwmark, | ||
522 | IP_VS_DBG_ADDR(svc->af, &svc->addr), | ||
523 | ntohs(svc->port), atomic_read(&svc->usecnt)); | ||
520 | kfree(svc); | 524 | kfree(svc); |
525 | } | ||
521 | } | 526 | } |
522 | 527 | ||
523 | 528 | ||
@@ -762,8 +767,8 @@ ip_vs_zero_stats(struct ip_vs_stats *stats) | |||
762 | * Update a destination in the given service | 767 | * Update a destination in the given service |
763 | */ | 768 | */ |
764 | static void | 769 | static void |
765 | __ip_vs_update_dest(struct ip_vs_service *svc, | 770 | __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, |
766 | struct ip_vs_dest *dest, struct ip_vs_dest_user_kern *udest) | 771 | struct ip_vs_dest_user_kern *udest, int add) |
767 | { | 772 | { |
768 | int conn_flags; | 773 | int conn_flags; |
769 | 774 | ||
@@ -818,6 +823,25 @@ __ip_vs_update_dest(struct ip_vs_service *svc, | |||
818 | dest->flags &= ~IP_VS_DEST_F_OVERLOAD; | 823 | dest->flags &= ~IP_VS_DEST_F_OVERLOAD; |
819 | dest->u_threshold = udest->u_threshold; | 824 | dest->u_threshold = udest->u_threshold; |
820 | dest->l_threshold = udest->l_threshold; | 825 | dest->l_threshold = udest->l_threshold; |
826 | |||
827 | if (add) | ||
828 | ip_vs_new_estimator(&dest->stats); | ||
829 | |||
830 | write_lock_bh(&__ip_vs_svc_lock); | ||
831 | |||
832 | /* Wait until all other svc users go away */ | ||
833 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); | ||
834 | |||
835 | if (add) { | ||
836 | list_add(&dest->n_list, &svc->destinations); | ||
837 | svc->num_dests++; | ||
838 | } | ||
839 | |||
840 | /* call the update_service, because server weight may be changed */ | ||
841 | if (svc->scheduler->update_service) | ||
842 | svc->scheduler->update_service(svc); | ||
843 | |||
844 | write_unlock_bh(&__ip_vs_svc_lock); | ||
821 | } | 845 | } |
822 | 846 | ||
823 | 847 | ||
@@ -865,13 +889,12 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, | |||
865 | atomic_set(&dest->activeconns, 0); | 889 | atomic_set(&dest->activeconns, 0); |
866 | atomic_set(&dest->inactconns, 0); | 890 | atomic_set(&dest->inactconns, 0); |
867 | atomic_set(&dest->persistconns, 0); | 891 | atomic_set(&dest->persistconns, 0); |
868 | atomic_set(&dest->refcnt, 0); | 892 | atomic_set(&dest->refcnt, 1); |
869 | 893 | ||
870 | INIT_LIST_HEAD(&dest->d_list); | 894 | INIT_LIST_HEAD(&dest->d_list); |
871 | spin_lock_init(&dest->dst_lock); | 895 | spin_lock_init(&dest->dst_lock); |
872 | spin_lock_init(&dest->stats.lock); | 896 | spin_lock_init(&dest->stats.lock); |
873 | __ip_vs_update_dest(svc, dest, udest); | 897 | __ip_vs_update_dest(svc, dest, udest, 1); |
874 | ip_vs_new_estimator(&dest->stats); | ||
875 | 898 | ||
876 | *dest_p = dest; | 899 | *dest_p = dest; |
877 | 900 | ||
@@ -931,65 +954,22 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) | |||
931 | IP_VS_DBG_ADDR(svc->af, &dest->vaddr), | 954 | IP_VS_DBG_ADDR(svc->af, &dest->vaddr), |
932 | ntohs(dest->vport)); | 955 | ntohs(dest->vport)); |
933 | 956 | ||
934 | __ip_vs_update_dest(svc, dest, udest); | ||
935 | |||
936 | /* | 957 | /* |
937 | * Get the destination from the trash | 958 | * Get the destination from the trash |
938 | */ | 959 | */ |
939 | list_del(&dest->n_list); | 960 | list_del(&dest->n_list); |
940 | 961 | ||
941 | ip_vs_new_estimator(&dest->stats); | 962 | __ip_vs_update_dest(svc, dest, udest, 1); |
942 | 963 | ret = 0; | |
943 | write_lock_bh(&__ip_vs_svc_lock); | 964 | } else { |
944 | |||
945 | /* | 965 | /* |
946 | * Wait until all other svc users go away. | 966 | * Allocate and initialize the dest structure |
947 | */ | 967 | */ |
948 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 1); | 968 | ret = ip_vs_new_dest(svc, udest, &dest); |
949 | |||
950 | list_add(&dest->n_list, &svc->destinations); | ||
951 | svc->num_dests++; | ||
952 | |||
953 | /* call the update_service function of its scheduler */ | ||
954 | if (svc->scheduler->update_service) | ||
955 | svc->scheduler->update_service(svc); | ||
956 | |||
957 | write_unlock_bh(&__ip_vs_svc_lock); | ||
958 | return 0; | ||
959 | } | ||
960 | |||
961 | /* | ||
962 | * Allocate and initialize the dest structure | ||
963 | */ | ||
964 | ret = ip_vs_new_dest(svc, udest, &dest); | ||
965 | if (ret) { | ||
966 | return ret; | ||
967 | } | 969 | } |
968 | |||
969 | /* | ||
970 | * Add the dest entry into the list | ||
971 | */ | ||
972 | atomic_inc(&dest->refcnt); | ||
973 | |||
974 | write_lock_bh(&__ip_vs_svc_lock); | ||
975 | |||
976 | /* | ||
977 | * Wait until all other svc users go away. | ||
978 | */ | ||
979 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 1); | ||
980 | |||
981 | list_add(&dest->n_list, &svc->destinations); | ||
982 | svc->num_dests++; | ||
983 | |||
984 | /* call the update_service function of its scheduler */ | ||
985 | if (svc->scheduler->update_service) | ||
986 | svc->scheduler->update_service(svc); | ||
987 | |||
988 | write_unlock_bh(&__ip_vs_svc_lock); | ||
989 | |||
990 | LeaveFunction(2); | 970 | LeaveFunction(2); |
991 | 971 | ||
992 | return 0; | 972 | return ret; |
993 | } | 973 | } |
994 | 974 | ||
995 | 975 | ||
@@ -1028,19 +1008,7 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) | |||
1028 | return -ENOENT; | 1008 | return -ENOENT; |
1029 | } | 1009 | } |
1030 | 1010 | ||
1031 | __ip_vs_update_dest(svc, dest, udest); | 1011 | __ip_vs_update_dest(svc, dest, udest, 0); |
1032 | |||
1033 | write_lock_bh(&__ip_vs_svc_lock); | ||
1034 | |||
1035 | /* Wait until all other svc users go away */ | ||
1036 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 1); | ||
1037 | |||
1038 | /* call the update_service, because server weight may be changed */ | ||
1039 | if (svc->scheduler->update_service) | ||
1040 | svc->scheduler->update_service(svc); | ||
1041 | |||
1042 | write_unlock_bh(&__ip_vs_svc_lock); | ||
1043 | |||
1044 | LeaveFunction(2); | 1012 | LeaveFunction(2); |
1045 | 1013 | ||
1046 | return 0; | 1014 | return 0; |
@@ -1067,6 +1035,10 @@ static void __ip_vs_del_dest(struct ip_vs_dest *dest) | |||
1067 | * the destination into the trash. | 1035 | * the destination into the trash. |
1068 | */ | 1036 | */ |
1069 | if (atomic_dec_and_test(&dest->refcnt)) { | 1037 | if (atomic_dec_and_test(&dest->refcnt)) { |
1038 | IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u\n", | ||
1039 | dest->vfwmark, | ||
1040 | IP_VS_DBG_ADDR(dest->af, &dest->addr), | ||
1041 | ntohs(dest->port)); | ||
1070 | ip_vs_dst_reset(dest); | 1042 | ip_vs_dst_reset(dest); |
1071 | /* simply decrease svc->refcnt here, let the caller check | 1043 | /* simply decrease svc->refcnt here, let the caller check |
1072 | and release the service if nobody refers to it. | 1044 | and release the service if nobody refers to it. |
@@ -1133,7 +1105,7 @@ ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) | |||
1133 | /* | 1105 | /* |
1134 | * Wait until all other svc users go away. | 1106 | * Wait until all other svc users go away. |
1135 | */ | 1107 | */ |
1136 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 1); | 1108 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); |
1137 | 1109 | ||
1138 | /* | 1110 | /* |
1139 | * Unlink dest from the service | 1111 | * Unlink dest from the service |
@@ -1190,7 +1162,7 @@ ip_vs_add_service(struct ip_vs_service_user_kern *u, | |||
1190 | } | 1162 | } |
1191 | 1163 | ||
1192 | /* I'm the first user of the service */ | 1164 | /* I'm the first user of the service */ |
1193 | atomic_set(&svc->usecnt, 1); | 1165 | atomic_set(&svc->usecnt, 0); |
1194 | atomic_set(&svc->refcnt, 0); | 1166 | atomic_set(&svc->refcnt, 0); |
1195 | 1167 | ||
1196 | svc->af = u->af; | 1168 | svc->af = u->af; |
@@ -1284,7 +1256,7 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u) | |||
1284 | /* | 1256 | /* |
1285 | * Wait until all other svc users go away. | 1257 | * Wait until all other svc users go away. |
1286 | */ | 1258 | */ |
1287 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 1); | 1259 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); |
1288 | 1260 | ||
1289 | /* | 1261 | /* |
1290 | * Set the flags and timeout value | 1262 | * Set the flags and timeout value |
@@ -1383,21 +1355,23 @@ static void __ip_vs_del_service(struct ip_vs_service *svc) | |||
1383 | /* | 1355 | /* |
1384 | * Free the service if nobody refers to it | 1356 | * Free the service if nobody refers to it |
1385 | */ | 1357 | */ |
1386 | if (atomic_read(&svc->refcnt) == 0) | 1358 | if (atomic_read(&svc->refcnt) == 0) { |
1359 | IP_VS_DBG_BUF(3, "Removing service %u/%s:%u usecnt=%d\n", | ||
1360 | svc->fwmark, | ||
1361 | IP_VS_DBG_ADDR(svc->af, &svc->addr), | ||
1362 | ntohs(svc->port), atomic_read(&svc->usecnt)); | ||
1387 | kfree(svc); | 1363 | kfree(svc); |
1364 | } | ||
1388 | 1365 | ||
1389 | /* decrease the module use count */ | 1366 | /* decrease the module use count */ |
1390 | ip_vs_use_count_dec(); | 1367 | ip_vs_use_count_dec(); |
1391 | } | 1368 | } |
1392 | 1369 | ||
1393 | /* | 1370 | /* |
1394 | * Delete a service from the service list | 1371 | * Unlink a service from list and try to delete it if its refcnt reached 0 |
1395 | */ | 1372 | */ |
1396 | static int ip_vs_del_service(struct ip_vs_service *svc) | 1373 | static void ip_vs_unlink_service(struct ip_vs_service *svc) |
1397 | { | 1374 | { |
1398 | if (svc == NULL) | ||
1399 | return -EEXIST; | ||
1400 | |||
1401 | /* | 1375 | /* |
1402 | * Unhash it from the service table | 1376 | * Unhash it from the service table |
1403 | */ | 1377 | */ |
@@ -1408,11 +1382,21 @@ static int ip_vs_del_service(struct ip_vs_service *svc) | |||
1408 | /* | 1382 | /* |
1409 | * Wait until all the svc users go away. | 1383 | * Wait until all the svc users go away. |
1410 | */ | 1384 | */ |
1411 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 1); | 1385 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); |
1412 | 1386 | ||
1413 | __ip_vs_del_service(svc); | 1387 | __ip_vs_del_service(svc); |
1414 | 1388 | ||
1415 | write_unlock_bh(&__ip_vs_svc_lock); | 1389 | write_unlock_bh(&__ip_vs_svc_lock); |
1390 | } | ||
1391 | |||
1392 | /* | ||
1393 | * Delete a service from the service list | ||
1394 | */ | ||
1395 | static int ip_vs_del_service(struct ip_vs_service *svc) | ||
1396 | { | ||
1397 | if (svc == NULL) | ||
1398 | return -EEXIST; | ||
1399 | ip_vs_unlink_service(svc); | ||
1416 | 1400 | ||
1417 | return 0; | 1401 | return 0; |
1418 | } | 1402 | } |
@@ -1431,14 +1415,7 @@ static int ip_vs_flush(void) | |||
1431 | */ | 1415 | */ |
1432 | for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { | 1416 | for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { |
1433 | list_for_each_entry_safe(svc, nxt, &ip_vs_svc_table[idx], s_list) { | 1417 | list_for_each_entry_safe(svc, nxt, &ip_vs_svc_table[idx], s_list) { |
1434 | write_lock_bh(&__ip_vs_svc_lock); | 1418 | ip_vs_unlink_service(svc); |
1435 | ip_vs_svc_unhash(svc); | ||
1436 | /* | ||
1437 | * Wait until all the svc users go away. | ||
1438 | */ | ||
1439 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); | ||
1440 | __ip_vs_del_service(svc); | ||
1441 | write_unlock_bh(&__ip_vs_svc_lock); | ||
1442 | } | 1419 | } |
1443 | } | 1420 | } |
1444 | 1421 | ||
@@ -1448,14 +1425,7 @@ static int ip_vs_flush(void) | |||
1448 | for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { | 1425 | for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { |
1449 | list_for_each_entry_safe(svc, nxt, | 1426 | list_for_each_entry_safe(svc, nxt, |
1450 | &ip_vs_svc_fwm_table[idx], f_list) { | 1427 | &ip_vs_svc_fwm_table[idx], f_list) { |
1451 | write_lock_bh(&__ip_vs_svc_lock); | 1428 | ip_vs_unlink_service(svc); |
1452 | ip_vs_svc_unhash(svc); | ||
1453 | /* | ||
1454 | * Wait until all the svc users go away. | ||
1455 | */ | ||
1456 | IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); | ||
1457 | __ip_vs_del_service(svc); | ||
1458 | write_unlock_bh(&__ip_vs_svc_lock); | ||
1459 | } | 1429 | } |
1460 | } | 1430 | } |
1461 | 1431 | ||
@@ -2168,15 +2138,15 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | |||
2168 | 2138 | ||
2169 | /* Lookup the exact service by <protocol, addr, port> or fwmark */ | 2139 | /* Lookup the exact service by <protocol, addr, port> or fwmark */ |
2170 | if (usvc.fwmark == 0) | 2140 | if (usvc.fwmark == 0) |
2171 | svc = __ip_vs_service_get(usvc.af, usvc.protocol, | 2141 | svc = __ip_vs_service_find(usvc.af, usvc.protocol, |
2172 | &usvc.addr, usvc.port); | 2142 | &usvc.addr, usvc.port); |
2173 | else | 2143 | else |
2174 | svc = __ip_vs_svc_fwm_get(usvc.af, usvc.fwmark); | 2144 | svc = __ip_vs_svc_fwm_find(usvc.af, usvc.fwmark); |
2175 | 2145 | ||
2176 | if (cmd != IP_VS_SO_SET_ADD | 2146 | if (cmd != IP_VS_SO_SET_ADD |
2177 | && (svc == NULL || svc->protocol != usvc.protocol)) { | 2147 | && (svc == NULL || svc->protocol != usvc.protocol)) { |
2178 | ret = -ESRCH; | 2148 | ret = -ESRCH; |
2179 | goto out_drop_service; | 2149 | goto out_unlock; |
2180 | } | 2150 | } |
2181 | 2151 | ||
2182 | switch (cmd) { | 2152 | switch (cmd) { |
@@ -2210,10 +2180,6 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | |||
2210 | ret = -EINVAL; | 2180 | ret = -EINVAL; |
2211 | } | 2181 | } |
2212 | 2182 | ||
2213 | out_drop_service: | ||
2214 | if (svc) | ||
2215 | ip_vs_service_put(svc); | ||
2216 | |||
2217 | out_unlock: | 2183 | out_unlock: |
2218 | mutex_unlock(&__ip_vs_mutex); | 2184 | mutex_unlock(&__ip_vs_mutex); |
2219 | out_dec: | 2185 | out_dec: |
@@ -2306,10 +2272,10 @@ __ip_vs_get_dest_entries(const struct ip_vs_get_dests *get, | |||
2306 | int ret = 0; | 2272 | int ret = 0; |
2307 | 2273 | ||
2308 | if (get->fwmark) | 2274 | if (get->fwmark) |
2309 | svc = __ip_vs_svc_fwm_get(AF_INET, get->fwmark); | 2275 | svc = __ip_vs_svc_fwm_find(AF_INET, get->fwmark); |
2310 | else | 2276 | else |
2311 | svc = __ip_vs_service_get(AF_INET, get->protocol, &addr, | 2277 | svc = __ip_vs_service_find(AF_INET, get->protocol, &addr, |
2312 | get->port); | 2278 | get->port); |
2313 | 2279 | ||
2314 | if (svc) { | 2280 | if (svc) { |
2315 | int count = 0; | 2281 | int count = 0; |
@@ -2337,7 +2303,6 @@ __ip_vs_get_dest_entries(const struct ip_vs_get_dests *get, | |||
2337 | } | 2303 | } |
2338 | count++; | 2304 | count++; |
2339 | } | 2305 | } |
2340 | ip_vs_service_put(svc); | ||
2341 | } else | 2306 | } else |
2342 | ret = -ESRCH; | 2307 | ret = -ESRCH; |
2343 | return ret; | 2308 | return ret; |
@@ -2458,15 +2423,14 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | |||
2458 | entry = (struct ip_vs_service_entry *)arg; | 2423 | entry = (struct ip_vs_service_entry *)arg; |
2459 | addr.ip = entry->addr; | 2424 | addr.ip = entry->addr; |
2460 | if (entry->fwmark) | 2425 | if (entry->fwmark) |
2461 | svc = __ip_vs_svc_fwm_get(AF_INET, entry->fwmark); | 2426 | svc = __ip_vs_svc_fwm_find(AF_INET, entry->fwmark); |
2462 | else | 2427 | else |
2463 | svc = __ip_vs_service_get(AF_INET, entry->protocol, | 2428 | svc = __ip_vs_service_find(AF_INET, entry->protocol, |
2464 | &addr, entry->port); | 2429 | &addr, entry->port); |
2465 | if (svc) { | 2430 | if (svc) { |
2466 | ip_vs_copy_service(entry, svc); | 2431 | ip_vs_copy_service(entry, svc); |
2467 | if (copy_to_user(user, entry, sizeof(*entry)) != 0) | 2432 | if (copy_to_user(user, entry, sizeof(*entry)) != 0) |
2468 | ret = -EFAULT; | 2433 | ret = -EFAULT; |
2469 | ip_vs_service_put(svc); | ||
2470 | } else | 2434 | } else |
2471 | ret = -ESRCH; | 2435 | ret = -ESRCH; |
2472 | } | 2436 | } |
@@ -2733,10 +2697,12 @@ nla_put_failure: | |||
2733 | } | 2697 | } |
2734 | 2698 | ||
2735 | static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, | 2699 | static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, |
2736 | struct nlattr *nla, int full_entry) | 2700 | struct nlattr *nla, int full_entry, |
2701 | struct ip_vs_service **ret_svc) | ||
2737 | { | 2702 | { |
2738 | struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1]; | 2703 | struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1]; |
2739 | struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr; | 2704 | struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr; |
2705 | struct ip_vs_service *svc; | ||
2740 | 2706 | ||
2741 | /* Parse mandatory identifying service fields first */ | 2707 | /* Parse mandatory identifying service fields first */ |
2742 | if (nla == NULL || | 2708 | if (nla == NULL || |
@@ -2772,12 +2738,18 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, | |||
2772 | usvc->fwmark = 0; | 2738 | usvc->fwmark = 0; |
2773 | } | 2739 | } |
2774 | 2740 | ||
2741 | if (usvc->fwmark) | ||
2742 | svc = __ip_vs_svc_fwm_find(usvc->af, usvc->fwmark); | ||
2743 | else | ||
2744 | svc = __ip_vs_service_find(usvc->af, usvc->protocol, | ||
2745 | &usvc->addr, usvc->port); | ||
2746 | *ret_svc = svc; | ||
2747 | |||
2775 | /* If a full entry was requested, check for the additional fields */ | 2748 | /* If a full entry was requested, check for the additional fields */ |
2776 | if (full_entry) { | 2749 | if (full_entry) { |
2777 | struct nlattr *nla_sched, *nla_flags, *nla_timeout, | 2750 | struct nlattr *nla_sched, *nla_flags, *nla_timeout, |
2778 | *nla_netmask; | 2751 | *nla_netmask; |
2779 | struct ip_vs_flags flags; | 2752 | struct ip_vs_flags flags; |
2780 | struct ip_vs_service *svc; | ||
2781 | 2753 | ||
2782 | nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME]; | 2754 | nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME]; |
2783 | nla_flags = attrs[IPVS_SVC_ATTR_FLAGS]; | 2755 | nla_flags = attrs[IPVS_SVC_ATTR_FLAGS]; |
@@ -2790,16 +2762,8 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, | |||
2790 | nla_memcpy(&flags, nla_flags, sizeof(flags)); | 2762 | nla_memcpy(&flags, nla_flags, sizeof(flags)); |
2791 | 2763 | ||
2792 | /* prefill flags from service if it already exists */ | 2764 | /* prefill flags from service if it already exists */ |
2793 | if (usvc->fwmark) | 2765 | if (svc) |
2794 | svc = __ip_vs_svc_fwm_get(usvc->af, usvc->fwmark); | ||
2795 | else | ||
2796 | svc = __ip_vs_service_get(usvc->af, usvc->protocol, | ||
2797 | &usvc->addr, usvc->port); | ||
2798 | if (svc) { | ||
2799 | usvc->flags = svc->flags; | 2766 | usvc->flags = svc->flags; |
2800 | ip_vs_service_put(svc); | ||
2801 | } else | ||
2802 | usvc->flags = 0; | ||
2803 | 2767 | ||
2804 | /* set new flags from userland */ | 2768 | /* set new flags from userland */ |
2805 | usvc->flags = (usvc->flags & ~flags.mask) | | 2769 | usvc->flags = (usvc->flags & ~flags.mask) | |
@@ -2815,17 +2779,11 @@ static int ip_vs_genl_parse_service(struct ip_vs_service_user_kern *usvc, | |||
2815 | static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla) | 2779 | static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla) |
2816 | { | 2780 | { |
2817 | struct ip_vs_service_user_kern usvc; | 2781 | struct ip_vs_service_user_kern usvc; |
2782 | struct ip_vs_service *svc; | ||
2818 | int ret; | 2783 | int ret; |
2819 | 2784 | ||
2820 | ret = ip_vs_genl_parse_service(&usvc, nla, 0); | 2785 | ret = ip_vs_genl_parse_service(&usvc, nla, 0, &svc); |
2821 | if (ret) | 2786 | return ret ? ERR_PTR(ret) : svc; |
2822 | return ERR_PTR(ret); | ||
2823 | |||
2824 | if (usvc.fwmark) | ||
2825 | return __ip_vs_svc_fwm_get(usvc.af, usvc.fwmark); | ||
2826 | else | ||
2827 | return __ip_vs_service_get(usvc.af, usvc.protocol, | ||
2828 | &usvc.addr, usvc.port); | ||
2829 | } | 2787 | } |
2830 | 2788 | ||
2831 | static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) | 2789 | static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) |
@@ -2916,7 +2874,6 @@ static int ip_vs_genl_dump_dests(struct sk_buff *skb, | |||
2916 | 2874 | ||
2917 | nla_put_failure: | 2875 | nla_put_failure: |
2918 | cb->args[0] = idx; | 2876 | cb->args[0] = idx; |
2919 | ip_vs_service_put(svc); | ||
2920 | 2877 | ||
2921 | out_err: | 2878 | out_err: |
2922 | mutex_unlock(&__ip_vs_mutex); | 2879 | mutex_unlock(&__ip_vs_mutex); |
@@ -3129,17 +3086,10 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) | |||
3129 | 3086 | ||
3130 | ret = ip_vs_genl_parse_service(&usvc, | 3087 | ret = ip_vs_genl_parse_service(&usvc, |
3131 | info->attrs[IPVS_CMD_ATTR_SERVICE], | 3088 | info->attrs[IPVS_CMD_ATTR_SERVICE], |
3132 | need_full_svc); | 3089 | need_full_svc, &svc); |
3133 | if (ret) | 3090 | if (ret) |
3134 | goto out; | 3091 | goto out; |
3135 | 3092 | ||
3136 | /* Lookup the exact service by <protocol, addr, port> or fwmark */ | ||
3137 | if (usvc.fwmark == 0) | ||
3138 | svc = __ip_vs_service_get(usvc.af, usvc.protocol, | ||
3139 | &usvc.addr, usvc.port); | ||
3140 | else | ||
3141 | svc = __ip_vs_svc_fwm_get(usvc.af, usvc.fwmark); | ||
3142 | |||
3143 | /* Unless we're adding a new service, the service must already exist */ | 3093 | /* Unless we're adding a new service, the service must already exist */ |
3144 | if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) { | 3094 | if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) { |
3145 | ret = -ESRCH; | 3095 | ret = -ESRCH; |
@@ -3173,6 +3123,7 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) | |||
3173 | break; | 3123 | break; |
3174 | case IPVS_CMD_DEL_SERVICE: | 3124 | case IPVS_CMD_DEL_SERVICE: |
3175 | ret = ip_vs_del_service(svc); | 3125 | ret = ip_vs_del_service(svc); |
3126 | /* do not use svc, it can be freed */ | ||
3176 | break; | 3127 | break; |
3177 | case IPVS_CMD_NEW_DEST: | 3128 | case IPVS_CMD_NEW_DEST: |
3178 | ret = ip_vs_add_dest(svc, &udest); | 3129 | ret = ip_vs_add_dest(svc, &udest); |
@@ -3191,8 +3142,6 @@ static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) | |||
3191 | } | 3142 | } |
3192 | 3143 | ||
3193 | out: | 3144 | out: |
3194 | if (svc) | ||
3195 | ip_vs_service_put(svc); | ||
3196 | mutex_unlock(&__ip_vs_mutex); | 3145 | mutex_unlock(&__ip_vs_mutex); |
3197 | 3146 | ||
3198 | return ret; | 3147 | return ret; |
@@ -3238,7 +3187,6 @@ static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) | |||
3238 | goto out_err; | 3187 | goto out_err; |
3239 | } else if (svc) { | 3188 | } else if (svc) { |
3240 | ret = ip_vs_genl_fill_service(msg, svc); | 3189 | ret = ip_vs_genl_fill_service(msg, svc); |
3241 | ip_vs_service_put(svc); | ||
3242 | if (ret) | 3190 | if (ret) |
3243 | goto nla_put_failure; | 3191 | goto nla_put_failure; |
3244 | } else { | 3192 | } else { |