sctp: implement renege_events for sctp_stream_interleave
renege_events is added as a member of sctp_stream_interleave, used to renege some old data or idata in reasm or lobby queue properly to free some memory for the new data when there's memory stress. It defines sctp_renege_events for idata, and leaves sctp_ulpq_renege as it is for data. Signed-off-by: Xin Long <lucien.xin@gmail.com> Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Acked-by: Neil Horman <nhorman@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
9162e0ed9e
Коммит
94014e8d87
|
@ -43,6 +43,8 @@ struct sctp_stream_interleave {
|
||||||
struct sctp_chunk *chunk, gfp_t gfp);
|
struct sctp_chunk *chunk, gfp_t gfp);
|
||||||
int (*enqueue_event)(struct sctp_ulpq *ulpq,
|
int (*enqueue_event)(struct sctp_ulpq *ulpq,
|
||||||
struct sctp_ulpevent *event);
|
struct sctp_ulpevent *event);
|
||||||
|
void (*renege_events)(struct sctp_ulpq *ulpq,
|
||||||
|
struct sctp_chunk *chunk, gfp_t gfp);
|
||||||
};
|
};
|
||||||
|
|
||||||
void sctp_stream_interleave_init(struct sctp_stream *stream);
|
void sctp_stream_interleave_init(struct sctp_stream *stream);
|
||||||
|
|
|
@ -76,11 +76,8 @@ int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc);
|
||||||
void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn);
|
void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn);
|
||||||
|
|
||||||
void sctp_ulpq_reasm_flushtsn(struct sctp_ulpq *, __u32);
|
void sctp_ulpq_reasm_flushtsn(struct sctp_ulpq *, __u32);
|
||||||
|
|
||||||
|
__u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq,
|
||||||
|
struct sk_buff_head *list, __u16 needed);
|
||||||
|
|
||||||
#endif /* __sctp_ulpqueue_h__ */
|
#endif /* __sctp_ulpqueue_h__ */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1735,8 +1735,9 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCTP_CMD_RENEGE:
|
case SCTP_CMD_RENEGE:
|
||||||
sctp_ulpq_renege(&asoc->ulpq, cmd->obj.chunk,
|
asoc->stream.si->renege_events(&asoc->ulpq,
|
||||||
GFP_ATOMIC);
|
cmd->obj.chunk,
|
||||||
|
GFP_ATOMIC);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCTP_CMD_SETUP_T4:
|
case SCTP_CMD_SETUP_T4:
|
||||||
|
|
|
@ -545,6 +545,113 @@ static int sctp_ulpevent_idata(struct sctp_ulpq *ulpq,
|
||||||
return event_eor;
|
return event_eor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct sctp_ulpevent *sctp_intl_retrieve_first(struct sctp_ulpq *ulpq)
|
||||||
|
{
|
||||||
|
struct sctp_stream_in *csin, *sin = NULL;
|
||||||
|
struct sk_buff *first_frag = NULL;
|
||||||
|
struct sk_buff *last_frag = NULL;
|
||||||
|
struct sctp_ulpevent *retval;
|
||||||
|
struct sk_buff *pos;
|
||||||
|
__u32 next_fsn = 0;
|
||||||
|
__u16 sid = 0;
|
||||||
|
|
||||||
|
skb_queue_walk(&ulpq->reasm, pos) {
|
||||||
|
struct sctp_ulpevent *cevent = sctp_skb2event(pos);
|
||||||
|
|
||||||
|
csin = sctp_stream_in(ulpq->asoc, cevent->stream);
|
||||||
|
if (csin->pd_mode)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
|
||||||
|
case SCTP_DATA_FIRST_FRAG:
|
||||||
|
if (first_frag)
|
||||||
|
goto out;
|
||||||
|
if (cevent->mid == csin->mid) {
|
||||||
|
first_frag = pos;
|
||||||
|
last_frag = pos;
|
||||||
|
next_fsn = 0;
|
||||||
|
sin = csin;
|
||||||
|
sid = cevent->stream;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCTP_DATA_MIDDLE_FRAG:
|
||||||
|
if (!first_frag)
|
||||||
|
break;
|
||||||
|
if (cevent->stream == sid &&
|
||||||
|
cevent->mid == sin->mid &&
|
||||||
|
cevent->fsn == next_fsn) {
|
||||||
|
next_fsn++;
|
||||||
|
last_frag = pos;
|
||||||
|
} else {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SCTP_DATA_LAST_FRAG:
|
||||||
|
if (first_frag)
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!first_frag)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
out:
|
||||||
|
retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
|
||||||
|
&ulpq->reasm, first_frag,
|
||||||
|
last_frag);
|
||||||
|
if (retval) {
|
||||||
|
sin->fsn = next_fsn;
|
||||||
|
sin->pd_mode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sctp_intl_start_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
|
||||||
|
{
|
||||||
|
struct sctp_ulpevent *event;
|
||||||
|
|
||||||
|
if (skb_queue_empty(&ulpq->reasm))
|
||||||
|
return;
|
||||||
|
|
||||||
|
do {
|
||||||
|
event = sctp_intl_retrieve_first(ulpq);
|
||||||
|
if (event)
|
||||||
|
sctp_enqueue_event(ulpq, event);
|
||||||
|
} while (event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sctp_renege_events(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
|
||||||
|
gfp_t gfp)
|
||||||
|
{
|
||||||
|
struct sctp_association *asoc = ulpq->asoc;
|
||||||
|
__u32 freed = 0;
|
||||||
|
__u16 needed;
|
||||||
|
|
||||||
|
if (chunk) {
|
||||||
|
needed = ntohs(chunk->chunk_hdr->length);
|
||||||
|
needed -= sizeof(struct sctp_idata_chunk);
|
||||||
|
} else {
|
||||||
|
needed = SCTP_DEFAULT_MAXWINDOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skb_queue_empty(&asoc->base.sk->sk_receive_queue)) {
|
||||||
|
freed = sctp_ulpq_renege_list(ulpq, &ulpq->lobby, needed);
|
||||||
|
if (freed < needed)
|
||||||
|
freed += sctp_ulpq_renege_list(ulpq, &ulpq->reasm,
|
||||||
|
needed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk && freed >= needed)
|
||||||
|
if (sctp_ulpevent_idata(ulpq, chunk, gfp) <= 0)
|
||||||
|
sctp_intl_start_pd(ulpq, gfp);
|
||||||
|
|
||||||
|
sk_mem_reclaim(asoc->base.sk);
|
||||||
|
}
|
||||||
|
|
||||||
static struct sctp_stream_interleave sctp_stream_interleave_0 = {
|
static struct sctp_stream_interleave sctp_stream_interleave_0 = {
|
||||||
.data_chunk_len = sizeof(struct sctp_data_chunk),
|
.data_chunk_len = sizeof(struct sctp_data_chunk),
|
||||||
/* DATA process functions */
|
/* DATA process functions */
|
||||||
|
@ -553,6 +660,7 @@ static struct sctp_stream_interleave sctp_stream_interleave_0 = {
|
||||||
.validate_data = sctp_validate_data,
|
.validate_data = sctp_validate_data,
|
||||||
.ulpevent_data = sctp_ulpq_tail_data,
|
.ulpevent_data = sctp_ulpq_tail_data,
|
||||||
.enqueue_event = sctp_ulpq_tail_event,
|
.enqueue_event = sctp_ulpq_tail_event,
|
||||||
|
.renege_events = sctp_ulpq_renege,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sctp_stream_interleave sctp_stream_interleave_1 = {
|
static struct sctp_stream_interleave sctp_stream_interleave_1 = {
|
||||||
|
@ -563,6 +671,7 @@ static struct sctp_stream_interleave sctp_stream_interleave_1 = {
|
||||||
.validate_data = sctp_validate_idata,
|
.validate_data = sctp_validate_idata,
|
||||||
.ulpevent_data = sctp_ulpevent_idata,
|
.ulpevent_data = sctp_ulpevent_idata,
|
||||||
.enqueue_event = sctp_enqueue_event,
|
.enqueue_event = sctp_enqueue_event,
|
||||||
|
.renege_events = sctp_renege_events,
|
||||||
};
|
};
|
||||||
|
|
||||||
void sctp_stream_interleave_init(struct sctp_stream *stream)
|
void sctp_stream_interleave_init(struct sctp_stream *stream)
|
||||||
|
|
|
@ -978,8 +978,8 @@ void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
|
||||||
sctp_ulpq_reap_ordered(ulpq, sid);
|
sctp_ulpq_reap_ordered(ulpq, sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq,
|
__u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq, struct sk_buff_head *list,
|
||||||
struct sk_buff_head *list, __u16 needed)
|
__u16 needed)
|
||||||
{
|
{
|
||||||
__u16 freed = 0;
|
__u16 freed = 0;
|
||||||
__u32 tsn, last_tsn;
|
__u32 tsn, last_tsn;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче