|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [RFC PATCH v3 2/2] xen: credit2: provide custom option to create
On Fri, Mar 31, 2017 at 10:32:09AM +0200, Dario Faggioli wrote:
> On Fri, 2017-03-31 at 00:58 +0530, Praveen Kumar wrote:
> > The patch introduces a new command line option 'custom' that when
> used will
> > create runqueue based upon the pCPU subset provide during bootup.
> >
> "introduces a new, very flexible, way of arranging runqueues in
> Credit2. It allows to specify, explicitly and precisely, what pCPUs
> should belong to which runqueue"
>
> Or something like this.
>
> It's great that you've got the code working. I have some comments,
> though, on how it actually looks like.
>
> > Signed-off-by: Praveen Kumar <kpraveen.lkml@xxxxxxxxx>
> > ---
>
> > --- a/docs/misc/xen-command-line.markdown
> > +++ b/docs/misc/xen-command-line.markdown
> > @@ -525,7 +525,7 @@ also slow in responding to load changes.
> > The default value of `1 sec` is rather long.
> >
> > ### credit2\_runqueue
> > -> `= cpu | core | socket | node | all`
> > +> `= cpu | core | socket | node | all | custom`
> >
> Err... but this is not really correct, right?
>
> I mean, it's not that the user should use the word 'custom' here.
>
> You probably want to use something like <custom>, and then define what
> that means below.
>
> > @@ -543,6 +543,12 @@ Available alternatives, with their meaning, are:
> > * `node`: one runqueue per each NUMA node of the host;
> > * `all`: just one runqueue shared by all the logical pCPUs of
> > the host
> > +* `custom`: one runqueue per subset. Example:
> > + credit2_runqueue=[[0,1,4,5][2,3,6,7][8,9,12,13][10,11,14
> > ,15]]
> >
> Maybe just use 2 (or at most 3) pCPUs per runqueue in the example. It'd
> make the line shorter and easier to read and understand.
>
Sure, Will take care of the same.
> > + - pCPUs 0, 1, 4 and 5 belong to runqueue 0
> > + - pCPUs 2, 3, 6 and 7 belong to runqueue 1
> > + - pCPUs 8, 9, 12 and 13 belong to runqueue 2
> > + - pCPUs 10, 11, 14 and 15 belong to runqueue 3
>
> > --- a/xen/common/sched_credit2.c
> > +++ b/xen/common/sched_credit2.c
> > @@ -330,18 +339,60 @@ integer_param("credit2_balance_over",
> > opt_overload_balance_tolerance);
> > #define OPT_RUNQUEUE_SOCKET 2
> > #define OPT_RUNQUEUE_NODE 3
> > #define OPT_RUNQUEUE_ALL 4
> > +#define OPT_RUNQUEUE_CUSTOM 5
> > static const char *const opt_runqueue_str[] = {
> > [OPT_RUNQUEUE_CPU] = "cpu",
> > [OPT_RUNQUEUE_CORE] = "core",
> > [OPT_RUNQUEUE_SOCKET] = "socket",
> > [OPT_RUNQUEUE_NODE] = "node",
> > - [OPT_RUNQUEUE_ALL] = "all"
> > + [OPT_RUNQUEUE_ALL] = "all",
> > + [OPT_RUNQUEUE_CUSTOM] = "custom"
> > };
> > static int __read_mostly opt_runqueue = OPT_RUNQUEUE_SOCKET;
> >
> > +static int __read_mostly custom_cpu_runqueue[NR_CPUS];
> > +
> I think this can be nr_cpu_ids (and be allocated dynamically during
> parsing).
>
Had to use NR_CPUS to make compiler happy with "error:Variably modified 'types'
at file scope".
> > +#define GETTOKEN( token, len, start, end ) \
> > +{ \
> > + char _tmp[len+1]; \
> > + int _i; \
> > + safe_strcpy(_tmp, start); \
> > + _tmp[len] = '\0'; \
> > + for ( _i = 0; _tmp[_i] != '\0'; _i++ ) \
> > + token = ( ( token * 10 ) + ( _tmp[_i] - '0' ) ); \
> > +}
> > +
> If you really need this, make it 'static inline', as you do for other
> functions.
>
> As a matter of fact, I don't think you need it, as, for instance, we do
> have simple_strtoul() (and some others). :-)
>
True, will be replacing this with simple_strtoul()
> > +static inline int trim(const char *c, char *t, char elem)
> > +{
> > + int l = strlen(c);
> > + const char *x = c ;
> > + int i = 0;
> > + if ( !c || !t )
> > + return -1;
> > + while ( *x != '\0' && i < l )
> > + {
> > + if ( *x != elem )
> > + t[i++] = *x;
> > + x++;
> > + }
> > + t[i] = '\0';
> > + return 0;
> > +}
> > +
> Again, why we need this? Can't we just deal with invalid characters
> while parsing the string (by, e.g., either skipping them or aborting,
> depending on whether or not they're innocuous)?
>
> > +static inline int getlen(char *start, char *end)
> > +{
> > + if ( ( start ) && ( end ) && ( end > start ) )
> > + return end-start;
> > + else
> > + return -1;
> > +}
> > +
> >
> Same.
>
> > static void parse_credit2_runqueue(const char *s)
> > {
> > unsigned int i;
> > + const char *s_end = NULL;
> > + char m[strlen(s)];
> > + char *_s = NULL;
> >
> No '_' prefixed variable names, please. :-)
>
> Also, what's m for?
>
> > @@ -351,7 +402,115 @@ static void parse_credit2_runqueue(const char
> > *s)
> > return;
> > }
> > }
> > +/*
> > + * At this stage we are either unknown value of credit2_runqueue
> > or we can
> > + * consider it to be custom cpu. Lets try parsing the same.
> > + * Resetting the custom_cpu_runqueue for future use. Only the
> > non-negative
> > + * entries will be valid. The index 'i' in custom_cpu_runqueue
> > will store
> > + * the specific runqueue it belongs to.
> > + * Example:
> > + * If custom_cpu_runqueue[3] == 2
> > + * Then, it means that cpu 3 belong to runqueue 2.
> > + * If custom_cpu_runqueue[4] == -1
> > + * Then, it means that cpu 4 doesn't belong to any runqueue.
> > + */
> > + for ( i = 0; i < nr_cpu_ids; i++ )
> > + custom_cpu_runqueue[i] = -1;
> > +
> > + /*
> > + * Format [[0,1,4,5][2,3,6,7][8,9,12,13][10,11,14,15]]
> > + */
> > + i = 0;
> > +
> > + /* In case user have spaces included in the input */
> > + if ( trim(s, m, ' ') != 0 )
> > + {
> > + printk( "WARNING : %s[%d] trim failed.\n", __func__,
> > __LINE__ );
> > + goto errReturn;
> > + }
> >
> Right. So, as I was saying above, can't we just start parsing the
> string --with something like a `char *p; while ( *p != '\0' ) {...}`--
> and skip the spaces when we find them?
>
> > + /* Starting to parse and get the cpu information on trimmed data
> > */
> > + _s = m;
> > + s_end = _s + strlen(_s);
> > +
> > + /* The start and should always be in format of '[..]' */
> > + if ( ( '[' == *_s ) && ( ']' == *(s_end-1)) )
> >
> But in the cover you said we also support:
>
> credit2_runqueue=0,1,4,5;2,3,6,7;8,9,12,13;10,11,14,15
>
> So, what I think we want here is something that checks that, if the
> first character is '[', then the last must be ']', or we already know
> the format is invalid, and we can stop.
>
> And it would also be better to write an if that checks whether the
> format is _invalid_, and stop the parsing if that is the case. That
> saves a level of indentation for all the code that follows.
>
Yes. Modifying the code in similar lines.
> > + {
> > + char *start = NULL, *end = NULL;
> > + int cpu_added_to_runqueue = 0;
> >
> It looks like this can be bool.
>
> > + _s++;
> > + while ( ( _s != NULL ) && ( _s < s_end ) )
> > + {
> > + char *token_sub_str = NULL;
> >
> We want a blank line between variable definition and actual code.
>
> > + start = strstr(_s, "[");
> > + end = strstr(_s, "]");
> > + /* Validation checks for '[' and ']' to properly parse
> > the entries
> > + */
> > + if ( ( !start && !end ) || ( ( end == ( s_end -1 ) ) &&
> > start ) )
> > + goto errReturn;
> >
> No camel case either, please. 'err' is probably just fine. :-)
>
> But, honestly, I can't quite tell what's going on here. Why strstr()?
> You can just check whether the first char of a set is '[' by looking at
> the current string parsing cursor. If it is, you can take a note about
> that somewhere, to remember to check for a ']' at some point.
>
>
strstr() is actually helping in getting the subsets, from where we will further
parse the cpu's which will belong to same runqueue.
> > + /* Check for last entry */
> > + else if ( !start && ( end == ( s_end -1 ) ) )
> > + goto nextSet;
> > +
> > + /* If we have [] as entry, move to nextSet */
> > + if ( getlen ( start, end ) < 1 )
> > + goto nextSet;
> >
> 'next' (or 'next_set', but I think 'next' is fine).
>
> > + /* Start to parse the actual value */
> > + start++;
> > + /*
> > + * find token within the subset
> > + */
> > + do
> > + {
> > + int token = 0;
> > + int len = 0;
> > + /* Get cpu ids separated by ',' within each set */
> > + token_sub_str = strpbrk(start, ",");
> > + if ( ( !token_sub_str && start < end ) ||
> > + ( token_sub_str > end && token_sub_str > start )
> > )
> > + len = getlen(start, end);
> > + else
> > + len = getlen(start, token_sub_str);
> > +
> > + if ( len <= 0 )
> > + continue;
> > + /* GETTOKEN will get return the parse and populate
> > the cpu in
> > + * token
> > + */
> > + GETTOKEN(token, len, start , end );
> > +
> > + if ( token >= nr_cpu_ids)
> > + goto errReturn;
> > + /* If not set already */
> > + if ( custom_cpu_runqueue[token] == -1 )
> > + {
> > + custom_cpu_runqueue[token] = i;
> > + cpu_added_to_runqueue = 1;
> > + }
> > + else
> > + goto errReturn;
> > +
> > + if ( !token_sub_str || token_sub_str > end )
> > + goto nextSet;
> > +
> > + start = ++token_sub_str;
> > + } while ( start < end );
> > +nextSet:
> > + if ( cpu_added_to_runqueue )
> > + {
> > + i++;
> > + cpu_added_to_runqueue = 0;
> > + }
> >
> >
> > + _s = ++end;
> > + }
> >
> Ok, so this looks more complex that it could be, to me. Mostly because
> you're doing your own token parsing.
>
> In my mind, parsing a string like the ones we want to accept, should
> look not too different from what's done inside init_boot_pages().
>
> Indeed our case looks a bit more complicated, but the general idea
> should be the same, i.e.:
> - scan the string and deal with what you find in there online, not
> beforehand;
> - use simple_strtoul()
Sure thing. Trying to use the exising APIs
>
> > @@ -661,6 +820,15 @@ cpu_to_runqueue(struct csched2_private *prv,
> > unsigned int cpu)
> > struct csched2_runqueue_data *rqd;
> > unsigned int rqi;
> >
> > + if ( opt_runqueue == OPT_RUNQUEUE_CUSTOM )
> > + {
> > + if ( custom_cpu_runqueue[cpu] != -1 )
> > + {
> > + BUG_ON(custom_cpu_runqueue[cpu] >= nr_cpu_ids);
> > + return custom_cpu_runqueue[cpu];
> > + }
> >
> Wait, and then what happens if a CPU was not in any set, and hence has
> it's custom_cpu_runqueue element == -1 ?
>
> I am ok with it not being added to any runqueue, but for that to be
> what really happens, I think we need to change code in init_pdata()
> too.
>
> It would be also good to print a warning, in such case. This is a
> custom mode, and the user may well be doing such a thing for a reason,
> but it doesn't harm trying make sure nothing this bad happened by
> mistake.
>
Yes, its better we show warning in case we receive -1 in init_pdata().
Working on the updated patch, will share once done with the basic unit test.
Thanks for your comments.
Regards,
~Praveen.
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |