Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/atm/mpoa_caches.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <linux/types.h>
3
#include <linux/atmmpc.h>
4
#include <linux/slab.h>
5
#include <linux/time.h>
6
7
#include "mpoa_caches.h"
8
#include "mpc.h"
9
10
/*
11
* mpoa_caches.c: Implementation of ingress and egress cache
12
* handling functions
13
*/
14
15
#if 0
16
#define dprintk(format, args...) \
17
printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */
18
#else
19
#define dprintk(format, args...) \
20
do { if (0) \
21
printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\
22
} while (0)
23
#endif
24
25
#if 0
26
#define ddprintk(format, args...) \
27
printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args) /* debug */
28
#else
29
#define ddprintk(format, args...) \
30
do { if (0) \
31
printk(KERN_DEBUG "mpoa:%s: " format, __FILE__, ##args);\
32
} while (0)
33
#endif
34
35
static in_cache_entry *in_cache_get(__be32 dst_ip,
36
struct mpoa_client *client)
37
{
38
in_cache_entry *entry;
39
40
read_lock_bh(&client->ingress_lock);
41
entry = client->in_cache;
42
while (entry != NULL) {
43
if (entry->ctrl_info.in_dst_ip == dst_ip) {
44
refcount_inc(&entry->use);
45
read_unlock_bh(&client->ingress_lock);
46
return entry;
47
}
48
entry = entry->next;
49
}
50
read_unlock_bh(&client->ingress_lock);
51
52
return NULL;
53
}
54
55
static in_cache_entry *in_cache_get_with_mask(__be32 dst_ip,
56
struct mpoa_client *client,
57
__be32 mask)
58
{
59
in_cache_entry *entry;
60
61
read_lock_bh(&client->ingress_lock);
62
entry = client->in_cache;
63
while (entry != NULL) {
64
if ((entry->ctrl_info.in_dst_ip & mask) == (dst_ip & mask)) {
65
refcount_inc(&entry->use);
66
read_unlock_bh(&client->ingress_lock);
67
return entry;
68
}
69
entry = entry->next;
70
}
71
read_unlock_bh(&client->ingress_lock);
72
73
return NULL;
74
75
}
76
77
static in_cache_entry *in_cache_get_by_vcc(struct atm_vcc *vcc,
78
struct mpoa_client *client)
79
{
80
in_cache_entry *entry;
81
82
read_lock_bh(&client->ingress_lock);
83
entry = client->in_cache;
84
while (entry != NULL) {
85
if (entry->shortcut == vcc) {
86
refcount_inc(&entry->use);
87
read_unlock_bh(&client->ingress_lock);
88
return entry;
89
}
90
entry = entry->next;
91
}
92
read_unlock_bh(&client->ingress_lock);
93
94
return NULL;
95
}
96
97
static in_cache_entry *in_cache_add_entry(__be32 dst_ip,
98
struct mpoa_client *client)
99
{
100
in_cache_entry *entry = kzalloc(sizeof(in_cache_entry), GFP_KERNEL);
101
102
if (entry == NULL) {
103
pr_info("mpoa: mpoa_caches.c: new_in_cache_entry: out of memory\n");
104
return NULL;
105
}
106
107
dprintk("adding an ingress entry, ip = %pI4\n", &dst_ip);
108
109
refcount_set(&entry->use, 1);
110
dprintk("new_in_cache_entry: about to lock\n");
111
write_lock_bh(&client->ingress_lock);
112
entry->next = client->in_cache;
113
entry->prev = NULL;
114
if (client->in_cache != NULL)
115
client->in_cache->prev = entry;
116
client->in_cache = entry;
117
118
memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
119
entry->ctrl_info.in_dst_ip = dst_ip;
120
entry->time = ktime_get_seconds();
121
entry->retry_time = client->parameters.mpc_p4;
122
entry->count = 1;
123
entry->entry_state = INGRESS_INVALID;
124
entry->ctrl_info.holding_time = HOLDING_TIME_DEFAULT;
125
refcount_inc(&entry->use);
126
127
write_unlock_bh(&client->ingress_lock);
128
dprintk("new_in_cache_entry: unlocked\n");
129
130
return entry;
131
}
132
133
static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc)
134
{
135
struct atm_mpoa_qos *qos;
136
struct k_message msg;
137
138
entry->count++;
139
if (entry->entry_state == INGRESS_RESOLVED && entry->shortcut != NULL)
140
return OPEN;
141
142
if (entry->entry_state == INGRESS_REFRESHING) {
143
if (entry->count > mpc->parameters.mpc_p1) {
144
msg.type = SND_MPOA_RES_RQST;
145
msg.content.in_info = entry->ctrl_info;
146
memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
147
qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
148
if (qos != NULL)
149
msg.qos = qos->qos;
150
msg_to_mpoad(&msg, mpc);
151
entry->reply_wait = ktime_get_seconds();
152
entry->entry_state = INGRESS_RESOLVING;
153
}
154
if (entry->shortcut != NULL)
155
return OPEN;
156
return CLOSED;
157
}
158
159
if (entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL)
160
return OPEN;
161
162
if (entry->count > mpc->parameters.mpc_p1 &&
163
entry->entry_state == INGRESS_INVALID) {
164
dprintk("(%s) threshold exceeded for ip %pI4, sending MPOA res req\n",
165
mpc->dev->name, &entry->ctrl_info.in_dst_ip);
166
entry->entry_state = INGRESS_RESOLVING;
167
msg.type = SND_MPOA_RES_RQST;
168
memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
169
msg.content.in_info = entry->ctrl_info;
170
qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
171
if (qos != NULL)
172
msg.qos = qos->qos;
173
msg_to_mpoad(&msg, mpc);
174
entry->reply_wait = ktime_get_seconds();
175
}
176
177
return CLOSED;
178
}
179
180
static void in_cache_put(in_cache_entry *entry)
181
{
182
if (refcount_dec_and_test(&entry->use)) {
183
kfree_sensitive(entry);
184
}
185
}
186
187
/*
188
* This should be called with write lock on
189
*/
190
static void in_cache_remove_entry(in_cache_entry *entry,
191
struct mpoa_client *client)
192
{
193
struct atm_vcc *vcc;
194
struct k_message msg;
195
196
vcc = entry->shortcut;
197
dprintk("removing an ingress entry, ip = %pI4\n",
198
&entry->ctrl_info.in_dst_ip);
199
200
if (entry->prev != NULL)
201
entry->prev->next = entry->next;
202
else
203
client->in_cache = entry->next;
204
if (entry->next != NULL)
205
entry->next->prev = entry->prev;
206
client->in_ops->put(entry);
207
if (client->in_cache == NULL && client->eg_cache == NULL) {
208
msg.type = STOP_KEEP_ALIVE_SM;
209
msg_to_mpoad(&msg, client);
210
}
211
212
/* Check if the egress side still uses this VCC */
213
if (vcc != NULL) {
214
eg_cache_entry *eg_entry = client->eg_ops->get_by_vcc(vcc,
215
client);
216
if (eg_entry != NULL) {
217
client->eg_ops->put(eg_entry);
218
return;
219
}
220
vcc_release_async(vcc, -EPIPE);
221
}
222
}
223
224
/* Call this every MPC-p2 seconds... Not exactly correct solution,
225
but an easy one... */
226
static void clear_count_and_expired(struct mpoa_client *client)
227
{
228
in_cache_entry *entry, *next_entry;
229
time64_t now;
230
231
now = ktime_get_seconds();
232
233
write_lock_bh(&client->ingress_lock);
234
entry = client->in_cache;
235
while (entry != NULL) {
236
entry->count = 0;
237
next_entry = entry->next;
238
if ((now - entry->time) > entry->ctrl_info.holding_time) {
239
dprintk("holding time expired, ip = %pI4\n",
240
&entry->ctrl_info.in_dst_ip);
241
client->in_ops->remove_entry(entry, client);
242
}
243
entry = next_entry;
244
}
245
write_unlock_bh(&client->ingress_lock);
246
}
247
248
/* Call this every MPC-p4 seconds. */
249
static void check_resolving_entries(struct mpoa_client *client)
250
{
251
252
struct atm_mpoa_qos *qos;
253
in_cache_entry *entry;
254
time64_t now;
255
struct k_message msg;
256
257
now = ktime_get_seconds();
258
259
read_lock_bh(&client->ingress_lock);
260
entry = client->in_cache;
261
while (entry != NULL) {
262
if (entry->entry_state == INGRESS_RESOLVING) {
263
264
if ((now - entry->hold_down)
265
< client->parameters.mpc_p6) {
266
entry = entry->next; /* Entry in hold down */
267
continue;
268
}
269
if ((now - entry->reply_wait) > entry->retry_time) {
270
entry->retry_time = MPC_C1 * (entry->retry_time);
271
/*
272
* Retry time maximum exceeded,
273
* put entry in hold down.
274
*/
275
if (entry->retry_time > client->parameters.mpc_p5) {
276
entry->hold_down = ktime_get_seconds();
277
entry->retry_time = client->parameters.mpc_p4;
278
entry = entry->next;
279
continue;
280
}
281
/* Ask daemon to send a resolution request. */
282
memset(&entry->hold_down, 0, sizeof(time64_t));
283
msg.type = SND_MPOA_RES_RTRY;
284
memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN);
285
msg.content.in_info = entry->ctrl_info;
286
qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
287
if (qos != NULL)
288
msg.qos = qos->qos;
289
msg_to_mpoad(&msg, client);
290
entry->reply_wait = ktime_get_seconds();
291
}
292
}
293
entry = entry->next;
294
}
295
read_unlock_bh(&client->ingress_lock);
296
}
297
298
/* Call this every MPC-p5 seconds. */
299
static void refresh_entries(struct mpoa_client *client)
300
{
301
time64_t now;
302
struct in_cache_entry *entry = client->in_cache;
303
304
ddprintk("refresh_entries\n");
305
now = ktime_get_seconds();
306
307
read_lock_bh(&client->ingress_lock);
308
while (entry != NULL) {
309
if (entry->entry_state == INGRESS_RESOLVED) {
310
if (!(entry->refresh_time))
311
entry->refresh_time = (2 * (entry->ctrl_info.holding_time))/3;
312
if ((now - entry->reply_wait) >
313
entry->refresh_time) {
314
dprintk("refreshing an entry.\n");
315
entry->entry_state = INGRESS_REFRESHING;
316
317
}
318
}
319
entry = entry->next;
320
}
321
read_unlock_bh(&client->ingress_lock);
322
}
323
324
static void in_destroy_cache(struct mpoa_client *mpc)
325
{
326
write_lock_irq(&mpc->ingress_lock);
327
while (mpc->in_cache != NULL)
328
mpc->in_ops->remove_entry(mpc->in_cache, mpc);
329
write_unlock_irq(&mpc->ingress_lock);
330
}
331
332
static eg_cache_entry *eg_cache_get_by_cache_id(__be32 cache_id,
333
struct mpoa_client *mpc)
334
{
335
eg_cache_entry *entry;
336
337
read_lock_irq(&mpc->egress_lock);
338
entry = mpc->eg_cache;
339
while (entry != NULL) {
340
if (entry->ctrl_info.cache_id == cache_id) {
341
refcount_inc(&entry->use);
342
read_unlock_irq(&mpc->egress_lock);
343
return entry;
344
}
345
entry = entry->next;
346
}
347
read_unlock_irq(&mpc->egress_lock);
348
349
return NULL;
350
}
351
352
/* This can be called from any context since it saves CPU flags */
353
static eg_cache_entry *eg_cache_get_by_tag(__be32 tag, struct mpoa_client *mpc)
354
{
355
unsigned long flags;
356
eg_cache_entry *entry;
357
358
read_lock_irqsave(&mpc->egress_lock, flags);
359
entry = mpc->eg_cache;
360
while (entry != NULL) {
361
if (entry->ctrl_info.tag == tag) {
362
refcount_inc(&entry->use);
363
read_unlock_irqrestore(&mpc->egress_lock, flags);
364
return entry;
365
}
366
entry = entry->next;
367
}
368
read_unlock_irqrestore(&mpc->egress_lock, flags);
369
370
return NULL;
371
}
372
373
/* This can be called from any context since it saves CPU flags */
374
static eg_cache_entry *eg_cache_get_by_vcc(struct atm_vcc *vcc,
375
struct mpoa_client *mpc)
376
{
377
unsigned long flags;
378
eg_cache_entry *entry;
379
380
read_lock_irqsave(&mpc->egress_lock, flags);
381
entry = mpc->eg_cache;
382
while (entry != NULL) {
383
if (entry->shortcut == vcc) {
384
refcount_inc(&entry->use);
385
read_unlock_irqrestore(&mpc->egress_lock, flags);
386
return entry;
387
}
388
entry = entry->next;
389
}
390
read_unlock_irqrestore(&mpc->egress_lock, flags);
391
392
return NULL;
393
}
394
395
static eg_cache_entry *eg_cache_get_by_src_ip(__be32 ipaddr,
396
struct mpoa_client *mpc)
397
{
398
eg_cache_entry *entry;
399
400
read_lock_irq(&mpc->egress_lock);
401
entry = mpc->eg_cache;
402
while (entry != NULL) {
403
if (entry->latest_ip_addr == ipaddr) {
404
refcount_inc(&entry->use);
405
read_unlock_irq(&mpc->egress_lock);
406
return entry;
407
}
408
entry = entry->next;
409
}
410
read_unlock_irq(&mpc->egress_lock);
411
412
return NULL;
413
}
414
415
static void eg_cache_put(eg_cache_entry *entry)
416
{
417
if (refcount_dec_and_test(&entry->use)) {
418
kfree_sensitive(entry);
419
}
420
}
421
422
/*
423
* This should be called with write lock on
424
*/
425
static void eg_cache_remove_entry(eg_cache_entry *entry,
426
struct mpoa_client *client)
427
{
428
struct atm_vcc *vcc;
429
struct k_message msg;
430
431
vcc = entry->shortcut;
432
dprintk("removing an egress entry.\n");
433
if (entry->prev != NULL)
434
entry->prev->next = entry->next;
435
else
436
client->eg_cache = entry->next;
437
if (entry->next != NULL)
438
entry->next->prev = entry->prev;
439
client->eg_ops->put(entry);
440
if (client->in_cache == NULL && client->eg_cache == NULL) {
441
msg.type = STOP_KEEP_ALIVE_SM;
442
msg_to_mpoad(&msg, client);
443
}
444
445
/* Check if the ingress side still uses this VCC */
446
if (vcc != NULL) {
447
in_cache_entry *in_entry = client->in_ops->get_by_vcc(vcc, client);
448
if (in_entry != NULL) {
449
client->in_ops->put(in_entry);
450
return;
451
}
452
vcc_release_async(vcc, -EPIPE);
453
}
454
}
455
456
static eg_cache_entry *eg_cache_add_entry(struct k_message *msg,
457
struct mpoa_client *client)
458
{
459
eg_cache_entry *entry = kzalloc(sizeof(eg_cache_entry), GFP_KERNEL);
460
461
if (entry == NULL) {
462
pr_info("out of memory\n");
463
return NULL;
464
}
465
466
dprintk("adding an egress entry, ip = %pI4, this should be our IP\n",
467
&msg->content.eg_info.eg_dst_ip);
468
469
refcount_set(&entry->use, 1);
470
dprintk("new_eg_cache_entry: about to lock\n");
471
write_lock_irq(&client->egress_lock);
472
entry->next = client->eg_cache;
473
entry->prev = NULL;
474
if (client->eg_cache != NULL)
475
client->eg_cache->prev = entry;
476
client->eg_cache = entry;
477
478
memcpy(entry->MPS_ctrl_ATM_addr, client->mps_ctrl_addr, ATM_ESA_LEN);
479
entry->ctrl_info = msg->content.eg_info;
480
entry->time = ktime_get_seconds();
481
entry->entry_state = EGRESS_RESOLVED;
482
dprintk("new_eg_cache_entry cache_id %u\n",
483
ntohl(entry->ctrl_info.cache_id));
484
dprintk("mps_ip = %pI4\n", &entry->ctrl_info.mps_ip);
485
refcount_inc(&entry->use);
486
487
write_unlock_irq(&client->egress_lock);
488
dprintk("new_eg_cache_entry: unlocked\n");
489
490
return entry;
491
}
492
493
static void update_eg_cache_entry(eg_cache_entry *entry, uint16_t holding_time)
494
{
495
entry->time = ktime_get_seconds();
496
entry->entry_state = EGRESS_RESOLVED;
497
entry->ctrl_info.holding_time = holding_time;
498
}
499
500
static void clear_expired(struct mpoa_client *client)
501
{
502
eg_cache_entry *entry, *next_entry;
503
time64_t now;
504
struct k_message msg;
505
506
now = ktime_get_seconds();
507
508
write_lock_irq(&client->egress_lock);
509
entry = client->eg_cache;
510
while (entry != NULL) {
511
next_entry = entry->next;
512
if ((now - entry->time) > entry->ctrl_info.holding_time) {
513
msg.type = SND_EGRESS_PURGE;
514
msg.content.eg_info = entry->ctrl_info;
515
dprintk("egress_cache: holding time expired, cache_id = %u.\n",
516
ntohl(entry->ctrl_info.cache_id));
517
msg_to_mpoad(&msg, client);
518
client->eg_ops->remove_entry(entry, client);
519
}
520
entry = next_entry;
521
}
522
write_unlock_irq(&client->egress_lock);
523
}
524
525
static void eg_destroy_cache(struct mpoa_client *mpc)
526
{
527
write_lock_irq(&mpc->egress_lock);
528
while (mpc->eg_cache != NULL)
529
mpc->eg_ops->remove_entry(mpc->eg_cache, mpc);
530
write_unlock_irq(&mpc->egress_lock);
531
}
532
533
534
static const struct in_cache_ops ingress_ops = {
535
.add_entry = in_cache_add_entry,
536
.get = in_cache_get,
537
.get_with_mask = in_cache_get_with_mask,
538
.get_by_vcc = in_cache_get_by_vcc,
539
.put = in_cache_put,
540
.remove_entry = in_cache_remove_entry,
541
.cache_hit = cache_hit,
542
.clear_count = clear_count_and_expired,
543
.check_resolving = check_resolving_entries,
544
.refresh = refresh_entries,
545
.destroy_cache = in_destroy_cache
546
};
547
548
static const struct eg_cache_ops egress_ops = {
549
.add_entry = eg_cache_add_entry,
550
.get_by_cache_id = eg_cache_get_by_cache_id,
551
.get_by_tag = eg_cache_get_by_tag,
552
.get_by_vcc = eg_cache_get_by_vcc,
553
.get_by_src_ip = eg_cache_get_by_src_ip,
554
.put = eg_cache_put,
555
.remove_entry = eg_cache_remove_entry,
556
.update = update_eg_cache_entry,
557
.clear_expired = clear_expired,
558
.destroy_cache = eg_destroy_cache
559
};
560
561
void atm_mpoa_init_cache(struct mpoa_client *mpc)
562
{
563
mpc->in_ops = &ingress_ops;
564
mpc->eg_ops = &egress_ops;
565
}
566
567