aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched')
-rw-r--r--net/sched/Kconfig12
-rw-r--r--net/sched/Makefile11
-rw-r--r--net/sched/act_api.c4
-rw-r--r--net/sched/cls_fw.c31
-rw-r--r--net/sched/sch_api.c1
-rw-r--r--net/sched/sch_generic.c5
-rw-r--r--net/sched/sch_htb.c4
-rw-r--r--net/sched/sch_netem.c131
-rw-r--r--net/sched/simple.c93
9 files changed, 230 insertions, 62 deletions
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 3d1d902dd1a1..b0941186f867 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -185,7 +185,7 @@ config NET_SCH_GRED
185 depends on NET_SCHED 185 depends on NET_SCHED
186 help 186 help
187 Say Y here if you want to use the Generic Random Early Detection 187 Say Y here if you want to use the Generic Random Early Detection
188 (RED) packet scheduling algorithm for some of your network devices 188 (GRED) packet scheduling algorithm for some of your network devices
189 (see the top of <file:net/sched/sch_red.c> for details and 189 (see the top of <file:net/sched/sch_red.c> for details and
190 references about the algorithm). 190 references about the algorithm).
191 191
@@ -506,3 +506,13 @@ config NET_CLS_POLICE
506 Say Y to support traffic policing (bandwidth limits). Needed for 506 Say Y to support traffic policing (bandwidth limits). Needed for
507 ingress and egress rate limiting. 507 ingress and egress rate limiting.
508 508
509config NET_ACT_SIMP
510 tristate "Simple action"
511 depends on NET_CLS_ACT
512 ---help---
513 You must have new iproute2 to use this feature.
514 This adds a very simple action for demonstration purposes
515 The idea is to give action authors a basic example to look at.
516 All this action will do is print on the console the configured
517 policy string followed by _ then packet count.
518
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 431e55786efd..eb3fe583eba8 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -6,13 +6,14 @@ obj-y := sch_generic.o
6 6
7obj-$(CONFIG_NET_SCHED) += sch_api.o sch_fifo.o 7obj-$(CONFIG_NET_SCHED) += sch_api.o sch_fifo.o
8obj-$(CONFIG_NET_CLS) += cls_api.o 8obj-$(CONFIG_NET_CLS) += cls_api.o
9obj-$(CONFIG_NET_CLS_ACT) += act_api.o 9obj-$(CONFIG_NET_CLS_ACT) += act_api.o
10obj-$(CONFIG_NET_ACT_POLICE) += police.o 10obj-$(CONFIG_NET_ACT_POLICE) += police.o
11obj-$(CONFIG_NET_CLS_POLICE) += police.o 11obj-$(CONFIG_NET_CLS_POLICE) += police.o
12obj-$(CONFIG_NET_ACT_GACT) += gact.o 12obj-$(CONFIG_NET_ACT_GACT) += gact.o
13obj-$(CONFIG_NET_ACT_MIRRED) += mirred.o 13obj-$(CONFIG_NET_ACT_MIRRED) += mirred.o
14obj-$(CONFIG_NET_ACT_IPT) += ipt.o 14obj-$(CONFIG_NET_ACT_IPT) += ipt.o
15obj-$(CONFIG_NET_ACT_PEDIT) += pedit.o 15obj-$(CONFIG_NET_ACT_PEDIT) += pedit.o
16obj-$(CONFIG_NET_ACT_SIMP) += simple.o
16obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o 17obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o
17obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o 18obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o
18obj-$(CONFIG_NET_SCH_HPFQ) += sch_hpfq.o 19obj-$(CONFIG_NET_SCH_HPFQ) += sch_hpfq.o
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 5e6cc371b39e..cafcb084098d 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -171,10 +171,10 @@ repeat:
171 skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd); 171 skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);
172 skb->tc_verd = CLR_TC_MUNGED(skb->tc_verd); 172 skb->tc_verd = CLR_TC_MUNGED(skb->tc_verd);
173 } 173 }
174 if (ret != TC_ACT_PIPE)
175 goto exec_done;
176 if (ret == TC_ACT_REPEAT) 174 if (ret == TC_ACT_REPEAT)
177 goto repeat; /* we need a ttl - JHS */ 175 goto repeat; /* we need a ttl - JHS */
176 if (ret != TC_ACT_PIPE)
177 goto exec_done;
178 } 178 }
179 act = a->next; 179 act = a->next;
180 } 180 }
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index fdfc83af3d1f..29d8b9a4d162 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -46,9 +46,11 @@
46#include <net/act_api.h> 46#include <net/act_api.h>
47#include <net/pkt_cls.h> 47#include <net/pkt_cls.h>
48 48
49#define HTSIZE (PAGE_SIZE/sizeof(struct fw_filter *))
50
49struct fw_head 51struct fw_head
50{ 52{
51 struct fw_filter *ht[256]; 53 struct fw_filter *ht[HTSIZE];
52}; 54};
53 55
54struct fw_filter 56struct fw_filter
@@ -69,7 +71,28 @@ static struct tcf_ext_map fw_ext_map = {
69 71
70static __inline__ int fw_hash(u32 handle) 72static __inline__ int fw_hash(u32 handle)
71{ 73{
72 return handle&0xFF; 74 if (HTSIZE == 4096)
75 return ((handle >> 24) & 0xFFF) ^
76 ((handle >> 12) & 0xFFF) ^
77 (handle & 0xFFF);
78 else if (HTSIZE == 2048)
79 return ((handle >> 22) & 0x7FF) ^
80 ((handle >> 11) & 0x7FF) ^
81 (handle & 0x7FF);
82 else if (HTSIZE == 1024)
83 return ((handle >> 20) & 0x3FF) ^
84 ((handle >> 10) & 0x3FF) ^
85 (handle & 0x3FF);
86 else if (HTSIZE == 512)
87 return (handle >> 27) ^
88 ((handle >> 18) & 0x1FF) ^
89 ((handle >> 9) & 0x1FF) ^
90 (handle & 0x1FF);
91 else if (HTSIZE == 256) {
92 u8 *t = (u8 *) &handle;
93 return t[0] ^ t[1] ^ t[2] ^ t[3];
94 } else
95 return handle & (HTSIZE - 1);
73} 96}
74 97
75static int fw_classify(struct sk_buff *skb, struct tcf_proto *tp, 98static int fw_classify(struct sk_buff *skb, struct tcf_proto *tp,
@@ -152,7 +175,7 @@ static void fw_destroy(struct tcf_proto *tp)
152 if (head == NULL) 175 if (head == NULL)
153 return; 176 return;
154 177
155 for (h=0; h<256; h++) { 178 for (h=0; h<HTSIZE; h++) {
156 while ((f=head->ht[h]) != NULL) { 179 while ((f=head->ht[h]) != NULL) {
157 head->ht[h] = f->next; 180 head->ht[h] = f->next;
158 fw_delete_filter(tp, f); 181 fw_delete_filter(tp, f);
@@ -291,7 +314,7 @@ static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
291 if (arg->stop) 314 if (arg->stop)
292 return; 315 return;
293 316
294 for (h = 0; h < 256; h++) { 317 for (h = 0; h < HTSIZE; h++) {
295 struct fw_filter *f; 318 struct fw_filter *f;
296 319
297 for (f = head->ht[h]; f; f = f->next) { 320 for (f = head->ht[h]; f; f = f->next) {
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 4323a74eea30..07977f8f2679 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1289,6 +1289,7 @@ static int __init pktsched_init(void)
1289 1289
1290subsys_initcall(pktsched_init); 1290subsys_initcall(pktsched_init);
1291 1291
1292EXPORT_SYMBOL(qdisc_lookup);
1292EXPORT_SYMBOL(qdisc_get_rtab); 1293EXPORT_SYMBOL(qdisc_get_rtab);
1293EXPORT_SYMBOL(qdisc_put_rtab); 1294EXPORT_SYMBOL(qdisc_put_rtab);
1294EXPORT_SYMBOL(register_qdisc); 1295EXPORT_SYMBOL(register_qdisc);
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 8c01e023f02e..87e48a4e1051 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -179,6 +179,7 @@ requeue:
179 netif_schedule(dev); 179 netif_schedule(dev);
180 return 1; 180 return 1;
181 } 181 }
182 BUG_ON((int) q->q.qlen < 0);
182 return q->q.qlen; 183 return q->q.qlen;
183} 184}
184 185
@@ -539,6 +540,10 @@ void dev_activate(struct net_device *dev)
539 write_unlock_bh(&qdisc_tree_lock); 540 write_unlock_bh(&qdisc_tree_lock);
540 } 541 }
541 542
543 if (!netif_carrier_ok(dev))
544 /* Delay activation until next carrier-on event */
545 return;
546
542 spin_lock_bh(&dev->queue_lock); 547 spin_lock_bh(&dev->queue_lock);
543 rcu_assign_pointer(dev->qdisc, dev->qdisc_sleeping); 548 rcu_assign_pointer(dev->qdisc, dev->qdisc_sleeping);
544 if (dev->qdisc != &noqueue_qdisc) { 549 if (dev->qdisc != &noqueue_qdisc) {
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index a85935e7d53d..558cc087e602 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -717,6 +717,10 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch)
717 if (q->direct_queue.qlen < q->direct_qlen) { 717 if (q->direct_queue.qlen < q->direct_qlen) {
718 __skb_queue_tail(&q->direct_queue, skb); 718 __skb_queue_tail(&q->direct_queue, skb);
719 q->direct_pkts++; 719 q->direct_pkts++;
720 } else {
721 kfree_skb(skb);
722 sch->qstats.drops++;
723 return NET_XMIT_DROP;
720 } 724 }
721#ifdef CONFIG_NET_CLS_ACT 725#ifdef CONFIG_NET_CLS_ACT
722 } else if (!cl) { 726 } else if (!cl) {
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 31c29deb139d..e0c9fbe73b15 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -138,38 +138,77 @@ static long tabledist(unsigned long mu, long sigma,
138} 138}
139 139
140/* Put skb in the private delayed queue. */ 140/* Put skb in the private delayed queue. */
141static int delay_skb(struct Qdisc *sch, struct sk_buff *skb) 141static int netem_delay(struct Qdisc *sch, struct sk_buff *skb)
142{ 142{
143 struct netem_sched_data *q = qdisc_priv(sch); 143 struct netem_sched_data *q = qdisc_priv(sch);
144 struct netem_skb_cb *cb = (struct netem_skb_cb *)skb->cb;
145 psched_tdiff_t td; 144 psched_tdiff_t td;
146 psched_time_t now; 145 psched_time_t now;
147 146
148 PSCHED_GET_TIME(now); 147 PSCHED_GET_TIME(now);
149 td = tabledist(q->latency, q->jitter, &q->delay_cor, q->delay_dist); 148 td = tabledist(q->latency, q->jitter, &q->delay_cor, q->delay_dist);
150 PSCHED_TADD2(now, td, cb->time_to_send);
151 149
152 /* Always queue at tail to keep packets in order */ 150 /* Always queue at tail to keep packets in order */
153 if (likely(q->delayed.qlen < q->limit)) { 151 if (likely(q->delayed.qlen < q->limit)) {
152 struct netem_skb_cb *cb = (struct netem_skb_cb *)skb->cb;
153
154 PSCHED_TADD2(now, td, cb->time_to_send);
155
156 pr_debug("netem_delay: skb=%p now=%llu tosend=%llu\n", skb,
157 now, cb->time_to_send);
158
154 __skb_queue_tail(&q->delayed, skb); 159 __skb_queue_tail(&q->delayed, skb);
155 if (!timer_pending(&q->timer)) {
156 q->timer.expires = jiffies + PSCHED_US2JIFFIE(td);
157 add_timer(&q->timer);
158 }
159 return NET_XMIT_SUCCESS; 160 return NET_XMIT_SUCCESS;
160 } 161 }
161 162
163 pr_debug("netem_delay: queue over limit %d\n", q->limit);
164 sch->qstats.overlimits++;
162 kfree_skb(skb); 165 kfree_skb(skb);
163 return NET_XMIT_DROP; 166 return NET_XMIT_DROP;
164} 167}
165 168
169/*
170 * Move a packet that is ready to send from the delay holding
171 * list to the underlying qdisc.
172 */
173static int netem_run(struct Qdisc *sch)
174{
175 struct netem_sched_data *q = qdisc_priv(sch);
176 struct sk_buff *skb;
177 psched_time_t now;
178
179 PSCHED_GET_TIME(now);
180
181 skb = skb_peek(&q->delayed);
182 if (skb) {
183 const struct netem_skb_cb *cb
184 = (const struct netem_skb_cb *)skb->cb;
185 long delay
186 = PSCHED_US2JIFFIE(PSCHED_TDIFF(cb->time_to_send, now));
187 pr_debug("netem_run: skb=%p delay=%ld\n", skb, delay);
188
189 /* if more time remaining? */
190 if (delay > 0) {
191 mod_timer(&q->timer, jiffies + delay);
192 return 1;
193 }
194
195 __skb_unlink(skb, &q->delayed);
196
197 if (q->qdisc->enqueue(skb, q->qdisc)) {
198 sch->q.qlen--;
199 sch->qstats.drops++;
200 }
201 }
202
203 return 0;
204}
205
166static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) 206static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
167{ 207{
168 struct netem_sched_data *q = qdisc_priv(sch); 208 struct netem_sched_data *q = qdisc_priv(sch);
169 struct sk_buff *skb2;
170 int ret; 209 int ret;
171 210
172 pr_debug("netem_enqueue skb=%p @%lu\n", skb, jiffies); 211 pr_debug("netem_enqueue skb=%p\n", skb);
173 212
174 /* Random packet drop 0 => none, ~0 => all */ 213 /* Random packet drop 0 => none, ~0 => all */
175 if (q->loss && q->loss >= get_crandom(&q->loss_cor)) { 214 if (q->loss && q->loss >= get_crandom(&q->loss_cor)) {
@@ -180,11 +219,21 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
180 } 219 }
181 220
182 /* Random duplication */ 221 /* Random duplication */
183 if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor) 222 if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor)) {
184 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) { 223 struct sk_buff *skb2;
185 pr_debug("netem_enqueue: dup %p\n", skb2); 224
225 skb2 = skb_clone(skb, GFP_ATOMIC);
226 if (skb2 && netem_delay(sch, skb2) == NET_XMIT_SUCCESS) {
227 struct Qdisc *qp;
228
229 /* Since one packet can generate two packets in the
230 * queue, the parent's qlen accounting gets confused,
231 * so fix it.
232 */
233 qp = qdisc_lookup(sch->dev, TC_H_MAJ(sch->parent));
234 if (qp)
235 qp->q.qlen++;
186 236
187 if (delay_skb(sch, skb2)) {
188 sch->q.qlen++; 237 sch->q.qlen++;
189 sch->bstats.bytes += skb2->len; 238 sch->bstats.bytes += skb2->len;
190 sch->bstats.packets++; 239 sch->bstats.packets++;
@@ -202,7 +251,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
202 ret = q->qdisc->enqueue(skb, q->qdisc); 251 ret = q->qdisc->enqueue(skb, q->qdisc);
203 } else { 252 } else {
204 q->counter = 0; 253 q->counter = 0;
205 ret = delay_skb(sch, skb); 254 ret = netem_delay(sch, skb);
255 netem_run(sch);
206 } 256 }
207 257
208 if (likely(ret == NET_XMIT_SUCCESS)) { 258 if (likely(ret == NET_XMIT_SUCCESS)) {
@@ -212,6 +262,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
212 } else 262 } else
213 sch->qstats.drops++; 263 sch->qstats.drops++;
214 264
265 pr_debug("netem: enqueue ret %d\n", ret);
215 return ret; 266 return ret;
216} 267}
217 268
@@ -241,56 +292,35 @@ static unsigned int netem_drop(struct Qdisc* sch)
241 return len; 292 return len;
242} 293}
243 294
244/* Dequeue packet.
245 * Move all packets that are ready to send from the delay holding
246 * list to the underlying qdisc, then just call dequeue
247 */
248static struct sk_buff *netem_dequeue(struct Qdisc *sch) 295static struct sk_buff *netem_dequeue(struct Qdisc *sch)
249{ 296{
250 struct netem_sched_data *q = qdisc_priv(sch); 297 struct netem_sched_data *q = qdisc_priv(sch);
251 struct sk_buff *skb; 298 struct sk_buff *skb;
299 int pending;
300
301 pending = netem_run(sch);
252 302
253 skb = q->qdisc->dequeue(q->qdisc); 303 skb = q->qdisc->dequeue(q->qdisc);
254 if (skb) 304 if (skb) {
305 pr_debug("netem_dequeue: return skb=%p\n", skb);
255 sch->q.qlen--; 306 sch->q.qlen--;
307 sch->flags &= ~TCQ_F_THROTTLED;
308 }
309 else if (pending) {
310 pr_debug("netem_dequeue: throttling\n");
311 sch->flags |= TCQ_F_THROTTLED;
312 }
313
256 return skb; 314 return skb;
257} 315}
258 316
259static void netem_watchdog(unsigned long arg) 317static void netem_watchdog(unsigned long arg)
260{ 318{
261 struct Qdisc *sch = (struct Qdisc *)arg; 319 struct Qdisc *sch = (struct Qdisc *)arg;
262 struct netem_sched_data *q = qdisc_priv(sch);
263 struct net_device *dev = sch->dev;
264 struct sk_buff *skb;
265 psched_time_t now;
266
267 pr_debug("netem_watchdog: fired @%lu\n", jiffies);
268
269 spin_lock_bh(&dev->queue_lock);
270 PSCHED_GET_TIME(now);
271
272 while ((skb = skb_peek(&q->delayed)) != NULL) {
273 const struct netem_skb_cb *cb
274 = (const struct netem_skb_cb *)skb->cb;
275 long delay
276 = PSCHED_US2JIFFIE(PSCHED_TDIFF(cb->time_to_send, now));
277 pr_debug("netem_watchdog: skb %p@%lu %ld\n",
278 skb, jiffies, delay);
279 320
280 /* if more time remaining? */ 321 pr_debug("netem_watchdog qlen=%d\n", sch->q.qlen);
281 if (delay > 0) { 322 sch->flags &= ~TCQ_F_THROTTLED;
282 mod_timer(&q->timer, jiffies + delay); 323 netif_schedule(sch->dev);
283 break;
284 }
285 __skb_unlink(skb, &q->delayed);
286
287 if (q->qdisc->enqueue(skb, q->qdisc)) {
288 sch->q.qlen--;
289 sch->qstats.drops++;
290 }
291 }
292 qdisc_run(dev);
293 spin_unlock_bh(&dev->queue_lock);
294} 324}
295 325
296static void netem_reset(struct Qdisc *sch) 326static void netem_reset(struct Qdisc *sch)
@@ -301,6 +331,7 @@ static void netem_reset(struct Qdisc *sch)
301 skb_queue_purge(&q->delayed); 331 skb_queue_purge(&q->delayed);
302 332
303 sch->q.qlen = 0; 333 sch->q.qlen = 0;
334 sch->flags &= ~TCQ_F_THROTTLED;
304 del_timer_sync(&q->timer); 335 del_timer_sync(&q->timer);
305} 336}
306 337
diff --git a/net/sched/simple.c b/net/sched/simple.c
new file mode 100644
index 000000000000..3ab4c675ab5d
--- /dev/null
+++ b/net/sched/simple.c
@@ -0,0 +1,93 @@
1/*
2 * net/sched/simp.c Simple example of an action
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Jamal Hadi Salim (2005)
10 *
11 */
12
13#include <linux/config.h>
14#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/kernel.h>
17#include <linux/netdevice.h>
18#include <linux/skbuff.h>
19#include <linux/rtnetlink.h>
20#include <net/pkt_sched.h>
21
22#define TCA_ACT_SIMP 22
23
24/* XXX: Hide all these common elements under some macro
25 * probably
26*/
27#include <linux/tc_act/tc_defact.h>
28#include <net/tc_act/tc_defact.h>
29
30/* use generic hash table with 8 buckets */
31#define MY_TAB_SIZE 8
32#define MY_TAB_MASK (MY_TAB_SIZE - 1)
33static u32 idx_gen;
34static struct tcf_defact *tcf_simp_ht[MY_TAB_SIZE];
35static DEFINE_RWLOCK(simp_lock);
36
37/* override the defaults */
38#define tcf_st tcf_defact
39#define tc_st tc_defact
40#define tcf_t_lock simp_lock
41#define tcf_ht tcf_simp_ht
42
43#define CONFIG_NET_ACT_INIT 1
44#include <net/pkt_act.h>
45#include <net/act_generic.h>
46
47static int tcf_simp(struct sk_buff **pskb, struct tc_action *a)
48{
49 struct sk_buff *skb = *pskb;
50 struct tcf_defact *p = PRIV(a, defact);
51
52 spin_lock(&p->lock);
53 p->tm.lastuse = jiffies;
54 p->bstats.bytes += skb->len;
55 p->bstats.packets++;
56
57 /* print policy string followed by _ then packet count
58 * Example if this was the 3rd packet and the string was "hello"
59 * then it would look like "hello_3" (without quotes)
60 **/
61 printk("simple: %s_%d\n", (char *)p->defdata, p->bstats.packets);
62 spin_unlock(&p->lock);
63 return p->action;
64}
65
66static struct tc_action_ops act_simp_ops = {
67 .kind = "simple",
68 .type = TCA_ACT_SIMP,
69 .capab = TCA_CAP_NONE,
70 .owner = THIS_MODULE,
71 .act = tcf_simp,
72 tca_use_default_ops
73};
74
75MODULE_AUTHOR("Jamal Hadi Salim(2005)");
76MODULE_DESCRIPTION("Simple example action");
77MODULE_LICENSE("GPL");
78
79static int __init simp_init_module(void)
80{
81 int ret = tcf_register_action(&act_simp_ops);
82 if (!ret)
83 printk("Simple TC action Loaded\n");
84 return ret;
85}
86
87static void __exit simp_cleanup_module(void)
88{
89 tcf_unregister_action(&act_simp_ops);
90}
91
92module_init(simp_init_module);
93module_exit(simp_cleanup_module);