aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter/ipvs/ip_vs_pe.c
blob: 5cf859ccb31bbe096c9588643a9ed0f0d5f13342 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#define KMSG_COMPONENT "IPVS"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt

#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/string.h>
#include <linux/kmod.h>
#include <linux/sysctl.h>

#include <net/ip_vs.h>

/* IPVS pe list */
static LIST_HEAD(ip_vs_pe);

/* lock for service table */
static DEFINE_SPINLOCK(ip_vs_pe_lock);

/* Bind a service with a pe */
void ip_vs_bind_pe(struct ip_vs_service *svc, struct ip_vs_pe *pe)
{
	svc->pe = pe;
}

/* Unbind a service from its pe */
void ip_vs_unbind_pe(struct ip_vs_service *svc)
{
	svc->pe = NULL;
}

/* Get pe in the pe list by name */
struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name)
{
	struct ip_vs_pe *pe;

	IP_VS_DBG(10, "%s(): pe_name \"%s\"\n", __func__,
		  pe_name);

	spin_lock_bh(&ip_vs_pe_lock);

	list_for_each_entry(pe, &ip_vs_pe, n_list) {
		/* Test and get the modules atomically */
		if (pe->module &&
		    !try_module_get(pe->module)) {
			/* This pe is just deleted */
			continue;
		}
		if (strcmp(pe_name, pe->name)==0) {
			/* HIT */
			spin_unlock_bh(&ip_vs_pe_lock);
			return pe;
		}
		if (pe->module)
			module_put(pe->module);
	}

	spin_unlock_bh(&ip_vs_pe_lock);
	return NULL;
}

/* Lookup pe and try to load it if it doesn't exist */
struct ip_vs_pe *ip_vs_pe_getbyname(const char *name)
{
	struct ip_vs_pe *pe;

	/* Search for the pe by name */
	pe = __ip_vs_pe_getbyname(name);

	/* If pe not found, load the module and search again */
	if (!pe) {
		request_module("ip_vs_pe_%s", name);
		pe = __ip_vs_pe_getbyname(name);
	}

	return pe;
}

/* Register a pe in the pe list */
int register_ip_vs_pe(struct ip_vs_pe *pe)
{
	struct ip_vs_pe *tmp;

	/* increase the module use count */
	ip_vs_use_count_inc();

	spin_lock_bh(&ip_vs_pe_lock);

	if (!list_empty(&pe->n_list)) {
		spin_unlock_bh(&ip_vs_pe_lock);
		ip_vs_use_count_dec();
		pr_err("%s(): [%s] pe already linked\n",
		       __func__, pe->name);
		return -EINVAL;
	}

	/* Make sure that the pe with this name doesn't exist
	 * in the pe list.
	 */
	list_for_each_entry(tmp, &ip_vs_pe, n_list) {
		if (strcmp(tmp->name, pe->name) == 0) {
			spin_unlock_bh(&ip_vs_pe_lock);
			ip_vs_use_count_dec();
			pr_err("%s(): [%s] pe already existed "
			       "in the system\n", __func__, pe->name);
			return -EINVAL;
		}
	}
	/* Add it into the d-linked pe list */
	list_add(&pe->n_list, &ip_vs_pe);
	spin_unlock_bh(&ip_vs_pe_lock);

	pr_info("[%s] pe registered.\n", pe->name);

	return 0;
}
EXPORT_SYMBOL_GPL(register_ip_vs_pe);

/* Unregister a pe from the pe list */
int unregister_ip_vs_pe(struct ip_vs_pe *pe)
{
	spin_lock_bh(&ip_vs_pe_lock);
	if (list_empty(&pe->n_list)) {
		spin_unlock_bh(&ip_vs_pe_lock);
		pr_err("%s(): [%s] pe is not in the list. failed\n",
		       __func__, pe->name);
		return -EINVAL;
	}

	/* Remove it from the d-linked pe list */
	list_del(&pe->n_list);
	spin_unlock_bh(&ip_vs_pe_lock);

	/* decrease the module use count */
	ip_vs_use_count_dec();

	pr_info("[%s] pe unregistered.\n", pe->name);

	return 0;
}
EXPORT_SYMBOL_GPL(unregister_ip_vs_pe);