aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/policer/police_inlines.h
blob: 08000b9a303a3db8cc15a9a1b6b9ae006d8d2a30 (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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef __POLICE_INLINES_H__
#define __POLICE_INLINES_H__

#include <vnet/policer/police.h>
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>

#define IP4_NON_DSCP_BITS 0x03
#define IP4_DSCP_SHIFT    2
#define IP6_NON_DSCP_BITS 0xf03fffff
#define IP6_DSCP_SHIFT    22

static_always_inline void
vnet_policer_mark (vlib_buffer_t *b, ip_dscp_t dscp)
{
  ethernet_header_t *eh;
  ip4_header_t *ip4h;
  ip6_header_t *ip6h;
  u16 type;

  eh = (ethernet_header_t *) b->data;
  type = clib_net_to_host_u16 (eh->type);

  if (PREDICT_TRUE (type == ETHERNET_TYPE_IP4))
    {
      ip4h = (ip4_header_t *) & (b->data[sizeof (ethernet_header_t)]);;
      ip4h->tos &= IP4_NON_DSCP_BITS;
      ip4h->tos |= dscp << IP4_DSCP_SHIFT;
      ip4h->checksum = ip4_header_checksum (ip4h);
    }
  else
    {
      if (PREDICT_TRUE (type == ETHERNET_TYPE_IP6))
	{
	  ip6h = (ip6_header_t *) & (b->data[sizeof (ethernet_header_t)]);
	  ip6h->ip_version_traffic_class_and_flow_label &=
	    clib_host_to_net_u32 (IP6_NON_DSCP_BITS);
	  ip6h->ip_version_traffic_class_and_flow_label |=
	    clib_host_to_net_u32 (dscp << IP6_DSCP_SHIFT);
	}
    }
}

static_always_inline u8
vnet_policer_police (vlib_main_t *vm, vlib_buffer_t *b, u32 policer_index,
		     u64 time_in_policer_periods,
		     policer_result_e packet_color, bool handoff)
{
  qos_action_type_en act;
  u32 len;
  u32 col;
  policer_t *pol;
  vnet_policer_main_t *pm = &vnet_policer_main;

  /* Speculative prefetch assuming a conform result */
  vlib_prefetch_combined_counter (&policer_counters[POLICE_CONFORM],
				  vm->thread_index, policer_index);

  pol = &pm->policers[policer_index];

  if (handoff)
    {
      if (PREDICT_FALSE (pol->thread_index == ~0))
	/*
	 * This is the first packet to use this policer. Set the
	 * thread index in the policer to this thread and any
	 * packets seen by this node on other threads will
	 * be handed off to this one.
	 *
	 * This could happen simultaneously on another thread.
	 */
	clib_atomic_cmp_and_swap (&pol->thread_index, ~0, vm->thread_index);
      else if (PREDICT_FALSE (pol->thread_index != vm->thread_index))
	return QOS_ACTION_HANDOFF;
    }

  len = vlib_buffer_length_in_chain (vm, b);
  col = vnet_police_packet (pol, len, packet_color, time_in_policer_periods);
  act = pol->action[col];
  vlib_increment_combined_counter (&policer_counters[col], vm->thread_index,
				   policer_index, 1, len);
  if (PREDICT_TRUE (act == QOS_ACTION_MARK_AND_TRANSMIT))
    vnet_policer_mark (b, pol->mark_dscp[col]);

  return act;
}

typedef enum
{
  POLICER_HANDOFF_ERROR_CONGESTION_DROP,
} policer_handoff_error_t;

typedef struct policer_handoff_trace_t_
{
  u32 policer_index;
  u32 current_worker_index;
  u32 next_worker_index;
} policer_handoff_trace_t;

extern u8 *format_policer_handoff_trace (u8 *s, va_list *args);

/* Do worker handoff based on the policer's thread_index */
static_always_inline uword
policer_handoff (vlib_main_t *vm, vlib_node_runtime_t *node,
		 vlib_frame_t *frame, u32 fq_index, u32 policer_index)
{
  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
  u16 thread_indices[VLIB_FRAME_SIZE], *ti;
  u32 n_enq, n_left_from, *from;
  vnet_policer_main_t *pm;
  policer_t *policer;
  u32 this_thread, policer_thread = 0;
  bool single_policer_node = (policer_index != ~0);

  pm = &vnet_policer_main;
  if (single_policer_node)
    {
      policer = &pm->policers[policer_index];
      policer_thread = policer->thread_index;
    }

  this_thread = vm->thread_index;
  from = vlib_frame_vector_args (frame);
  n_left_from = frame->n_vectors;
  vlib_get_buffers (vm, from, bufs, n_left_from);

  b = bufs;
  ti = thread_indices;

  while (n_left_from > 0)
    {
      if (!single_policer_node)
	{
	  policer_index = vnet_buffer (b[0])->policer.index;
	  policer = &pm->policers[policer_index];
	  ti[0] = policer->thread_index;
	}
      else
	{
	  ti[0] = policer_thread;
	}

      if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
			 b[0]->flags & VLIB_BUFFER_IS_TRACED))
	{
	  policer_handoff_trace_t *t =
	    vlib_add_trace (vm, node, b[0], sizeof (*t));
	  t->current_worker_index = this_thread;
	  t->next_worker_index = ti[0];
	  t->policer_index = policer_index;
	}

      n_left_from--;
      ti++;
      b++;
    }

  n_enq = vlib_buffer_enqueue_to_thread (vm, node, fq_index, from,
					 thread_indices, frame->n_vectors, 1);

  if (n_enq < frame->n_vectors)
    vlib_node_increment_counter (vm, node->node_index,
				 POLICER_HANDOFF_ERROR_CONGESTION_DROP,
				 frame->n_vectors - n_enq);

  return n_enq;
}
#endif // __POLICE_INLINES_H__

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */