[Devel] [PATCH RHEL7 COMMIT] net/sctp: Allocate SSN map on a per-page basis

Konstantin Khorenko khorenko at virtuozzo.com
Mon Aug 20 17:19:22 MSK 2018


Please ignore.

--
Best regards,

Konstantin Khorenko,
Virtuozzo Linux Kernel Team

On 08/20/2018 05:14 PM, Konstantin Khorenko wrote:
> The commit is pushed to "branch-rh7-3.10.0-862.11.6.vz7.64.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
> after rh7-3.10.0-862.11.6.vz7.64.5
> ------>
> commit 9a125c47fe764b63803e20a74a81db0b64230765
> Author: Oleg Babin <obabin at virtuozzo.com>
> Date:   Mon Aug 20 17:14:09 2018 +0300
>
>     net/sctp: Allocate SSN map on a per-page basis
>
>     SCTP protocol allocates TCB on receiving INIT and COOKIE ECHO chunks
>     which specify input and output stream count. As the total count of
>     those streams can be up to (2^16 - 1) of each type, it is possible
>     to occupy a fifth order of memory for only one type of streams. As
>     allocation can happen in a softirq contex with GFP_ATOMIC flag set
>     and we actually do not need the memory to be physically contiguous,
>     for performance reasons we use flex_array which allocates memory on
>     a per-page basis.
>
>     v2: Use flex_array instead of allocating pages manually.
>
>     https://jira.sw.ru/browse/PSBM-82552
>
>     Signed-off-by: Oleg Babin <obabin at virtuozzo.com>
> ---
>  include/net/sctp/structs.h | 26 ++++++++----
>  net/sctp/ssnmap.c          | 99 ++++++++++++++++++++++------------------------
>  2 files changed, 65 insertions(+), 60 deletions(-)
>
> diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
> index 6bcd9d18b751..432affdacd7a 100644
> --- a/include/net/sctp/structs.h
> +++ b/include/net/sctp/structs.h
> @@ -64,6 +64,7 @@
>  #include <linux/atomic.h>		/* This gets us atomic counters.  */
>  #include <linux/skbuff.h>	/* We need sk_buff_head. */
>  #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 <net/sctp/auth.h>	/* We need auth specific structs */
>  #include <net/ip.h>		/* For inet_skb_parm */
> @@ -396,10 +397,17 @@ typedef struct sctp_sender_hb_info {
>   *
>   *  This is the structure we use to track both our outbound and inbound
>   *  SSN, or Stream Sequence Numbers.
> + *
> + *  As the total count of input or output streams can be up to (2^16 - 1)
> + *  of each type, it is possible to occupy a fifth order of memory for
> + *  only one type of streams. As allocation can happen in a softirq
> + *  contex with GFP_ATOMIC flag set and we actually do not need the memory
> + *  to be physically contiguous, for performance reasons we use flex_array
> + *  which allocates memory on a per-page basis.
>   */
>
>  struct sctp_stream {
> -	__u16 *ssn;
> +	struct flex_array *ssn;
>  	unsigned int len;
>  };
>
> @@ -408,30 +416,32 @@ struct sctp_ssnmap {
>  	struct sctp_stream out;
>  };
>
> -struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out,
> -				    gfp_t gfp);
> +struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, gfp_t gfp);
>  void sctp_ssnmap_free(struct sctp_ssnmap *map);
>  void sctp_ssnmap_clear(struct sctp_ssnmap *map);
>
>  /* What is the current SSN number for this stream? */
>  static inline __u16 sctp_ssn_peek(struct sctp_stream *stream, __u16 id)
>  {
> -	return stream->ssn[id];
> +	__u16 *ssnp = flex_array_get(stream->ssn, id);
> +	return *ssnp;
>  }
>
>  /* Return the next SSN number for this stream.	*/
>  static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id)
>  {
> -	return stream->ssn[id]++;
> +	__u16 *ssnp = flex_array_get(stream->ssn, id);
> +	return (*ssnp)++;
>  }
>
>  /* Skip over this ssn and all below. */
> -static inline void sctp_ssn_skip(struct sctp_stream *stream, __u16 id,
> +static inline void sctp_ssn_skip(struct sctp_stream *stream, __u16 id,
>  				 __u16 ssn)
>  {
> -	stream->ssn[id] = ssn+1;
> +	__u16 *ssnp = flex_array_get(stream->ssn, id);
> +	*ssnp = ssn + 1;
>  }
> -
> +
>  /*
>   * Pointers to address related SCTP functions.
>   * (i.e. things that depend on the address family.)
> diff --git a/net/sctp/ssnmap.c b/net/sctp/ssnmap.c
> index da8603523808..06a6e46c6d1e 100644
> --- a/net/sctp/ssnmap.c
> +++ b/net/sctp/ssnmap.c
> @@ -41,37 +41,57 @@
>  #include <net/sctp/sctp.h>
>  #include <net/sctp/sm.h>
>
> -static struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in,
> -					    __u16 out);
> +/* Allocate memory pages for one type of stream (in or out). */
> +static int sctp_stream_alloc(struct sctp_stream *stream, __u16 len, gfp_t gfp)
> +{
> +	int err = -ENOMEM;
> +
> +	stream->ssn = flex_array_alloc(sizeof(__u16), len, gfp);
> +	if (stream->ssn) {
> +		err = flex_array_prealloc(stream->ssn, 0, len, gfp);
> +		if (err) {
> +			flex_array_free(stream->ssn);
> +			stream->ssn = NULL;
> +		}
> +	}
> +
> +	return err;
> +}
>
> -/* Storage size needed for map includes 2 headers and then the
> - * specific needs of in or out streams.
> - */
> -static inline size_t sctp_ssnmap_size(__u16 in, __u16 out)
> +/* Free memory pages for one type of stream (in or out). */
> +static void sctp_stream_free(struct sctp_stream *stream)
>  {
> -	return sizeof(struct sctp_ssnmap) + (in + out) * sizeof(__u16);
> +	if (stream->ssn)
> +		flex_array_free(stream->ssn);
>  }
>
> +/* Clear all SSNs for one type of stream (in or out). */
> +static void sctp_stream_clear(struct sctp_stream *stream)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < stream->len; i++)
> +		flex_array_clear(stream->ssn, i);
> +}
>
>  /* Create a new sctp_ssnmap.
> - * Allocate room to store at least 'len' contiguous TSNs.
> + * Allocate room to store at least 'in' + 'out' SSNs.
>   */
> -struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out,
> -				    gfp_t gfp)
> +struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, gfp_t gfp)
>  {
>  	struct sctp_ssnmap *retval;
> -	int size;
> -
> -	size = sctp_ssnmap_size(in, out);
> -	if (size <= KMALLOC_MAX_SIZE)
> -		retval = kmalloc(size, gfp);
> -	else
> -		retval = (struct sctp_ssnmap *)
> -			  __get_free_pages(gfp, get_order(size));
> +	int err;
> +
> +	retval = (struct sctp_ssnmap *)kzalloc(sizeof(struct sctp_ssnmap), gfp);
>  	if (!retval)
>  		goto fail;
>
> -	if (!sctp_ssnmap_init(retval, in, out))
> +	err = sctp_stream_alloc(&retval->in, in, gfp);
> +	if (err)
> +		goto fail_map;
> +
> +	err = sctp_stream_alloc(&retval->out, out, gfp);
> +	if (err)
>  		goto fail_map;
>
>  	SCTP_DBG_OBJCNT_INC(ssnmap);
> @@ -79,54 +99,29 @@ struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out,
>  	return retval;
>
>  fail_map:
> -	if (size <= KMALLOC_MAX_SIZE)
> -		kfree(retval);
> -	else
> -		free_pages((unsigned long)retval, get_order(size));
> +	sctp_stream_free(&retval->in);
> +	sctp_stream_free(&retval->out);
> +	kfree(retval);
>  fail:
>  	return NULL;
>  }
>
> -
> -/* Initialize a block of memory as a ssnmap.  */
> -static struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in,
> -					    __u16 out)
> -{
> -	memset(map, 0x00, sctp_ssnmap_size(in, out));
> -
> -	/* Start 'in' stream just after the map header. */
> -	map->in.ssn = (__u16 *)&map[1];
> -	map->in.len = in;
> -
> -	/* Start 'out' stream just after 'in'. */
> -	map->out.ssn = &map->in.ssn[in];
> -	map->out.len = out;
> -
> -	return map;
> -}
> -
>  /* Clear out the ssnmap streams.  */
>  void sctp_ssnmap_clear(struct sctp_ssnmap *map)
>  {
> -	size_t size;
> -
> -	size = (map->in.len + map->out.len) * sizeof(__u16);
> -	memset(map->in.ssn, 0x00, size);
> +	sctp_stream_clear(&map->in);
> +	sctp_stream_clear(&map->out);
>  }
>
>  /* Dispose of a ssnmap.  */
>  void sctp_ssnmap_free(struct sctp_ssnmap *map)
>  {
> -	int size;
> -
>  	if (unlikely(!map))
>  		return;
>
> -	size = sctp_ssnmap_size(map->in.len, map->out.len);
> -	if (size <= KMALLOC_MAX_SIZE)
> -		kfree(map);
> -	else
> -		free_pages((unsigned long)map, get_order(size));
> +	sctp_stream_free(&map->in);
> +	sctp_stream_free(&map->out);
> +	kfree(map);
>
>  	SCTP_DBG_OBJCNT_DEC(ssnmap);
>  }
> .
>


More information about the Devel mailing list