Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipvs: convert services to rcu

This is the final step in RCU conversion.

Things that are removed:

- svc->usecnt: now svc is accessed under RCU read lock
- svc->inc: and some unused code
- ip_vs_bind_pe and ip_vs_unbind_pe: no ability to replace PE
- __ip_vs_svc_lock: replaced with RCU
- IP_VS_WAIT_WHILE: now readers lookup svcs and dests under
RCU and work in parallel with configuration

Other changes:

- before now, a RCU read-side critical section included the
calling of the schedule method, now it is extended to include
service lookup
- ip_vs_svc_table and ip_vs_svc_fwm_table are now using hlist
- svc->pe and svc->scheduler remain to the end (of grace period),
the schedulers are prepared for such RCU readers
even after done_service is called but they need
to use synchronize_rcu because last ip_vs_scheduler_put
can happen while RCU read-side critical sections
use an outdated svc->scheduler pointer
- as planned, update_service is removed
- empty services can be freed immediately after grace period.
If dests were present, the services are freed from
the dest trash code

Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>

authored by

Julian Anastasov and committed by
Pablo Neira Ayuso
ceec4c38 413c2d04

+186 -259
+9 -19
include/net/ip_vs.h
··· 359 359 #define LeaveFunction(level) do {} while (0) 360 360 #endif 361 361 362 - #define IP_VS_WAIT_WHILE(expr) while (expr) { cpu_relax(); } 363 - 364 362 365 363 /* 366 364 * The port number of FTP service (in network order). ··· 710 712 * and the forwarding entries 711 713 */ 712 714 struct ip_vs_service { 713 - struct list_head s_list; /* for normal service table */ 714 - struct list_head f_list; /* for fwmark-based service table */ 715 + struct hlist_node s_list; /* for normal service table */ 716 + struct hlist_node f_list; /* for fwmark-based service table */ 715 717 atomic_t refcnt; /* reference counter */ 716 - atomic_t usecnt; /* use counter */ 717 718 718 719 u16 af; /* address family */ 719 720 __u16 protocol; /* which protocol (TCP/UDP) */ ··· 727 730 struct list_head destinations; /* real server d-linked list */ 728 731 __u32 num_dests; /* number of servers */ 729 732 struct ip_vs_stats stats; /* statistics for the service */ 730 - struct ip_vs_app *inc; /* bind conns to this app inc */ 731 733 732 734 /* for scheduling */ 733 - struct ip_vs_scheduler *scheduler; /* bound scheduler object */ 735 + struct ip_vs_scheduler __rcu *scheduler; /* bound scheduler object */ 734 736 spinlock_t sched_lock; /* lock sched_data */ 735 737 void *sched_data; /* scheduler application data */ 736 738 737 739 /* alternate persistence engine */ 738 - struct ip_vs_pe *pe; 740 + struct ip_vs_pe __rcu *pe; 741 + 742 + struct rcu_head rcu_head; 739 743 }; 740 744 741 745 /* Information for cached dst */ ··· 805 807 int (*init_service)(struct ip_vs_service *svc); 806 808 /* scheduling service finish */ 807 809 void (*done_service)(struct ip_vs_service *svc); 808 - /* scheduler updating service */ 809 - int (*update_service)(struct ip_vs_service *svc); 810 810 /* dest is linked */ 811 811 int (*add_dest)(struct ip_vs_service *svc, struct ip_vs_dest *dest); 812 812 /* dest is unlinked */ ··· 1340 1344 extern int ip_vs_app_pkt_out(struct ip_vs_conn *, struct sk_buff *skb); 1341 1345 extern int ip_vs_app_pkt_in(struct ip_vs_conn *, struct sk_buff *skb); 1342 1346 1343 - void ip_vs_bind_pe(struct ip_vs_service *svc, struct ip_vs_pe *pe); 1344 - void ip_vs_unbind_pe(struct ip_vs_service *svc); 1345 1347 int register_ip_vs_pe(struct ip_vs_pe *pe); 1346 1348 int unregister_ip_vs_pe(struct ip_vs_pe *pe); 1347 1349 struct ip_vs_pe *ip_vs_pe_getbyname(const char *name); ··· 1386 1392 extern int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler); 1387 1393 extern int ip_vs_bind_scheduler(struct ip_vs_service *svc, 1388 1394 struct ip_vs_scheduler *scheduler); 1389 - extern void ip_vs_unbind_scheduler(struct ip_vs_service *svc); 1395 + extern void ip_vs_unbind_scheduler(struct ip_vs_service *svc, 1396 + struct ip_vs_scheduler *sched); 1390 1397 extern struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name); 1391 1398 extern void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler); 1392 1399 extern struct ip_vs_conn * ··· 1407 1412 extern int sysctl_ip_vs_sync_ver; 1408 1413 1409 1414 extern struct ip_vs_service * 1410 - ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol, 1415 + ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol, 1411 1416 const union nf_inet_addr *vaddr, __be16 vport); 1412 - 1413 - static inline void ip_vs_service_put(struct ip_vs_service *svc) 1414 - { 1415 - atomic_dec(&svc->usecnt); 1416 - } 1417 1417 1418 1418 extern bool 1419 1419 ip_vs_has_real_service(struct net *net, int af, __u16 protocol,
+9 -17
net/netfilter/ipvs/ip_vs_core.c
··· 203 203 { 204 204 ip_vs_conn_fill_param(svc->net, svc->af, protocol, caddr, cport, vaddr, 205 205 vport, p); 206 - p->pe = svc->pe; 206 + p->pe = rcu_dereference(svc->pe); 207 207 if (p->pe && p->pe->fill_param) 208 208 return p->pe->fill_param(p, skb); 209 209 ··· 296 296 /* Check if a template already exists */ 297 297 ct = ip_vs_ct_in_get(&param); 298 298 if (!ct || !ip_vs_check_template(ct)) { 299 + struct ip_vs_scheduler *sched; 300 + 299 301 /* 300 302 * No template found or the dest of the connection 301 303 * template is not available. 302 304 * return *ignored=0 i.e. ICMP and NF_DROP 303 305 */ 304 - rcu_read_lock(); 305 - dest = svc->scheduler->schedule(svc, skb); 306 + sched = rcu_dereference(svc->scheduler); 307 + dest = sched->schedule(svc, skb); 306 308 if (!dest) { 307 - rcu_read_unlock(); 308 309 IP_VS_DBG(1, "p-schedule: no dest found.\n"); 309 310 kfree(param.pe_data); 310 311 *ignored = 0; ··· 321 320 * when the template expires */ 322 321 ct = ip_vs_conn_new(&param, &dest->addr, dport, 323 322 IP_VS_CONN_F_TEMPLATE, dest, skb->mark); 324 - rcu_read_unlock(); 325 323 if (ct == NULL) { 326 324 kfree(param.pe_data); 327 325 *ignored = -1; ··· 394 394 { 395 395 struct ip_vs_protocol *pp = pd->pp; 396 396 struct ip_vs_conn *cp = NULL; 397 + struct ip_vs_scheduler *sched; 397 398 struct ip_vs_dest *dest; 398 399 __be16 _ports[2], *pptr; 399 400 unsigned int flags; ··· 450 449 return NULL; 451 450 } 452 451 453 - rcu_read_lock(); 454 - dest = svc->scheduler->schedule(svc, skb); 452 + sched = rcu_dereference(svc->scheduler); 453 + dest = sched->schedule(svc, skb); 455 454 if (dest == NULL) { 456 - rcu_read_unlock(); 457 455 IP_VS_DBG(1, "Schedule: no dest found.\n"); 458 456 return NULL; 459 457 } ··· 473 473 cp = ip_vs_conn_new(&p, &dest->addr, 474 474 dest->port ? dest->port : pptr[1], 475 475 flags, dest, skb->mark); 476 - rcu_read_unlock(); 477 476 if (!cp) { 478 477 *ignored = -1; 479 478 return NULL; ··· 509 510 510 511 pptr = frag_safe_skb_hp(skb, iph->len, sizeof(_ports), _ports, iph); 511 512 if (pptr == NULL) { 512 - ip_vs_service_put(svc); 513 513 return NF_DROP; 514 514 } 515 515 ··· 533 535 iph->protocol == IPPROTO_UDP) ? 534 536 IP_VS_CONN_F_ONE_PACKET : 0; 535 537 union nf_inet_addr daddr = { .all = { 0, 0, 0, 0 } }; 536 - 537 - ip_vs_service_put(svc); 538 538 539 539 /* create a new connection entry */ 540 540 IP_VS_DBG(6, "%s(): create a cache_bypass entry\n", __func__); ··· 570 574 * listed in the ipvs table), pass the packets, because it is 571 575 * not ipvs job to decide to drop the packets. 572 576 */ 573 - if ((svc->port == FTPPORT) && (pptr[1] != FTPPORT)) { 574 - ip_vs_service_put(svc); 577 + if ((svc->port == FTPPORT) && (pptr[1] != FTPPORT)) 575 578 return NF_ACCEPT; 576 - } 577 - 578 - ip_vs_service_put(svc); 579 579 580 580 /* 581 581 * Notify the client that the destination is unreachable, and
+123 -176
net/netfilter/ipvs/ip_vs_ctl.c
··· 55 55 /* semaphore for IPVS sockopts. And, [gs]etsockopt may sleep. */ 56 56 static DEFINE_MUTEX(__ip_vs_mutex); 57 57 58 - /* lock for service table */ 59 - static DEFINE_RWLOCK(__ip_vs_svc_lock); 60 - 61 58 /* sysctl variables */ 62 59 63 60 #ifdef CONFIG_IP_VS_DEBUG ··· 254 257 #define IP_VS_SVC_TAB_MASK (IP_VS_SVC_TAB_SIZE - 1) 255 258 256 259 /* the service table hashed by <protocol, addr, port> */ 257 - static struct list_head ip_vs_svc_table[IP_VS_SVC_TAB_SIZE]; 260 + static struct hlist_head ip_vs_svc_table[IP_VS_SVC_TAB_SIZE]; 258 261 /* the service table hashed by fwmark */ 259 - static struct list_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE]; 262 + static struct hlist_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE]; 260 263 261 264 262 265 /* ··· 311 314 */ 312 315 hash = ip_vs_svc_hashkey(svc->net, svc->af, svc->protocol, 313 316 &svc->addr, svc->port); 314 - list_add(&svc->s_list, &ip_vs_svc_table[hash]); 317 + hlist_add_head_rcu(&svc->s_list, &ip_vs_svc_table[hash]); 315 318 } else { 316 319 /* 317 320 * Hash it by fwmark in svc_fwm_table 318 321 */ 319 322 hash = ip_vs_svc_fwm_hashkey(svc->net, svc->fwmark); 320 - list_add(&svc->f_list, &ip_vs_svc_fwm_table[hash]); 323 + hlist_add_head_rcu(&svc->f_list, &ip_vs_svc_fwm_table[hash]); 321 324 } 322 325 323 326 svc->flags |= IP_VS_SVC_F_HASHED; ··· 341 344 342 345 if (svc->fwmark == 0) { 343 346 /* Remove it from the svc_table table */ 344 - list_del(&svc->s_list); 347 + hlist_del_rcu(&svc->s_list); 345 348 } else { 346 349 /* Remove it from the svc_fwm_table table */ 347 - list_del(&svc->f_list); 350 + hlist_del_rcu(&svc->f_list); 348 351 } 349 352 350 353 svc->flags &= ~IP_VS_SVC_F_HASHED; ··· 366 369 /* Check for "full" addressed entries */ 367 370 hash = ip_vs_svc_hashkey(net, af, protocol, vaddr, vport); 368 371 369 - list_for_each_entry(svc, &ip_vs_svc_table[hash], s_list){ 372 + hlist_for_each_entry_rcu(svc, &ip_vs_svc_table[hash], s_list) { 370 373 if ((svc->af == af) 371 374 && ip_vs_addr_equal(af, &svc->addr, vaddr) 372 375 && (svc->port == vport) ··· 393 396 /* Check for fwmark addressed entries */ 394 397 hash = ip_vs_svc_fwm_hashkey(net, fwmark); 395 398 396 - list_for_each_entry(svc, &ip_vs_svc_fwm_table[hash], f_list) { 399 + hlist_for_each_entry_rcu(svc, &ip_vs_svc_fwm_table[hash], f_list) { 397 400 if (svc->fwmark == fwmark && svc->af == af 398 401 && net_eq(svc->net, net)) { 399 402 /* HIT */ ··· 404 407 return NULL; 405 408 } 406 409 410 + /* Find service, called under RCU lock */ 407 411 struct ip_vs_service * 408 - ip_vs_service_get(struct net *net, int af, __u32 fwmark, __u16 protocol, 409 - const union nf_inet_addr *vaddr, __be16 vport) 412 + ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol, 413 + const union nf_inet_addr *vaddr, __be16 vport) 410 414 { 411 415 struct ip_vs_service *svc; 412 416 struct netns_ipvs *ipvs = net_ipvs(net); 413 - 414 - read_lock(&__ip_vs_svc_lock); 415 417 416 418 /* 417 419 * Check the table hashed by fwmark first ··· 447 451 } 448 452 449 453 out: 450 - if (svc) 451 - atomic_inc(&svc->usecnt); 452 - read_unlock(&__ip_vs_svc_lock); 453 - 454 454 IP_VS_DBG_BUF(9, "lookup service: fwm %u %s %s:%u %s\n", 455 455 fwmark, ip_vs_proto_name(protocol), 456 456 IP_VS_DBG_ADDR(af, vaddr), ntohs(vport), ··· 463 471 dest->svc = svc; 464 472 } 465 473 474 + static void ip_vs_service_free(struct ip_vs_service *svc) 475 + { 476 + if (svc->stats.cpustats) 477 + free_percpu(svc->stats.cpustats); 478 + kfree(svc); 479 + } 480 + 466 481 static void 467 482 __ip_vs_unbind_svc(struct ip_vs_dest *dest) 468 483 { ··· 477 478 478 479 dest->svc = NULL; 479 480 if (atomic_dec_and_test(&svc->refcnt)) { 480 - IP_VS_DBG_BUF(3, "Removing service %u/%s:%u usecnt=%d\n", 481 + IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n", 481 482 svc->fwmark, 482 483 IP_VS_DBG_ADDR(svc->af, &svc->addr), 483 - ntohs(svc->port), atomic_read(&svc->usecnt)); 484 - free_percpu(svc->stats.cpustats); 485 - kfree(svc); 484 + ntohs(svc->port)); 485 + ip_vs_service_free(svc); 486 486 } 487 487 } 488 488 ··· 606 608 struct ip_vs_service *svc; 607 609 __be16 port = dport; 608 610 609 - svc = ip_vs_service_get(net, af, fwmark, protocol, vaddr, vport); 611 + svc = ip_vs_service_find(net, af, fwmark, protocol, vaddr, vport); 610 612 if (!svc) 611 613 return NULL; 612 614 if (fwmark && (flags & IP_VS_CONN_F_FWD_MASK) != IP_VS_CONN_F_MASQ) ··· 614 616 dest = ip_vs_lookup_dest(svc, daddr, port); 615 617 if (!dest) 616 618 dest = ip_vs_lookup_dest(svc, daddr, port ^ dport); 617 - ip_vs_service_put(svc); 618 619 return dest; 619 620 } 620 621 ··· 771 774 struct ip_vs_dest_user_kern *udest, int add) 772 775 { 773 776 struct netns_ipvs *ipvs = net_ipvs(svc->net); 777 + struct ip_vs_scheduler *sched; 774 778 int conn_flags; 775 779 776 780 /* set the weight and the flags */ ··· 814 816 __ip_vs_dst_cache_reset(dest); 815 817 spin_unlock_bh(&dest->dst_lock); 816 818 817 - if (add) 818 - ip_vs_start_estimator(svc->net, &dest->stats); 819 - 820 - write_lock_bh(&__ip_vs_svc_lock); 821 - 822 - /* Wait until all other svc users go away */ 823 - IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); 824 - 819 + sched = rcu_dereference_protected(svc->scheduler, 1); 825 820 if (add) { 821 + ip_vs_start_estimator(svc->net, &dest->stats); 826 822 list_add_rcu(&dest->n_list, &svc->destinations); 827 823 svc->num_dests++; 828 - if (svc->scheduler->add_dest) 829 - svc->scheduler->add_dest(svc, dest); 824 + if (sched->add_dest) 825 + sched->add_dest(svc, dest); 830 826 } else { 831 - if (svc->scheduler->upd_dest) 832 - svc->scheduler->upd_dest(svc, dest); 827 + if (sched->upd_dest) 828 + sched->upd_dest(svc, dest); 833 829 } 834 - 835 - /* call the update_service, because server weight may be changed */ 836 - if (svc->scheduler->update_service) 837 - svc->scheduler->update_service(svc); 838 - 839 - write_unlock_bh(&__ip_vs_svc_lock); 840 830 } 841 831 842 832 ··· 1057 1071 list_del_rcu(&dest->n_list); 1058 1072 svc->num_dests--; 1059 1073 1060 - if (svcupd && svc->scheduler->del_dest) 1061 - svc->scheduler->del_dest(svc, dest); 1074 + if (svcupd) { 1075 + struct ip_vs_scheduler *sched; 1062 1076 1063 - /* 1064 - * Call the update_service function of its scheduler 1065 - */ 1066 - if (svcupd && svc->scheduler->update_service) 1067 - svc->scheduler->update_service(svc); 1077 + sched = rcu_dereference_protected(svc->scheduler, 1); 1078 + if (sched->del_dest) 1079 + sched->del_dest(svc, dest); 1080 + } 1068 1081 } 1069 1082 1070 1083 ··· 1088 1103 return -ENOENT; 1089 1104 } 1090 1105 1091 - write_lock_bh(&__ip_vs_svc_lock); 1092 - 1093 - /* 1094 - * Wait until all other svc users go away. 1095 - */ 1096 - IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); 1097 - 1098 1106 /* 1099 1107 * Unlink dest from the service 1100 1108 */ 1101 1109 __ip_vs_unlink_dest(svc, dest, 1); 1102 - 1103 - write_unlock_bh(&__ip_vs_svc_lock); 1104 1110 1105 1111 /* 1106 1112 * Delete the destination ··· 1183 1207 } 1184 1208 1185 1209 /* I'm the first user of the service */ 1186 - atomic_set(&svc->usecnt, 0); 1187 1210 atomic_set(&svc->refcnt, 0); 1188 1211 1189 1212 svc->af = u->af; ··· 1206 1231 sched = NULL; 1207 1232 1208 1233 /* Bind the ct retriever */ 1209 - ip_vs_bind_pe(svc, pe); 1234 + RCU_INIT_POINTER(svc->pe, pe); 1210 1235 pe = NULL; 1211 1236 1212 1237 /* Update the virtual service counters */ ··· 1222 1247 ipvs->num_services++; 1223 1248 1224 1249 /* Hash the service into the service table */ 1225 - write_lock_bh(&__ip_vs_svc_lock); 1226 1250 ip_vs_svc_hash(svc); 1227 - write_unlock_bh(&__ip_vs_svc_lock); 1228 1251 1229 1252 *svc_p = svc; 1230 1253 /* Now there is a service - full throttle */ ··· 1232 1259 1233 1260 out_err: 1234 1261 if (svc != NULL) { 1235 - ip_vs_unbind_scheduler(svc); 1236 - if (svc->inc) { 1237 - local_bh_disable(); 1238 - ip_vs_app_inc_put(svc->inc); 1239 - local_bh_enable(); 1240 - } 1241 - if (svc->stats.cpustats) 1242 - free_percpu(svc->stats.cpustats); 1243 - kfree(svc); 1262 + ip_vs_unbind_scheduler(svc, sched); 1263 + ip_vs_service_free(svc); 1244 1264 } 1245 1265 ip_vs_scheduler_put(sched); 1246 1266 ip_vs_pe_put(pe); ··· 1283 1317 } 1284 1318 #endif 1285 1319 1286 - write_lock_bh(&__ip_vs_svc_lock); 1287 - 1288 - /* 1289 - * Wait until all other svc users go away. 1290 - */ 1291 - IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); 1320 + old_sched = rcu_dereference_protected(svc->scheduler, 1); 1321 + if (sched != old_sched) { 1322 + /* Bind the new scheduler */ 1323 + ret = ip_vs_bind_scheduler(svc, sched); 1324 + if (ret) { 1325 + old_sched = sched; 1326 + goto out; 1327 + } 1328 + /* Unbind the old scheduler on success */ 1329 + ip_vs_unbind_scheduler(svc, old_sched); 1330 + } 1292 1331 1293 1332 /* 1294 1333 * Set the flags and timeout value ··· 1302 1331 svc->timeout = u->timeout * HZ; 1303 1332 svc->netmask = u->netmask; 1304 1333 1305 - old_sched = svc->scheduler; 1306 - if (sched != old_sched) { 1307 - /* 1308 - * Unbind the old scheduler 1309 - */ 1310 - ip_vs_unbind_scheduler(svc); 1334 + old_pe = rcu_dereference_protected(svc->pe, 1); 1335 + if (pe != old_pe) 1336 + rcu_assign_pointer(svc->pe, pe); 1311 1337 1312 - /* 1313 - * Bind the new scheduler 1314 - */ 1315 - if ((ret = ip_vs_bind_scheduler(svc, sched))) { 1316 - /* 1317 - * If ip_vs_bind_scheduler fails, restore the old 1318 - * scheduler. 1319 - * The main reason of failure is out of memory. 1320 - * 1321 - * The question is if the old scheduler can be 1322 - * restored all the time. TODO: if it cannot be 1323 - * restored some time, we must delete the service, 1324 - * otherwise the system may crash. 1325 - */ 1326 - ip_vs_bind_scheduler(svc, old_sched); 1327 - old_sched = sched; 1328 - goto out_unlock; 1329 - } 1330 - } 1331 - 1332 - old_pe = svc->pe; 1333 - if (pe != old_pe) { 1334 - ip_vs_unbind_pe(svc); 1335 - ip_vs_bind_pe(svc, pe); 1336 - } 1337 - 1338 - out_unlock: 1339 - write_unlock_bh(&__ip_vs_svc_lock); 1340 1338 out: 1341 1339 ip_vs_scheduler_put(old_sched); 1342 1340 ip_vs_pe_put(old_pe); 1343 1341 return ret; 1344 1342 } 1345 1343 1344 + static void ip_vs_service_rcu_free(struct rcu_head *head) 1345 + { 1346 + struct ip_vs_service *svc; 1347 + 1348 + svc = container_of(head, struct ip_vs_service, rcu_head); 1349 + ip_vs_service_free(svc); 1350 + } 1346 1351 1347 1352 /* 1348 1353 * Delete a service from the service list ··· 1341 1394 ip_vs_stop_estimator(svc->net, &svc->stats); 1342 1395 1343 1396 /* Unbind scheduler */ 1344 - old_sched = svc->scheduler; 1345 - ip_vs_unbind_scheduler(svc); 1397 + old_sched = rcu_dereference_protected(svc->scheduler, 1); 1398 + ip_vs_unbind_scheduler(svc, old_sched); 1346 1399 ip_vs_scheduler_put(old_sched); 1347 1400 1348 - /* Unbind persistence engine */ 1349 - old_pe = svc->pe; 1350 - ip_vs_unbind_pe(svc); 1401 + /* Unbind persistence engine, keep svc->pe */ 1402 + old_pe = rcu_dereference_protected(svc->pe, 1); 1351 1403 ip_vs_pe_put(old_pe); 1352 - 1353 - /* Unbind app inc */ 1354 - if (svc->inc) { 1355 - ip_vs_app_inc_put(svc->inc); 1356 - svc->inc = NULL; 1357 - } 1358 1404 1359 1405 /* 1360 1406 * Unlink the whole destination list ··· 1368 1428 /* 1369 1429 * Free the service if nobody refers to it 1370 1430 */ 1371 - if (atomic_read(&svc->refcnt) == 0) { 1372 - IP_VS_DBG_BUF(3, "Removing service %u/%s:%u usecnt=%d\n", 1431 + if (atomic_dec_and_test(&svc->refcnt)) { 1432 + IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n", 1373 1433 svc->fwmark, 1374 1434 IP_VS_DBG_ADDR(svc->af, &svc->addr), 1375 - ntohs(svc->port), atomic_read(&svc->usecnt)); 1376 - free_percpu(svc->stats.cpustats); 1377 - kfree(svc); 1435 + ntohs(svc->port)); 1436 + call_rcu(&svc->rcu_head, ip_vs_service_rcu_free); 1378 1437 } 1379 1438 1380 1439 /* decrease the module use count */ ··· 1385 1446 */ 1386 1447 static void ip_vs_unlink_service(struct ip_vs_service *svc, bool cleanup) 1387 1448 { 1449 + /* Hold svc to avoid double release from dest_trash */ 1450 + atomic_inc(&svc->refcnt); 1388 1451 /* 1389 1452 * Unhash it from the service table 1390 1453 */ 1391 - write_lock_bh(&__ip_vs_svc_lock); 1392 - 1393 1454 ip_vs_svc_unhash(svc); 1394 1455 1395 - /* 1396 - * Wait until all the svc users go away. 1397 - */ 1398 - IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); 1399 - 1400 1456 __ip_vs_del_service(svc, cleanup); 1401 - 1402 - write_unlock_bh(&__ip_vs_svc_lock); 1403 1457 } 1404 1458 1405 1459 /* ··· 1414 1482 static int ip_vs_flush(struct net *net, bool cleanup) 1415 1483 { 1416 1484 int idx; 1417 - struct ip_vs_service *svc, *nxt; 1485 + struct ip_vs_service *svc; 1486 + struct hlist_node *n; 1418 1487 1419 1488 /* 1420 1489 * Flush the service table hashed by <netns,protocol,addr,port> 1421 1490 */ 1422 1491 for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 1423 - list_for_each_entry_safe(svc, nxt, &ip_vs_svc_table[idx], 1424 - s_list) { 1492 + hlist_for_each_entry_safe(svc, n, &ip_vs_svc_table[idx], 1493 + s_list) { 1425 1494 if (net_eq(svc->net, net)) 1426 1495 ip_vs_unlink_service(svc, cleanup); 1427 1496 } ··· 1432 1499 * Flush the service table hashed by fwmark 1433 1500 */ 1434 1501 for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 1435 - list_for_each_entry_safe(svc, nxt, 1436 - &ip_vs_svc_fwm_table[idx], f_list) { 1502 + hlist_for_each_entry_safe(svc, n, &ip_vs_svc_fwm_table[idx], 1503 + f_list) { 1437 1504 if (net_eq(svc->net, net)) 1438 1505 ip_vs_unlink_service(svc, cleanup); 1439 1506 } ··· 1491 1558 EnterFunction(2); 1492 1559 mutex_lock(&__ip_vs_mutex); 1493 1560 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 1494 - list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { 1561 + hlist_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { 1495 1562 if (net_eq(svc->net, net)) { 1496 1563 list_for_each_entry(dest, &svc->destinations, 1497 1564 n_list) { ··· 1500 1567 } 1501 1568 } 1502 1569 1503 - list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { 1570 + hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { 1504 1571 if (net_eq(svc->net, net)) { 1505 1572 list_for_each_entry(dest, &svc->destinations, 1506 1573 n_list) { ··· 1528 1595 { 1529 1596 struct ip_vs_dest *dest; 1530 1597 1531 - write_lock_bh(&__ip_vs_svc_lock); 1532 1598 list_for_each_entry(dest, &svc->destinations, n_list) { 1533 1599 ip_vs_zero_stats(&dest->stats); 1534 1600 } 1535 1601 ip_vs_zero_stats(&svc->stats); 1536 - write_unlock_bh(&__ip_vs_svc_lock); 1537 1602 return 0; 1538 1603 } 1539 1604 ··· 1541 1610 struct ip_vs_service *svc; 1542 1611 1543 1612 for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 1544 - list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { 1613 + hlist_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { 1545 1614 if (net_eq(svc->net, net)) 1546 1615 ip_vs_zero_service(svc); 1547 1616 } 1548 1617 } 1549 1618 1550 1619 for(idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 1551 - list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { 1620 + hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { 1552 1621 if (net_eq(svc->net, net)) 1553 1622 ip_vs_zero_service(svc); 1554 1623 } ··· 1876 1945 1877 1946 struct ip_vs_iter { 1878 1947 struct seq_net_private p; /* Do not move this, netns depends upon it*/ 1879 - struct list_head *table; 1948 + struct hlist_head *table; 1880 1949 int bucket; 1881 1950 }; 1882 1951 ··· 1909 1978 1910 1979 /* look in hash by protocol */ 1911 1980 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 1912 - list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { 1981 + hlist_for_each_entry_rcu(svc, &ip_vs_svc_table[idx], s_list) { 1913 1982 if (net_eq(svc->net, net) && pos-- == 0) { 1914 1983 iter->table = ip_vs_svc_table; 1915 1984 iter->bucket = idx; ··· 1920 1989 1921 1990 /* keep looking in fwmark */ 1922 1991 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 1923 - list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { 1992 + hlist_for_each_entry_rcu(svc, &ip_vs_svc_fwm_table[idx], 1993 + f_list) { 1924 1994 if (net_eq(svc->net, net) && pos-- == 0) { 1925 1995 iter->table = ip_vs_svc_fwm_table; 1926 1996 iter->bucket = idx; ··· 1934 2002 } 1935 2003 1936 2004 static void *ip_vs_info_seq_start(struct seq_file *seq, loff_t *pos) 1937 - __acquires(__ip_vs_svc_lock) 1938 2005 { 1939 2006 1940 - read_lock_bh(&__ip_vs_svc_lock); 2007 + rcu_read_lock(); 1941 2008 return *pos ? ip_vs_info_array(seq, *pos - 1) : SEQ_START_TOKEN; 1942 2009 } 1943 2010 1944 2011 1945 2012 static void *ip_vs_info_seq_next(struct seq_file *seq, void *v, loff_t *pos) 1946 2013 { 1947 - struct list_head *e; 2014 + struct hlist_node *e; 1948 2015 struct ip_vs_iter *iter; 1949 2016 struct ip_vs_service *svc; 1950 2017 ··· 1956 2025 1957 2026 if (iter->table == ip_vs_svc_table) { 1958 2027 /* next service in table hashed by protocol */ 1959 - if ((e = svc->s_list.next) != &ip_vs_svc_table[iter->bucket]) 1960 - return list_entry(e, struct ip_vs_service, s_list); 1961 - 2028 + e = rcu_dereference(hlist_next_rcu(&svc->s_list)); 2029 + if (e) 2030 + return hlist_entry(e, struct ip_vs_service, s_list); 1962 2031 1963 2032 while (++iter->bucket < IP_VS_SVC_TAB_SIZE) { 1964 - list_for_each_entry(svc,&ip_vs_svc_table[iter->bucket], 1965 - s_list) { 2033 + hlist_for_each_entry_rcu(svc, 2034 + &ip_vs_svc_table[iter->bucket], 2035 + s_list) { 1966 2036 return svc; 1967 2037 } 1968 2038 } ··· 1974 2042 } 1975 2043 1976 2044 /* next service in hashed by fwmark */ 1977 - if ((e = svc->f_list.next) != &ip_vs_svc_fwm_table[iter->bucket]) 1978 - return list_entry(e, struct ip_vs_service, f_list); 2045 + e = rcu_dereference(hlist_next_rcu(&svc->f_list)); 2046 + if (e) 2047 + return hlist_entry(e, struct ip_vs_service, f_list); 1979 2048 1980 2049 scan_fwmark: 1981 2050 while (++iter->bucket < IP_VS_SVC_TAB_SIZE) { 1982 - list_for_each_entry(svc, &ip_vs_svc_fwm_table[iter->bucket], 1983 - f_list) 2051 + hlist_for_each_entry_rcu(svc, 2052 + &ip_vs_svc_fwm_table[iter->bucket], 2053 + f_list) 1984 2054 return svc; 1985 2055 } 1986 2056 ··· 1990 2056 } 1991 2057 1992 2058 static void ip_vs_info_seq_stop(struct seq_file *seq, void *v) 1993 - __releases(__ip_vs_svc_lock) 1994 2059 { 1995 - read_unlock_bh(&__ip_vs_svc_lock); 2060 + rcu_read_unlock(); 1996 2061 } 1997 2062 1998 2063 ··· 2009 2076 const struct ip_vs_service *svc = v; 2010 2077 const struct ip_vs_iter *iter = seq->private; 2011 2078 const struct ip_vs_dest *dest; 2079 + struct ip_vs_scheduler *sched = rcu_dereference(svc->scheduler); 2012 2080 2013 2081 if (iter->table == ip_vs_svc_table) { 2014 2082 #ifdef CONFIG_IP_VS_IPV6 ··· 2018 2084 ip_vs_proto_name(svc->protocol), 2019 2085 &svc->addr.in6, 2020 2086 ntohs(svc->port), 2021 - svc->scheduler->name); 2087 + sched->name); 2022 2088 else 2023 2089 #endif 2024 2090 seq_printf(seq, "%s %08X:%04X %s %s ", 2025 2091 ip_vs_proto_name(svc->protocol), 2026 2092 ntohl(svc->addr.ip), 2027 2093 ntohs(svc->port), 2028 - svc->scheduler->name, 2094 + sched->name, 2029 2095 (svc->flags & IP_VS_SVC_F_ONEPACKET)?"ops ":""); 2030 2096 } else { 2031 2097 seq_printf(seq, "FWM %08X %s %s", 2032 - svc->fwmark, svc->scheduler->name, 2098 + svc->fwmark, sched->name, 2033 2099 (svc->flags & IP_VS_SVC_F_ONEPACKET)?"ops ":""); 2034 2100 } 2035 2101 ··· 2385 2451 } 2386 2452 2387 2453 /* Lookup the exact service by <protocol, addr, port> or fwmark */ 2454 + rcu_read_lock(); 2388 2455 if (usvc.fwmark == 0) 2389 2456 svc = __ip_vs_service_find(net, usvc.af, usvc.protocol, 2390 2457 &usvc.addr, usvc.port); 2391 2458 else 2392 2459 svc = __ip_vs_svc_fwm_find(net, usvc.af, usvc.fwmark); 2460 + rcu_read_unlock(); 2393 2461 2394 2462 if (cmd != IP_VS_SO_SET_ADD 2395 2463 && (svc == NULL || svc->protocol != usvc.protocol)) { ··· 2443 2507 static void 2444 2508 ip_vs_copy_service(struct ip_vs_service_entry *dst, struct ip_vs_service *src) 2445 2509 { 2510 + struct ip_vs_scheduler *sched; 2511 + 2512 + sched = rcu_dereference_protected(src->scheduler, 1); 2446 2513 dst->protocol = src->protocol; 2447 2514 dst->addr = src->addr.ip; 2448 2515 dst->port = src->port; 2449 2516 dst->fwmark = src->fwmark; 2450 - strlcpy(dst->sched_name, src->scheduler->name, sizeof(dst->sched_name)); 2517 + strlcpy(dst->sched_name, sched->name, sizeof(dst->sched_name)); 2451 2518 dst->flags = src->flags; 2452 2519 dst->timeout = src->timeout / HZ; 2453 2520 dst->netmask = src->netmask; ··· 2469 2530 int ret = 0; 2470 2531 2471 2532 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 2472 - list_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { 2533 + hlist_for_each_entry(svc, &ip_vs_svc_table[idx], s_list) { 2473 2534 /* Only expose IPv4 entries to old interface */ 2474 2535 if (svc->af != AF_INET || !net_eq(svc->net, net)) 2475 2536 continue; ··· 2488 2549 } 2489 2550 2490 2551 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 2491 - list_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { 2552 + hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[idx], f_list) { 2492 2553 /* Only expose IPv4 entries to old interface */ 2493 2554 if (svc->af != AF_INET || !net_eq(svc->net, net)) 2494 2555 continue; ··· 2517 2578 union nf_inet_addr addr = { .ip = get->addr }; 2518 2579 int ret = 0; 2519 2580 2581 + rcu_read_lock(); 2520 2582 if (get->fwmark) 2521 2583 svc = __ip_vs_svc_fwm_find(net, AF_INET, get->fwmark); 2522 2584 else 2523 2585 svc = __ip_vs_service_find(net, AF_INET, get->protocol, &addr, 2524 2586 get->port); 2587 + rcu_read_unlock(); 2525 2588 2526 2589 if (svc) { 2527 2590 int count = 0; ··· 2706 2765 2707 2766 entry = (struct ip_vs_service_entry *)arg; 2708 2767 addr.ip = entry->addr; 2768 + rcu_read_lock(); 2709 2769 if (entry->fwmark) 2710 2770 svc = __ip_vs_svc_fwm_find(net, AF_INET, entry->fwmark); 2711 2771 else 2712 2772 svc = __ip_vs_service_find(net, AF_INET, 2713 2773 entry->protocol, &addr, 2714 2774 entry->port); 2775 + rcu_read_unlock(); 2715 2776 if (svc) { 2716 2777 ip_vs_copy_service(entry, svc); 2717 2778 if (copy_to_user(user, entry, sizeof(*entry)) != 0) ··· 2870 2927 static int ip_vs_genl_fill_service(struct sk_buff *skb, 2871 2928 struct ip_vs_service *svc) 2872 2929 { 2930 + struct ip_vs_scheduler *sched; 2873 2931 struct nlattr *nl_service; 2874 2932 struct ip_vs_flags flags = { .flags = svc->flags, 2875 2933 .mask = ~0 }; ··· 2891 2947 goto nla_put_failure; 2892 2948 } 2893 2949 2894 - if (nla_put_string(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name) || 2950 + sched = rcu_dereference_protected(svc->scheduler, 1); 2951 + if (nla_put_string(skb, IPVS_SVC_ATTR_SCHED_NAME, sched->name) || 2895 2952 (svc->pe && 2896 2953 nla_put_string(skb, IPVS_SVC_ATTR_PE_NAME, svc->pe->name)) || 2897 2954 nla_put(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags) || ··· 2943 2998 2944 2999 mutex_lock(&__ip_vs_mutex); 2945 3000 for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) { 2946 - list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) { 3001 + hlist_for_each_entry(svc, &ip_vs_svc_table[i], s_list) { 2947 3002 if (++idx <= start || !net_eq(svc->net, net)) 2948 3003 continue; 2949 3004 if (ip_vs_genl_dump_service(skb, svc, cb) < 0) { ··· 2954 3009 } 2955 3010 2956 3011 for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) { 2957 - list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) { 3012 + hlist_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) { 2958 3013 if (++idx <= start || !net_eq(svc->net, net)) 2959 3014 continue; 2960 3015 if (ip_vs_genl_dump_service(skb, svc, cb) < 0) { ··· 3014 3069 usvc->fwmark = 0; 3015 3070 } 3016 3071 3072 + rcu_read_lock(); 3017 3073 if (usvc->fwmark) 3018 3074 svc = __ip_vs_svc_fwm_find(net, usvc->af, usvc->fwmark); 3019 3075 else 3020 3076 svc = __ip_vs_service_find(net, usvc->af, usvc->protocol, 3021 3077 &usvc->addr, usvc->port); 3078 + rcu_read_unlock(); 3022 3079 *ret_svc = svc; 3023 3080 3024 3081 /* If a full entry was requested, check for the additional fields */ ··· 3852 3905 3853 3906 /* Initialize svc_table, ip_vs_svc_fwm_table */ 3854 3907 for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { 3855 - INIT_LIST_HEAD(&ip_vs_svc_table[idx]); 3856 - INIT_LIST_HEAD(&ip_vs_svc_fwm_table[idx]); 3908 + INIT_HLIST_HEAD(&ip_vs_svc_table[idx]); 3909 + INIT_HLIST_HEAD(&ip_vs_svc_fwm_table[idx]); 3857 3910 } 3858 3911 3859 3912 smp_wmb(); /* Do we really need it now ? */
+1
net/netfilter/ipvs/ip_vs_dh.c
··· 269 269 static void __exit ip_vs_dh_cleanup(void) 270 270 { 271 271 unregister_ip_vs_scheduler(&ip_vs_dh_scheduler); 272 + synchronize_rcu(); 272 273 } 273 274 274 275
+1
net/netfilter/ipvs/ip_vs_lblc.c
··· 633 633 { 634 634 unregister_ip_vs_scheduler(&ip_vs_lblc_scheduler); 635 635 unregister_pernet_subsys(&ip_vs_lblc_ops); 636 + synchronize_rcu(); 636 637 } 637 638 638 639
+1
net/netfilter/ipvs/ip_vs_lblcr.c
··· 821 821 { 822 822 unregister_ip_vs_scheduler(&ip_vs_lblcr_scheduler); 823 823 unregister_pernet_subsys(&ip_vs_lblcr_ops); 824 + synchronize_rcu(); 824 825 } 825 826 826 827
+1
net/netfilter/ipvs/ip_vs_lc.c
··· 84 84 static void __exit ip_vs_lc_cleanup(void) 85 85 { 86 86 unregister_ip_vs_scheduler(&ip_vs_lc_scheduler); 87 + synchronize_rcu(); 87 88 } 88 89 89 90 module_init(ip_vs_lc_init);
+1
net/netfilter/ipvs/ip_vs_nq.c
··· 133 133 static void __exit ip_vs_nq_cleanup(void) 134 134 { 135 135 unregister_ip_vs_scheduler(&ip_vs_nq_scheduler); 136 + synchronize_rcu(); 136 137 } 137 138 138 139 module_init(ip_vs_nq_init);
-12
net/netfilter/ipvs/ip_vs_pe.c
··· 16 16 /* semaphore for IPVS PEs. */ 17 17 static DEFINE_MUTEX(ip_vs_pe_mutex); 18 18 19 - /* Bind a service with a pe */ 20 - void ip_vs_bind_pe(struct ip_vs_service *svc, struct ip_vs_pe *pe) 21 - { 22 - svc->pe = pe; 23 - } 24 - 25 - /* Unbind a service from its pe */ 26 - void ip_vs_unbind_pe(struct ip_vs_service *svc) 27 - { 28 - svc->pe = NULL; 29 - } 30 - 31 19 /* Get pe in the pe list by name */ 32 20 struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name) 33 21 {
+7 -7
net/netfilter/ipvs/ip_vs_proto_sctp.c
··· 27 27 if (sch == NULL) 28 28 return 0; 29 29 net = skb_net(skb); 30 + rcu_read_lock(); 30 31 if ((sch->type == SCTP_CID_INIT) && 31 - (svc = ip_vs_service_get(net, af, skb->mark, iph->protocol, 32 - &iph->daddr, sh->dest))) { 32 + (svc = ip_vs_service_find(net, af, skb->mark, iph->protocol, 33 + &iph->daddr, sh->dest))) { 33 34 int ignored; 34 35 35 36 if (ip_vs_todrop(net_ipvs(net))) { ··· 38 37 * It seems that we are very loaded. 39 38 * We have to drop this packet :( 40 39 */ 41 - ip_vs_service_put(svc); 40 + rcu_read_unlock(); 42 41 *verdict = NF_DROP; 43 42 return 0; 44 43 } ··· 50 49 if (!*cpp && ignored <= 0) { 51 50 if (!ignored) 52 51 *verdict = ip_vs_leave(svc, skb, pd, iph); 53 - else { 54 - ip_vs_service_put(svc); 52 + else 55 53 *verdict = NF_DROP; 56 - } 54 + rcu_read_unlock(); 57 55 return 0; 58 56 } 59 - ip_vs_service_put(svc); 60 57 } 58 + rcu_read_unlock(); 61 59 /* NF_ACCEPT */ 62 60 return 1; 63 61 }
+7 -7
net/netfilter/ipvs/ip_vs_proto_tcp.c
··· 47 47 } 48 48 net = skb_net(skb); 49 49 /* No !th->ack check to allow scheduling on SYN+ACK for Active FTP */ 50 + rcu_read_lock(); 50 51 if (th->syn && 51 - (svc = ip_vs_service_get(net, af, skb->mark, iph->protocol, 52 - &iph->daddr, th->dest))) { 52 + (svc = ip_vs_service_find(net, af, skb->mark, iph->protocol, 53 + &iph->daddr, th->dest))) { 53 54 int ignored; 54 55 55 56 if (ip_vs_todrop(net_ipvs(net))) { ··· 58 57 * It seems that we are very loaded. 59 58 * We have to drop this packet :( 60 59 */ 61 - ip_vs_service_put(svc); 60 + rcu_read_unlock(); 62 61 *verdict = NF_DROP; 63 62 return 0; 64 63 } ··· 71 70 if (!*cpp && ignored <= 0) { 72 71 if (!ignored) 73 72 *verdict = ip_vs_leave(svc, skb, pd, iph); 74 - else { 75 - ip_vs_service_put(svc); 73 + else 76 74 *verdict = NF_DROP; 77 - } 75 + rcu_read_unlock(); 78 76 return 0; 79 77 } 80 - ip_vs_service_put(svc); 81 78 } 79 + rcu_read_unlock(); 82 80 /* NF_ACCEPT */ 83 81 return 1; 84 82 }
+7 -7
net/netfilter/ipvs/ip_vs_proto_udp.c
··· 44 44 return 0; 45 45 } 46 46 net = skb_net(skb); 47 - svc = ip_vs_service_get(net, af, skb->mark, iph->protocol, 48 - &iph->daddr, uh->dest); 47 + rcu_read_lock(); 48 + svc = ip_vs_service_find(net, af, skb->mark, iph->protocol, 49 + &iph->daddr, uh->dest); 49 50 if (svc) { 50 51 int ignored; 51 52 ··· 55 54 * It seems that we are very loaded. 56 55 * We have to drop this packet :( 57 56 */ 58 - ip_vs_service_put(svc); 57 + rcu_read_unlock(); 59 58 *verdict = NF_DROP; 60 59 return 0; 61 60 } ··· 68 67 if (!*cpp && ignored <= 0) { 69 68 if (!ignored) 70 69 *verdict = ip_vs_leave(svc, skb, pd, iph); 71 - else { 72 - ip_vs_service_put(svc); 70 + else 73 71 *verdict = NF_DROP; 74 - } 72 + rcu_read_unlock(); 75 73 return 0; 76 74 } 77 - ip_vs_service_put(svc); 78 75 } 76 + rcu_read_unlock(); 79 77 /* NF_ACCEPT */ 80 78 return 1; 81 79 }
+1
net/netfilter/ipvs/ip_vs_rr.c
··· 121 121 static void __exit ip_vs_rr_cleanup(void) 122 122 { 123 123 unregister_ip_vs_scheduler(&ip_vs_rr_scheduler); 124 + synchronize_rcu(); 124 125 } 125 126 126 127 module_init(ip_vs_rr_init);
+14 -14
net/netfilter/ipvs/ip_vs_sched.c
··· 47 47 { 48 48 int ret; 49 49 50 - svc->scheduler = scheduler; 51 - 52 50 if (scheduler->init_service) { 53 51 ret = scheduler->init_service(svc); 54 52 if (ret) { ··· 54 56 return ret; 55 57 } 56 58 } 57 - 59 + rcu_assign_pointer(svc->scheduler, scheduler); 58 60 return 0; 59 61 } 60 62 ··· 62 64 /* 63 65 * Unbind a service with its scheduler 64 66 */ 65 - void ip_vs_unbind_scheduler(struct ip_vs_service *svc) 67 + void ip_vs_unbind_scheduler(struct ip_vs_service *svc, 68 + struct ip_vs_scheduler *sched) 66 69 { 67 - struct ip_vs_scheduler *sched = svc->scheduler; 70 + struct ip_vs_scheduler *cur_sched; 68 71 69 - if (!sched) 72 + cur_sched = rcu_dereference_protected(svc->scheduler, 1); 73 + /* This check proves that old 'sched' was installed */ 74 + if (!cur_sched) 70 75 return; 71 76 72 77 if (sched->done_service) 73 78 sched->done_service(svc); 74 - 75 - svc->scheduler = NULL; 79 + /* svc->scheduler can not be set to NULL */ 76 80 } 77 81 78 82 ··· 148 148 149 149 void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg) 150 150 { 151 + struct ip_vs_scheduler *sched; 152 + 153 + sched = rcu_dereference(svc->scheduler); 151 154 if (svc->fwmark) { 152 155 IP_VS_ERR_RL("%s: FWM %u 0x%08X - %s\n", 153 - svc->scheduler->name, svc->fwmark, 154 - svc->fwmark, msg); 156 + sched->name, svc->fwmark, svc->fwmark, msg); 155 157 #ifdef CONFIG_IP_VS_IPV6 156 158 } else if (svc->af == AF_INET6) { 157 159 IP_VS_ERR_RL("%s: %s [%pI6c]:%d - %s\n", 158 - svc->scheduler->name, 159 - ip_vs_proto_name(svc->protocol), 160 + sched->name, ip_vs_proto_name(svc->protocol), 160 161 &svc->addr.in6, ntohs(svc->port), msg); 161 162 #endif 162 163 } else { 163 164 IP_VS_ERR_RL("%s: %s %pI4:%d - %s\n", 164 - svc->scheduler->name, 165 - ip_vs_proto_name(svc->protocol), 165 + sched->name, ip_vs_proto_name(svc->protocol), 166 166 &svc->addr.ip, ntohs(svc->port), msg); 167 167 } 168 168 }
+1
net/netfilter/ipvs/ip_vs_sed.c
··· 134 134 static void __exit ip_vs_sed_cleanup(void) 135 135 { 136 136 unregister_ip_vs_scheduler(&ip_vs_sed_scheduler); 137 + synchronize_rcu(); 137 138 } 138 139 139 140 module_init(ip_vs_sed_init);
+1
net/netfilter/ipvs/ip_vs_sh.c
··· 283 283 static void __exit ip_vs_sh_cleanup(void) 284 284 { 285 285 unregister_ip_vs_scheduler(&ip_vs_sh_scheduler); 286 + synchronize_rcu(); 286 287 } 287 288 288 289
+1
net/netfilter/ipvs/ip_vs_wlc.c
··· 106 106 static void __exit ip_vs_wlc_cleanup(void) 107 107 { 108 108 unregister_ip_vs_scheduler(&ip_vs_wlc_scheduler); 109 + synchronize_rcu(); 109 110 } 110 111 111 112 module_init(ip_vs_wlc_init);
+1
net/netfilter/ipvs/ip_vs_wrr.c
··· 261 261 static void __exit ip_vs_wrr_cleanup(void) 262 262 { 263 263 unregister_ip_vs_scheduler(&ip_vs_wrr_scheduler); 264 + synchronize_rcu(); 264 265 } 265 266 266 267 module_init(ip_vs_wrr_init);