-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathvirtRetransmission.c
127 lines (102 loc) · 3.37 KB
/
virtRetransmission.c
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
/*
* Copyright (C) 2014 Joshua Hare, Lance Hartung, and Suman Banerjee.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include "virtRetransmission.h"
#include "virtEgress.h"
#include "virtNetwork.h"
#include "virtPolicy.h"
#include "virtPolicyTypes.h"
#include "virtFlowTable.h"
static void retx_timer_fn(unsigned long arg)
{
struct virt_retx *retx = (struct virt_retx *)arg;
unsigned long next_timer_base = jiffies;
struct sk_buff *skb = NULL;
struct virt_skb_cb *skb_cb;
struct flow_table_entry *flow;
struct remote_node *dest;
spin_lock_bh(&retx->lock);
if(retx->skb)
skb = skb_clone(retx->skb, GFP_ATOMIC);
spin_unlock_bh(&retx->lock);
if(!skb)
goto out;
skb_cb = (struct virt_skb_cb *)skb->cb;
if(WARN_ON(!skb_cb))
goto out;
flow = skb_cb->flow;
if(WARN_ON(!flow))
goto out;
dest = flow->rnode;
if(WARN_ON(!dest))
goto out;
if(afterl(flow->last_touched, retx->last_updated)) {
next_timer_base = flow->last_touched;
retx->last_updated = flow->last_touched;
consume_skb(skb);
} else {
if(skb_queue_empty(&dest->tx_queue) && dest->link_count > 0) {
flow_table_entry_hold(flow);
virt_start_tx(skb_cb->virt, skb, dest);
} else {
consume_skb(skb);
}
}
out:
spin_lock_bh(&retx->lock);
if(retx->restart_timer)
mod_timer(&retx->timer, next_timer_base + retx->timeout_jiffies);
spin_unlock_bh(&retx->lock);
}
void virt_retx_init(struct virt_retx *retx, unsigned int timeout_usecs)
{
retx->skb = NULL;
retx->last_updated = 0;
retx->timeout_jiffies = usecs_to_jiffies(timeout_usecs);
retx->restart_timer = true;
init_timer(&retx->timer);
retx->timer.data = (unsigned long)retx;
retx->timer.function = retx_timer_fn;
spin_lock_init(&retx->lock);
}
void virt_retx_destroy(struct virt_retx *retx)
{
spin_lock_bh(&retx->lock);
if(retx->skb)
consume_skb(retx->skb);
retx->restart_timer = false;
spin_unlock_bh(&retx->lock);
del_timer_sync(&retx->timer);
}
void flow_set_retx_skb(struct flow_table_entry *flow, struct sk_buff *skb)
{
spin_lock_bh(&flow->retx.lock);
if(flow->retx.skb)
consume_skb(flow->retx.skb);
flow->retx.skb = skb_clone(skb, GFP_ATOMIC);
flow->retx.last_updated = flow->last_touched;
if(flow->retx.restart_timer)
mod_timer(&flow->retx.timer, flow->last_touched + flow->retx.timeout_jiffies);
spin_unlock_bh(&flow->retx.lock);
}
int flow_retx_enabled(const struct policy_entry *policy)
{
return (policy->action & POLICY_OP_RETX);
}