linux/net/sctp/tsnmap.c

365 lines
8.8 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/* SCTP kernel implementation
* (C) Copyright IBM Corp. 2001, 2004
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
*
* This file is part of the SCTP kernel implementation
*
* These functions manipulate sctp tsn mapping array.
*
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <linux-sctp@vger.kernel.org>
*
* Written or modified by:
* La Monte H.P. Yarroll <piggy@acm.org>
* Jon Grimm <jgrimm@us.ibm.com>
* Karl Knutson <karl@athena.chicago.il.us>
* Sridhar Samudrala <sri@us.ibm.com>
*/
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/bitmap.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
static void sctp_tsnmap_update(struct sctp_tsnmap *map);
static void sctp_tsnmap_find_gap_ack(unsigned long *map, __u16 off,
__u16 len, __u16 *start, __u16 *end);
static int sctp_tsnmap_grow(struct sctp_tsnmap *map, u16 size);
/* Initialize a block of memory as a tsnmap. */
struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *map, __u16 len,
__u32 initial_tsn, gfp_t gfp)
{
if (!map->tsn_map) {
map->tsn_map = kzalloc(len>>3, gfp);
if (map->tsn_map == NULL)
return NULL;
map->len = len;
} else {
bitmap_zero(map->tsn_map, map->len);
}
/* Keep track of TSNs represented by tsn_map. */
map->base_tsn = initial_tsn;
map->cumulative_tsn_ack_point = initial_tsn - 1;
map->max_tsn_seen = map->cumulative_tsn_ack_point;
map->num_dup_tsns = 0;
return map;
}
void sctp_tsnmap_free(struct sctp_tsnmap *map)
{
map->len = 0;
kfree(map->tsn_map);
}
/* Test the tracking state of this TSN.
* Returns:
* 0 if the TSN has not yet been seen
* >0 if the TSN has been seen (duplicate)
* <0 if the TSN is invalid (too large to track)
*/
int sctp_tsnmap_check(const struct sctp_tsnmap *map, __u32 tsn)
{
u32 gap;
/* Check to see if this is an old TSN */
if (TSN_lte(tsn, map->cumulative_tsn_ack_point))
return 1;
/* Verify that we can hold this TSN and that it will not
* overflow our map
*/
if (!TSN_lt(tsn, map->base_tsn + SCTP_TSN_MAP_SIZE))
return -1;
/* Calculate the index into the mapping arrays. */
gap = tsn - map->base_tsn;
/* Check to see if TSN has already been recorded. */
if (gap < map->len && test_bit(gap, map->tsn_map))
return 1;
else
return 0;
}
/* Mark this TSN as seen. */
sctp: be more restrictive in transport selection on bundled sacks It was noticed recently that when we send data on a transport, its possible that we might bundle a sack that arrived on a different transport. While this isn't a major problem, it does go against the SHOULD requirement in section 6.4 of RFC 2960: An endpoint SHOULD transmit reply chunks (e.g., SACK, HEARTBEAT ACK, etc.) to the same destination transport address from which it received the DATA or control chunk to which it is replying. This rule should also be followed if the endpoint is bundling DATA chunks together with the reply chunk. This patch seeks to correct that. It restricts the bundling of sack operations to only those transports which have moved the ctsn of the association forward since the last sack. By doing this we guarantee that we only bundle outbound saks on a transport that has received a chunk since the last sack. This brings us into stricter compliance with the RFC. Vlad had initially suggested that we strictly allow only sack bundling on the transport that last moved the ctsn forward. While this makes sense, I was concerned that doing so prevented us from bundling in the case where we had received chunks that moved the ctsn on multiple transports. In those cases, the RFC allows us to select any of the transports having received chunks to bundle the sack on. so I've modified the approach to allow for that, by adding a state variable to each transport that tracks weather it has moved the ctsn since the last sack. This I think keeps our behavior (and performance), close enough to our current profile that I think we can do this without a sysctl knob to enable/disable it. Signed-off-by: Neil Horman <nhorman@tuxdriver.com> CC: Vlad Yaseivch <vyasevich@gmail.com> CC: David S. Miller <davem@davemloft.net> CC: linux-sctp@vger.kernel.org Reported-by: Michele Baldessari <michele@redhat.com> Reported-by: sorin serban <sserban@redhat.com> Acked-by: Vlad Yasevich <vyasevich@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2012-06-30 03:04:26 +00:00
int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn,
struct sctp_transport *trans)
{
u16 gap;
if (TSN_lt(tsn, map->base_tsn))
return 0;
gap = tsn - map->base_tsn;
if (gap >= map->len && !sctp_tsnmap_grow(map, gap + 1))
return -ENOMEM;
if (!sctp_tsnmap_has_gap(map) && gap == 0) {
/* In this case the map has no gaps and the tsn we are
* recording is the next expected tsn. We don't touch
* the map but simply bump the values.
*/
map->max_tsn_seen++;
map->cumulative_tsn_ack_point++;
sctp: be more restrictive in transport selection on bundled sacks It was noticed recently that when we send data on a transport, its possible that we might bundle a sack that arrived on a different transport. While this isn't a major problem, it does go against the SHOULD requirement in section 6.4 of RFC 2960: An endpoint SHOULD transmit reply chunks (e.g., SACK, HEARTBEAT ACK, etc.) to the same destination transport address from which it received the DATA or control chunk to which it is replying. This rule should also be followed if the endpoint is bundling DATA chunks together with the reply chunk. This patch seeks to correct that. It restricts the bundling of sack operations to only those transports which have moved the ctsn of the association forward since the last sack. By doing this we guarantee that we only bundle outbound saks on a transport that has received a chunk since the last sack. This brings us into stricter compliance with the RFC. Vlad had initially suggested that we strictly allow only sack bundling on the transport that last moved the ctsn forward. While this makes sense, I was concerned that doing so prevented us from bundling in the case where we had received chunks that moved the ctsn on multiple transports. In those cases, the RFC allows us to select any of the transports having received chunks to bundle the sack on. so I've modified the approach to allow for that, by adding a state variable to each transport that tracks weather it has moved the ctsn since the last sack. This I think keeps our behavior (and performance), close enough to our current profile that I think we can do this without a sysctl knob to enable/disable it. Signed-off-by: Neil Horman <nhorman@tuxdriver.com> CC: Vlad Yaseivch <vyasevich@gmail.com> CC: David S. Miller <davem@davemloft.net> CC: linux-sctp@vger.kernel.org Reported-by: Michele Baldessari <michele@redhat.com> Reported-by: sorin serban <sserban@redhat.com> Acked-by: Vlad Yasevich <vyasevich@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2012-06-30 03:04:26 +00:00
if (trans)
trans->sack_generation =
trans->asoc->peer.sack_generation;
map->base_tsn++;
} else {
/* Either we already have a gap, or about to record a gap, so
* have work to do.
*
* Bump the max.
*/
if (TSN_lt(map->max_tsn_seen, tsn))
map->max_tsn_seen = tsn;
/* Mark the TSN as received. */
set_bit(gap, map->tsn_map);
/* Go fixup any internal TSN mapping variables including
* cumulative_tsn_ack_point.
*/
sctp_tsnmap_update(map);
}
return 0;
}
/* Initialize a Gap Ack Block iterator from memory being provided. */
static void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map,
struct sctp_tsnmap_iter *iter)
{
/* Only start looking one past the Cumulative TSN Ack Point. */
iter->start = map->cumulative_tsn_ack_point + 1;
}
/* Get the next Gap Ack Blocks. Returns 0 if there was not another block
* to get.
*/
static int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map,
struct sctp_tsnmap_iter *iter,
__u16 *start, __u16 *end)
{
int ended = 0;
__u16 start_ = 0, end_ = 0, offset;
/* If there are no more gap acks possible, get out fast. */
if (TSN_lte(map->max_tsn_seen, iter->start))
return 0;
offset = iter->start - map->base_tsn;
sctp_tsnmap_find_gap_ack(map->tsn_map, offset, map->len,
&start_, &end_);
/* The Gap Ack Block happens to end at the end of the map. */
if (start_ && !end_)
end_ = map->len - 1;
/* If we found a Gap Ack Block, return the start and end and
* bump the iterator forward.
*/
if (end_) {
/* Fix up the start and end based on the
* Cumulative TSN Ack which is always 1 behind base.
*/
*start = start_ + 1;
*end = end_ + 1;
/* Move the iterator forward. */
iter->start = map->cumulative_tsn_ack_point + *end + 1;
ended = 1;
}
return ended;
}
/* Mark this and any lower TSN as seen. */
void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn)
{
u32 gap;
if (TSN_lt(tsn, map->base_tsn))
return;
if (!TSN_lt(tsn, map->base_tsn + SCTP_TSN_MAP_SIZE))
return;
/* Bump the max. */
if (TSN_lt(map->max_tsn_seen, tsn))
map->max_tsn_seen = tsn;
gap = tsn - map->base_tsn + 1;
map->base_tsn += gap;
map->cumulative_tsn_ack_point += gap;
if (gap >= map->len) {
/* If our gap is larger then the map size, just
* zero out the map.
*/
bitmap_zero(map->tsn_map, map->len);
} else {
/* If the gap is smaller than the map size,
* shift the map by 'gap' bits and update further.
*/
bitmap_shift_right(map->tsn_map, map->tsn_map, gap, map->len);
sctp_tsnmap_update(map);
}
}
/********************************************************************
* 2nd Level Abstractions
********************************************************************/
/* This private helper function updates the tsnmap buffers and
* the Cumulative TSN Ack Point.
*/
static void sctp_tsnmap_update(struct sctp_tsnmap *map)
{
u16 len;
unsigned long zero_bit;
len = map->max_tsn_seen - map->cumulative_tsn_ack_point;
zero_bit = find_first_zero_bit(map->tsn_map, len);
if (!zero_bit)
return; /* The first 0-bit is bit 0. nothing to do */
map->base_tsn += zero_bit;
map->cumulative_tsn_ack_point += zero_bit;
bitmap_shift_right(map->tsn_map, map->tsn_map, zero_bit, map->len);
}
/* How many data chunks are we missing from our peer?
*/
__u16 sctp_tsnmap_pending(struct sctp_tsnmap *map)
{
__u32 cum_tsn = map->cumulative_tsn_ack_point;
__u32 max_tsn = map->max_tsn_seen;
__u32 base_tsn = map->base_tsn;
__u16 pending_data;
u32 gap;
pending_data = max_tsn - cum_tsn;
gap = max_tsn - base_tsn;
if (gap == 0 || gap >= map->len)
goto out;
pending_data -= bitmap_weight(map->tsn_map, gap + 1);
out:
return pending_data;
}
/* This is a private helper for finding Gap Ack Blocks. It searches a
* single array for the start and end of a Gap Ack Block.
*
* The flags "started" and "ended" tell is if we found the beginning
* or (respectively) the end of a Gap Ack Block.
*/
static void sctp_tsnmap_find_gap_ack(unsigned long *map, __u16 off,
__u16 len, __u16 *start, __u16 *end)
{
int i = off;
/* Look through the entire array, but break out
* early if we have found the end of the Gap Ack Block.
*/
/* Also, stop looking past the maximum TSN seen. */
/* Look for the start. */
i = find_next_bit(map, len, off);
if (i < len)
*start = i;
/* Look for the end. */
if (*start) {
/* We have found the start, let's find the
* end. If we find the end, break out.
*/
i = find_next_zero_bit(map, len, i);
if (i < len)
*end = i - 1;
}
}
/* Renege that we have seen a TSN. */
void sctp_tsnmap_renege(struct sctp_tsnmap *map, __u32 tsn)
{
u32 gap;
if (TSN_lt(tsn, map->base_tsn))
return;
/* Assert: TSN is in range. */
if (!TSN_lt(tsn, map->base_tsn + map->len))
return;
gap = tsn - map->base_tsn;
/* Pretend we never saw the TSN. */
clear_bit(gap, map->tsn_map);
}
/* How many gap ack blocks do we have recorded? */
__u16 sctp_tsnmap_num_gabs(struct sctp_tsnmap *map,
struct sctp_gap_ack_block *gabs)
{
struct sctp_tsnmap_iter iter;
int ngaps = 0;
/* Refresh the gap ack information. */
if (sctp_tsnmap_has_gap(map)) {
__u16 start = 0, end = 0;
sctp_tsnmap_iter_init(map, &iter);
while (sctp_tsnmap_next_gap_ack(map, &iter,
&start,
&end)) {
gabs[ngaps].start = htons(start);
gabs[ngaps].end = htons(end);
ngaps++;
if (ngaps >= SCTP_MAX_GABS)
break;
}
}
return ngaps;
}
static int sctp_tsnmap_grow(struct sctp_tsnmap *map, u16 size)
{
unsigned long *new;
unsigned long inc;
u16 len;
if (size > SCTP_TSN_MAP_SIZE)
return 0;
inc = ALIGN((size - map->len), BITS_PER_LONG) + SCTP_TSN_MAP_INCREMENT;
len = min_t(u16, map->len + inc, SCTP_TSN_MAP_SIZE);
new = kzalloc(len>>3, GFP_ATOMIC);
if (!new)
return 0;
bitmap_copy(new, map->tsn_map,
map->max_tsn_seen - map->cumulative_tsn_ack_point);
kfree(map->tsn_map);
map->tsn_map = new;
map->len = len;
return 1;
}