lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <1313514934.2725.20.camel@bwh-desktop>
Date:	Tue, 16 Aug 2011 18:15:34 +0100
From:	Ben Hutchings <bhutchings@...arflare.com>
To:	Tom Herbert <therbert@...gle.com>
Cc:	davem@...emloft.net, netdev@...r.kernel.org
Subject: Re: [RFC PATCH v2 1/9] dql: Dynamic queue limits

On Sun, 2011-08-07 at 21:43 -0700, Tom Herbert wrote: 
> Implementation of dynamic queue limits (dql).  This is a libary which
> allows a queue limit to be dynamically managed.  The goal of dql is
> to set the queue limit, number of ojects to the queue, to be minimized
> without allowing the queue to be starved.
> 
> dql would be used with a queue whose use has these properties:
> 
> 1) Objects are queued up to some limit which can be expressed as a
>    count of objects.
> 2) Periodically a completion process executes which retires consumed
>    objects.
> 3) Starvation occurs when limit has been reached, all queued data has
>    actually been consumed but completion processing has not yet run,
>    so queuing new data is blocked.
> 4) Minimizing the amount of queued data is desirable.
> 
> A canonical example of such a queue would be a NIC HW transmit queue.
> 
> The queue limit is dynamic, it will increase or decrease over time
> depending on the workload.  The queue limit is recalculated each time
> completion processing is done.  Increases occur when the queue is
> starved and can exponentially increase over successive intervals.
> Decreases occur when more data is being maintained in the queue than
> needed to prevent starvation.  The number of extra objects, or "slack",
> is measured over successive intervals, and to avoid hysteresis the
> limit is only reduced by the miminum slack seen over a configurable
> time period.

How does this or could this interact with adaptive TX interrupt
coalescing?

> dql API provides routines to manage the queue:
> - dql_init is called to intialize the dql structure
> - dql_reset is called to reset dynamic structures
> - dql_queued when objects are being enqueued
> - dql_avail returns availability in the queue
> - dql_completed is called when objects have be consumed in the queue

These explanations should go in kernel-doc comments above the functions.

> Configuration consists of:
> - max_limit, maximum limit
> - min_limt, minimum limit
> - slack_hold_time, time to measure instances of slack before reducing
>   queue limit.
> 
> Signed-off-by: Tom Herbert <therbert@...gle.com>
> ---
>  include/linux/dynamic_queue_limits.h |   80 ++++++++++++++++++++
>  lib/Makefile                         |    2 +-
>  lib/dynamic_queue_limits.c           |  132 ++++++++++++++++++++++++++++++++++
>  3 files changed, 213 insertions(+), 1 deletions(-)
>  create mode 100644 include/linux/dynamic_queue_limits.h
>  create mode 100644 lib/dynamic_queue_limits.c
> 
> diff --git a/include/linux/dynamic_queue_limits.h b/include/linux/dynamic_queue_limits.h
> new file mode 100644
> index 0000000..3ffc591
> --- /dev/null
> +++ b/include/linux/dynamic_queue_limits.h
[...] 
> +struct dql {
> +	unsigned long	limit;			/* Current limit */
> +	unsigned long	prev_ovlimit;		/* Previous over limit */
> +
> +	unsigned long	num_queued;		/* Total ever queued */
> +	unsigned long	prev_num_queued;	/* Previous queue total */
> +	unsigned long	num_completed;		/* Total ever completed */
> +
> +	unsigned long	last_obj_cnt;		/* Count at last queuing */
> +	unsigned long	prev_last_obj_cnt;	/* Previous queuing cnt */
> +
> +	unsigned long	lowest_slack;		/* Lowest slack found */
> +	unsigned long	slack_start_time;	/* Time slacks seen */
> +
> +	unsigned long	max_limit;		/* Maximum limit */
> +	unsigned long	min_limit;		/* Minimum limit */
> +	unsigned	slack_hold_time;	/* Time to measure slack */
> +};

Please put the descriptions in kernel-doc format.  That would also make
it easy to expand them a little - for example, to say that 'limit' is
not a hard limit but the threshold above which the queue should be
stopped.

> +/* Set some static maximums */
> +#define	DQL_MAX_OBJECT (-1UL / 16)
> +#define	DQL_MAX_LIMIT ((-1UL / 2) - DQL_MAX_OBJECT)

ULONG_MAX would be clearer than -1UL.

