Commit 8e1ee18c authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller

sctp: Rework the tsn map to use generic bitmap.

The tsn map currently use is 4K large and is stuck inside
the sctp_association structure making memory references REALLY
expensive.  What we really need is at most 4K worth of bits
so the biggest map we would have is 512 bytes.   Also, the
map is only really usefull when we have gaps to store and
report.  As such, starting with minimal map of say 32 TSNs (bits)
should be enough for normal low-loss operations.  We can grow
the map by some multiple of 32 along with some extra room any
time we receive the TSN which would put us outside of the map
boundry.  As we close gaps, we can shift the map to rebase
it on the latest TSN we've seen.  This saves 4088 bytes per
association just in the map alone along savings from the now
unnecessary structure members.
Signed-off-by: default avatarVlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3c689b73
......@@ -261,7 +261,9 @@ enum { SCTP_ARBITRARY_COOKIE_ECHO_LEN = 200 };
* must be less than 65535 (2^16 - 1), or we will have overflow
* problems creating SACK's.
*/
#define SCTP_TSN_MAP_SIZE 2048
#define SCTP_TSN_MAP_INITIAL BITS_PER_LONG
#define SCTP_TSN_MAP_INCREMENT SCTP_TSN_MAP_INITIAL
#define SCTP_TSN_MAP_SIZE 4096
#define SCTP_TSN_MAX_GAP 65535
/* We will not record more than this many duplicate TSNs between two
......
......@@ -1545,7 +1545,6 @@ struct sctp_association {
* in tsn_map--we get it by calling sctp_tsnmap_get_ctsn().
*/
struct sctp_tsnmap tsn_map;
__u8 _map[sctp_tsnmap_storage_size(SCTP_TSN_MAP_SIZE)];
/* Ack State : This flag indicates if the next received
* : packet is to be responded to with a
......
......@@ -60,18 +60,7 @@ struct sctp_tsnmap {
* It points at one of the two buffers with which we will
* ping-pong between.
*/
__u8 *tsn_map;
/* This marks the tsn which overflows the tsn_map, when the
* cumulative ack point reaches this point we know we can switch
* maps (tsn_map and overflow_map swap).
*/
__u32 overflow_tsn;
/* This is the overflow array for tsn_map.
* It points at one of the other ping-pong buffers.
*/
__u8 *overflow_map;
unsigned long *tsn_map;
/* This is the TSN at tsn_map[0]. */
__u32 base_tsn;
......@@ -89,15 +78,15 @@ struct sctp_tsnmap {
*/
__u32 cumulative_tsn_ack_point;
/* This is the highest TSN we've marked. */
__u32 max_tsn_seen;
/* This is the minimum number of TSNs we can track. This corresponds
* to the size of tsn_map. Note: the overflow_map allows us to
* potentially track more than this quantity.
*/
__u16 len;
/* This is the highest TSN we've marked. */
__u32 max_tsn_seen;
/* Data chunks pending receipt. used by SCTP_STATUS sockopt */
__u16 pending_data;
......@@ -110,24 +99,17 @@ struct sctp_tsnmap {
/* Record gap ack block information here. */
struct sctp_gap_ack_block gabs[SCTP_MAX_GABS];
int malloced;
__u8 raw_map[0];
};
struct sctp_tsnmap_iter {
__u32 start;
};
/* This macro assists in creation of external storage for variable length
* internal buffers. We double allocate so the overflow map works.
*/
#define sctp_tsnmap_storage_size(count) (sizeof(__u8) * (count) * 2)
/* Initialize a block of memory as a tsnmap. */
struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *, __u16 len,
__u32 initial_tsn);
__u32 initial_tsn, gfp_t gfp);
void sctp_tsnmap_free(struct sctp_tsnmap *map);
/* Test the tracking state of this TSN.
* Returns:
......@@ -138,7 +120,7 @@ struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *, __u16 len,
int sctp_tsnmap_check(const struct sctp_tsnmap *, __u32 tsn);
/* Mark this TSN as seen. */
void sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn);
int sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn);
/* Mark this TSN and all lower as seen. */
void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn);
......@@ -183,10 +165,7 @@ static inline struct sctp_gap_ack_block *sctp_tsnmap_get_gabs(struct sctp_tsnmap
/* Is there a gap in the TSN map? */
static inline int sctp_tsnmap_has_gap(const struct sctp_tsnmap *map)
{
int has_gap;
has_gap = (map->cumulative_tsn_ack_point != map->max_tsn_seen);
return has_gap;
return (map->cumulative_tsn_ack_point != map->max_tsn_seen);
}
/* Mark a duplicate TSN. Note: limit the storage of duplicate TSN
......
......@@ -283,8 +283,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
if (!sctp_ulpq_init(&asoc->ulpq, asoc))
goto fail_init;
/* Set up the tsn tracking. */
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE, 0);
memset(&asoc->peer.tsn_map, 0, sizeof(struct sctp_tsnmap));
asoc->need_ecne = 0;
......@@ -402,6 +401,8 @@ void sctp_association_free(struct sctp_association *asoc)
/* Dispose of any pending chunks on the inqueue. */
sctp_inq_free(&asoc->base.inqueue);
sctp_tsnmap_free(&asoc->peer.tsn_map);
/* Free ssnmap storage. */
sctp_ssnmap_free(asoc->ssnmap);
......@@ -1122,8 +1123,8 @@ void sctp_assoc_update(struct sctp_association *asoc,
asoc->peer.rwnd = new->peer.rwnd;
asoc->peer.sack_needed = new->peer.sack_needed;
asoc->peer.i = new->peer.i;
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE,
asoc->peer.i.initial_tsn);
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,
asoc->peer.i.initial_tsn, GFP_ATOMIC);
/* Remove any peer addresses not present in the new association. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
......
......@@ -2288,8 +2288,9 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
}
/* Set up the TSN tracking pieces. */
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE,
asoc->peer.i.initial_tsn);
if (!sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,
asoc->peer.i.initial_tsn, gfp))
goto clean_up;
/* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
*
......
......@@ -1152,7 +1152,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
case SCTP_CMD_REPORT_TSN:
/* Record the arrival of a TSN. */
sctp_tsnmap_mark(&asoc->peer.tsn_map, cmd->obj.u32);
error = sctp_tsnmap_mark(&asoc->peer.tsn_map,
cmd->obj.u32);
break;
case SCTP_CMD_REPORT_FWDTSN:
......
This diff is collapsed.
......@@ -713,7 +713,9 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
/* Now that all memory allocations for this chunk succeeded, we
* can mark it as received so the tsn_map is updated correctly.
*/
sctp_tsnmap_mark(&asoc->peer.tsn_map, ntohl(chunk->subh.data_hdr->tsn));
if (sctp_tsnmap_mark(&asoc->peer.tsn_map,
ntohl(chunk->subh.data_hdr->tsn)))
goto fail_mark;
/* First calculate the padding, so we don't inadvertently
* pass up the wrong length to the user.
......@@ -755,8 +757,12 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
event->msg_flags |= chunk->chunk_hdr->flags;
event->iif = sctp_chunk_iif(chunk);
fail:
return event;
fail_mark:
kfree_skb(skb);
fail:
return NULL;
}
/* Create a partial delivery related event.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment