mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 16:54:21 +00:00 
			
		
		
		
	net/sctp: Replace in/out stream arrays with flex_array
This path replaces physically contiguous memory arrays allocated using kmalloc_array() with flexible arrays. This enables to avoid memory allocation failures on the systems under a memory stress. Signed-off-by: Oleg Babin <obabin@virtuozzo.com> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									05364ca03c
								
							
						
					
					
						commit
						0d493b4d0b
					
				
					 2 changed files with 71 additions and 26 deletions
				
			
		|  | @ -57,6 +57,7 @@ | ||||||
| #include <linux/atomic.h>		/* This gets us atomic counters.  */ | #include <linux/atomic.h>		/* This gets us atomic counters.  */ | ||||||
| #include <linux/skbuff.h>	/* We need sk_buff_head. */ | #include <linux/skbuff.h>	/* We need sk_buff_head. */ | ||||||
| #include <linux/workqueue.h>	/* We need tq_struct.	 */ | #include <linux/workqueue.h>	/* We need tq_struct.	 */ | ||||||
|  | #include <linux/flex_array.h>	/* We need flex_array.   */ | ||||||
| #include <linux/sctp.h>		/* We need sctp* header structs.  */ | #include <linux/sctp.h>		/* We need sctp* header structs.  */ | ||||||
| #include <net/sctp/auth.h>	/* We need auth specific structs */ | #include <net/sctp/auth.h>	/* We need auth specific structs */ | ||||||
| #include <net/ip.h>		/* For inet_skb_parm */ | #include <net/ip.h>		/* For inet_skb_parm */ | ||||||
|  | @ -1438,8 +1439,8 @@ struct sctp_stream_in { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct sctp_stream { | struct sctp_stream { | ||||||
| 	struct sctp_stream_out *out; | 	struct flex_array *out; | ||||||
| 	struct sctp_stream_in *in; | 	struct flex_array *in; | ||||||
| 	__u16 outcnt; | 	__u16 outcnt; | ||||||
| 	__u16 incnt; | 	__u16 incnt; | ||||||
| 	/* Current stream being sent, if any */ | 	/* Current stream being sent, if any */ | ||||||
|  | @ -1465,14 +1466,14 @@ static inline struct sctp_stream_out *sctp_stream_out( | ||||||
| 	const struct sctp_stream *stream, | 	const struct sctp_stream *stream, | ||||||
| 	__u16 sid) | 	__u16 sid) | ||||||
| { | { | ||||||
| 	return ((struct sctp_stream_out *)(stream->out)) + sid; | 	return flex_array_get(stream->out, sid); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline struct sctp_stream_in *sctp_stream_in( | static inline struct sctp_stream_in *sctp_stream_in( | ||||||
| 	const struct sctp_stream *stream, | 	const struct sctp_stream *stream, | ||||||
| 	__u16 sid) | 	__u16 sid) | ||||||
| { | { | ||||||
| 	return ((struct sctp_stream_in *)(stream->in)) + sid; | 	return flex_array_get(stream->in, sid); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define SCTP_SO(s, i) sctp_stream_out((s), (i)) | #define SCTP_SO(s, i) sctp_stream_out((s), (i)) | ||||||
|  |  | ||||||
|  | @ -37,6 +37,53 @@ | ||||||
| #include <net/sctp/sm.h> | #include <net/sctp/sm.h> | ||||||
| #include <net/sctp/stream_sched.h> | #include <net/sctp/stream_sched.h> | ||||||
| 
 | 
 | ||||||
|  | static struct flex_array *fa_alloc(size_t elem_size, size_t elem_count, | ||||||
|  | 				   gfp_t gfp) | ||||||
|  | { | ||||||
|  | 	struct flex_array *result; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	result = flex_array_alloc(elem_size, elem_count, gfp); | ||||||
|  | 	if (result) { | ||||||
|  | 		err = flex_array_prealloc(result, 0, elem_count, gfp); | ||||||
|  | 		if (err) { | ||||||
|  | 			flex_array_free(result); | ||||||
|  | 			result = NULL; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void fa_free(struct flex_array *fa) | ||||||
|  | { | ||||||
|  | 	if (fa) | ||||||
|  | 		flex_array_free(fa); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void fa_copy(struct flex_array *fa, struct flex_array *from, | ||||||
|  | 		    size_t index, size_t count) | ||||||
|  | { | ||||||
|  | 	void *elem; | ||||||
|  | 
 | ||||||
|  | 	while (count--) { | ||||||
|  | 		elem = flex_array_get(from, index); | ||||||
|  | 		flex_array_put(fa, index, elem, 0); | ||||||
|  | 		index++; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void fa_zero(struct flex_array *fa, size_t index, size_t count) | ||||||
|  | { | ||||||
|  | 	void *elem; | ||||||
|  | 
 | ||||||
|  | 	while (count--) { | ||||||
|  | 		elem = flex_array_get(fa, index); | ||||||
|  | 		memset(elem, 0, fa->element_size); | ||||||
|  | 		index++; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Migrates chunks from stream queues to new stream queues if needed,
 | /* Migrates chunks from stream queues to new stream queues if needed,
 | ||||||
|  * but not across associations. Also, removes those chunks to streams |  * but not across associations. Also, removes those chunks to streams | ||||||
|  * higher than the new max. |  * higher than the new max. | ||||||
|  | @ -78,34 +125,33 @@ static void sctp_stream_outq_migrate(struct sctp_stream *stream, | ||||||
| 		 * sctp_stream_update will swap ->out pointers. | 		 * sctp_stream_update will swap ->out pointers. | ||||||
| 		 */ | 		 */ | ||||||
| 		for (i = 0; i < outcnt; i++) { | 		for (i = 0; i < outcnt; i++) { | ||||||
| 			kfree(new->out[i].ext); | 			kfree(SCTP_SO(new, i)->ext); | ||||||
| 			new->out[i].ext = stream->out[i].ext; | 			SCTP_SO(new, i)->ext = SCTP_SO(stream, i)->ext; | ||||||
| 			stream->out[i].ext = NULL; | 			SCTP_SO(stream, i)->ext = NULL; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for (i = outcnt; i < stream->outcnt; i++) | 	for (i = outcnt; i < stream->outcnt; i++) | ||||||
| 		kfree(stream->out[i].ext); | 		kfree(SCTP_SO(stream, i)->ext); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, | static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, | ||||||
| 				 gfp_t gfp) | 				 gfp_t gfp) | ||||||
| { | { | ||||||
| 	struct sctp_stream_out *out; | 	struct flex_array *out; | ||||||
|  | 	size_t elem_size = sizeof(struct sctp_stream_out); | ||||||
| 
 | 
 | ||||||
| 	out = kmalloc_array(outcnt, sizeof(*out), gfp); | 	out = fa_alloc(elem_size, outcnt, gfp); | ||||||
| 	if (!out) | 	if (!out) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	if (stream->out) { | 	if (stream->out) { | ||||||
| 		memcpy(out, stream->out, min(outcnt, stream->outcnt) * | 		fa_copy(out, stream->out, 0, min(outcnt, stream->outcnt)); | ||||||
| 					 sizeof(*out)); | 		fa_free(stream->out); | ||||||
| 		kfree(stream->out); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (outcnt > stream->outcnt) | 	if (outcnt > stream->outcnt) | ||||||
| 		memset(out + stream->outcnt, 0, | 		fa_zero(out, stream->outcnt, (outcnt - stream->outcnt)); | ||||||
| 		       (outcnt - stream->outcnt) * sizeof(*out)); |  | ||||||
| 
 | 
 | ||||||
| 	stream->out = out; | 	stream->out = out; | ||||||
| 
 | 
 | ||||||
|  | @ -115,22 +161,20 @@ static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, | ||||||
| static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, | static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, | ||||||
| 				gfp_t gfp) | 				gfp_t gfp) | ||||||
| { | { | ||||||
| 	struct sctp_stream_in *in; | 	struct flex_array *in; | ||||||
| 
 | 	size_t elem_size = sizeof(struct sctp_stream_in); | ||||||
| 	in = kmalloc_array(incnt, sizeof(*stream->in), gfp); |  | ||||||
| 
 | 
 | ||||||
|  | 	in = fa_alloc(elem_size, incnt, gfp); | ||||||
| 	if (!in) | 	if (!in) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	if (stream->in) { | 	if (stream->in) { | ||||||
| 		memcpy(in, stream->in, min(incnt, stream->incnt) * | 		fa_copy(in, stream->in, 0, min(incnt, stream->incnt)); | ||||||
| 				       sizeof(*in)); | 		fa_free(stream->in); | ||||||
| 		kfree(stream->in); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (incnt > stream->incnt) | 	if (incnt > stream->incnt) | ||||||
| 		memset(in + stream->incnt, 0, | 		fa_zero(in, stream->incnt, (incnt - stream->incnt)); | ||||||
| 		       (incnt - stream->incnt) * sizeof(*in)); |  | ||||||
| 
 | 
 | ||||||
| 	stream->in = in; | 	stream->in = in; | ||||||
| 
 | 
 | ||||||
|  | @ -174,7 +218,7 @@ in: | ||||||
| 	ret = sctp_stream_alloc_in(stream, incnt, gfp); | 	ret = sctp_stream_alloc_in(stream, incnt, gfp); | ||||||
| 	if (ret) { | 	if (ret) { | ||||||
| 		sched->free(stream); | 		sched->free(stream); | ||||||
| 		kfree(stream->out); | 		fa_free(stream->out); | ||||||
| 		stream->out = NULL; | 		stream->out = NULL; | ||||||
| 		stream->outcnt = 0; | 		stream->outcnt = 0; | ||||||
| 		goto out; | 		goto out; | ||||||
|  | @ -206,8 +250,8 @@ void sctp_stream_free(struct sctp_stream *stream) | ||||||
| 	sched->free(stream); | 	sched->free(stream); | ||||||
| 	for (i = 0; i < stream->outcnt; i++) | 	for (i = 0; i < stream->outcnt; i++) | ||||||
| 		kfree(SCTP_SO(stream, i)->ext); | 		kfree(SCTP_SO(stream, i)->ext); | ||||||
| 	kfree(stream->out); | 	fa_free(stream->out); | ||||||
| 	kfree(stream->in); | 	fa_free(stream->in); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void sctp_stream_clear(struct sctp_stream *stream) | void sctp_stream_clear(struct sctp_stream *stream) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Konstantin Khorenko
						Konstantin Khorenko