[...] 
> --- /dev/null
> +++ b/lib/dynamic_queue_limits.c
> @@ -0,0 +1,132 @@
> +/*
> + * Dynamic byte queue limits.  See include/linux/dynamic_queue_limits.h
> + *
> + * Author: Tom Herbert (therbert@...gle.com)
> + */
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/ctype.h>
> +#include <linux/kernel.h>
> +#include <linux/dynamic_queue_limits.h>
> +
> +#define POSDIFF(A, B) ((A) > (B) ? (A) - (B) : 0)
> +
> +/* Records completed count and recalculates the queue limit */
> +void dql_completed(struct dql *dql, unsigned long count)
> +{
> +	unsigned long inprogress, prev_inprogress, limit;
> +	unsigned long ovlimit, all_prev_completed, completed;
> +
> +	/* Can't complete more than what's in queue */
> +	BUG_ON(count > dql->num_queued - dql->num_completed);
> +
> +	completed = dql->num_completed + count;
> +	limit = dql->limit;
> +	ovlimit = POSDIFF(dql->num_queued - dql->num_completed, limit);
> +	inprogress = dql->num_queued - completed;
> +	prev_inprogress = dql->prev_num_queued - dql->num_completed;
> +	all_prev_completed = POSDIFF(completed, dql->prev_num_queued);

all_prev_completed is named and used as a boolean, but initialised as a
count.  Since the count is actually useful, it should be renamed to,
e.g. completed_newly_queued.

> +	if ((ovlimit && !inprogress) ||
> +	    (dql->prev_ovlimit && all_prev_completed)) {
> +		/*
> +		 * Queue considered starved if:
> +		 *   - The queue was over-limit in the last interval,
> +		 *     and there is no more data in the queue.
> +		 *  OR
> +		 *   - The queue was over-limit in the previous interval and
> +		 *     when enqueuing it was possible that all queued data
> +		 *     had been consumed.  This covers the case when queue
> +		 *     may have becomes starved between completion processing
> +		 *     running and next time enqueue was scheduled.
> +		 *
> +		 *     When queue is starved increase the limit by the amount
> +		 *     of bytes both sent and completed in the last interval,

For consistency, this should say 'number of objects' not 'amount of
bytes'.

> +		 *     plus any previous over-limit.
> +		 */
> +		limit += POSDIFF(completed, dql->prev_num_queued) +
> +		     dql->prev_ovlimit;

limit += completed_newly_queued + dql->prev_ovlimit;

> +		dql->slack_start_time = jiffies;
> +		dql->lowest_slack = -1UL;

ULONG_MAX

> +	} else if (inprogress && prev_inprogress && !all_prev_completed) {
> +		/*
> +		 * Queue was not starved, check if the limit can be decreased.
> +		 * A decrease is only considered if the queue has been busy in
> +		 * the whole interval (the check above).
> +		 *
> +		 * If there is slack, the amount execess data queued above the
> +		 * the amount needed to prevent starvation, the queue limit can
> +		 * be decreased.  To avoid hysteresis we consider the
> +		 * minimum amount of slack found over several iterations of the
> +		 * completion routine.
> +		 */

This is *adding* hysteresis (resistance to changing state).

> +		unsigned long slack, slack_last_objs;
> +
> +		/*
> +		 * Slack is the maximum of
> +		 *   - The queue limit plus previous over-limit minus twice
> +		 *     the number of objects completed.  Note that two times
> +		 *     number of completed bytes is basis for upper bound

'objects' not 'bytes'

> +		 *     of the limit.
> +		 *   - Portion of objects in the last queuing operation that
> +		 *     was not part of non-zero previous over-limit.  That is
> +		 *     "round down" by non-overlimit portion of the last
> +		 *     queueing operation.

I don't follow either the explanation or the calculation of this second
value (slack_last_objs).

If the queue was filled over the 'limit', doesn't that mean there was
less slack?  Yet slack_last_objs can only be positive (and therefore
influence slack) if dql->prev_ovlimit is true.

> +		 */
> +		slack = POSDIFF(limit + dql->prev_ovlimit,
> +		    2 * (completed - dql->num_completed));
> +		slack_last_objs = dql->prev_ovlimit ?
> +		    POSDIFF(dql->prev_last_obj_cnt, dql->prev_ovlimit) : 0;
> +
> +		slack = max(slack, slack_last_objs);
> +
> +		if (slack < dql->lowest_slack)
> +			dql->lowest_slack = slack;
> +
> +		if (time_after(jiffies,
> +			       dql->slack_start_time + dql->slack_hold_time)) {
> +			limit = POSDIFF(limit, dql->lowest_slack);
> +			dql->slack_start_time = jiffies;
> +			dql->lowest_slack = -1UL;
[...]

ULONG_MAX

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