1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #define KMSG_COMPONENT "IPVS" |
3 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
4 | |
5 | #include <linux/module.h> |
6 | #include <linux/spinlock.h> |
7 | #include <linux/interrupt.h> |
8 | #include <asm/string.h> |
9 | #include <linux/kmod.h> |
10 | #include <linux/sysctl.h> |
11 | |
12 | #include <net/ip_vs.h> |
13 | |
14 | /* IPVS pe list */ |
15 | static LIST_HEAD(ip_vs_pe); |
16 | |
17 | /* semaphore for IPVS PEs. */ |
18 | static DEFINE_MUTEX(ip_vs_pe_mutex); |
19 | |
20 | /* Get pe in the pe list by name */ |
21 | struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name) |
22 | { |
23 | struct ip_vs_pe *pe; |
24 | |
25 | IP_VS_DBG(10, "%s(): pe_name \"%s\"\n" , __func__, |
26 | pe_name); |
27 | |
28 | rcu_read_lock(); |
29 | list_for_each_entry_rcu(pe, &ip_vs_pe, n_list) { |
30 | /* Test and get the modules atomically */ |
31 | if (pe->module && |
32 | !try_module_get(module: pe->module)) { |
33 | /* This pe is just deleted */ |
34 | continue; |
35 | } |
36 | if (strcmp(pe_name, pe->name)==0) { |
37 | /* HIT */ |
38 | rcu_read_unlock(); |
39 | return pe; |
40 | } |
41 | module_put(module: pe->module); |
42 | } |
43 | rcu_read_unlock(); |
44 | |
45 | return NULL; |
46 | } |
47 | |
48 | /* Lookup pe and try to load it if it doesn't exist */ |
49 | struct ip_vs_pe *ip_vs_pe_getbyname(const char *name) |
50 | { |
51 | struct ip_vs_pe *pe; |
52 | |
53 | /* Search for the pe by name */ |
54 | pe = __ip_vs_pe_getbyname(pe_name: name); |
55 | |
56 | /* If pe not found, load the module and search again */ |
57 | if (!pe) { |
58 | request_module("ip_vs_pe_%s" , name); |
59 | pe = __ip_vs_pe_getbyname(pe_name: name); |
60 | } |
61 | |
62 | return pe; |
63 | } |
64 | |
65 | /* Register a pe in the pe list */ |
66 | int register_ip_vs_pe(struct ip_vs_pe *pe) |
67 | { |
68 | struct ip_vs_pe *tmp; |
69 | |
70 | /* increase the module use count */ |
71 | if (!ip_vs_use_count_inc()) |
72 | return -ENOENT; |
73 | |
74 | mutex_lock(&ip_vs_pe_mutex); |
75 | /* Make sure that the pe with this name doesn't exist |
76 | * in the pe list. |
77 | */ |
78 | list_for_each_entry(tmp, &ip_vs_pe, n_list) { |
79 | if (strcmp(tmp->name, pe->name) == 0) { |
80 | mutex_unlock(lock: &ip_vs_pe_mutex); |
81 | ip_vs_use_count_dec(); |
82 | pr_err("%s(): [%s] pe already existed " |
83 | "in the system\n" , __func__, pe->name); |
84 | return -EINVAL; |
85 | } |
86 | } |
87 | /* Add it into the d-linked pe list */ |
88 | list_add_rcu(new: &pe->n_list, head: &ip_vs_pe); |
89 | mutex_unlock(lock: &ip_vs_pe_mutex); |
90 | |
91 | pr_info("[%s] pe registered.\n" , pe->name); |
92 | |
93 | return 0; |
94 | } |
95 | EXPORT_SYMBOL_GPL(register_ip_vs_pe); |
96 | |
97 | /* Unregister a pe from the pe list */ |
98 | int unregister_ip_vs_pe(struct ip_vs_pe *pe) |
99 | { |
100 | mutex_lock(&ip_vs_pe_mutex); |
101 | /* Remove it from the d-linked pe list */ |
102 | list_del_rcu(entry: &pe->n_list); |
103 | mutex_unlock(lock: &ip_vs_pe_mutex); |
104 | |
105 | /* decrease the module use count */ |
106 | ip_vs_use_count_dec(); |
107 | |
108 | pr_info("[%s] pe unregistered.\n" , pe->name); |
109 | |
110 | return 0; |
111 | } |
112 | EXPORT_SYMBOL_GPL(unregister_ip_vs_pe); |
113 | |