Re: [PATCH 2/3] libselinux: improve performance with pcre matches

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Carlo,

On Mon, Jan 23, 2023 at 01:40:46AM +0000, Carlo Marcelo Arenas Belón wrote:
> Since 30b3e9d2 (libselinux: Workaround for heap overhead of pcre,
> 2023-01-12), performance of PCRE2 matches has been affected due to
> excesive recreation of the match_data in an attempt to reduce memory
> utilization; instead of a workaround, it would be better to address
> the problem and maybe even improve performance in the process.
> 
> The issue is that currently the structure that holds PCRE state has
> both a pcre2_code (which is per pattern) and a pcre2_match_data (which
> is per match), forcing us to add a mutex to prevent multiple matches to
> step on each other.
> 
> Lets remove the match_data and the mutex and instead allocate one once
> in a thread independent way that could be used and reused, by extending
> our pthread interface to not only store TLS variables but also retrieve
> them, and then use one of those.
> 
> Since we are not interested on the capture groups (if any) lets only
> allocate 1 pair which is all that will be needed and change the logic
> so that a return of 0 (which means the pattern matched but there were
> not enough capture spots) is also considered a match.
> 
> This will ensure that the memory use would be bound to the number of
> concurrent matches instead of the number of patterns and therefore
> reduce the impact that recent changes on the way that the frames used
> for matching are allocated might had brough since 10.41 was released.
> 
> For cases where threads are not available, just keep it working in slow
> mode as done before the workaround was reverted.
> 
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@xxxxxxxxx>
> ---
>  libselinux/src/regex.c            | 108 +++++++++++++++++-------------
>  libselinux/src/selinux_internal.h |   4 ++
>  2 files changed, 64 insertions(+), 48 deletions(-)
> 

Thanks for writing this patch!  I notice it hasn't been applied upstream.  Is
this still being worked on?

A couple comments below:

> +static void __attribute__((destructor)) match_data_thread_free(void *key)
> +{
> +	void *value;
> +	pcre2_match_data *match_data;
> +
> +	if (match_data_key_initialized <= 0 || !match_data_initialized)
> +		return;
> +
> +	value = __selinux_getspecific(match_data_key);
> +	match_data = value ? value : key;
> +
> +	pcre2_match_data_free(match_data);
> +
> +	__pthread_mutex_lock(&key_mutex);
> +	if (--match_data_key_initialized == 1) {
> +		__selinux_key_delete(match_data_key);
> +		match_data_key_initialized = -1;
> +	}
> +	__pthread_mutex_unlock(&key_mutex);
> +}

This function is used as a pthread_key destructor.  But, it's also marked with
__attribute__((destructor)), making it a library-level destructor too.  That's
confusing.  Is there a reason for that?  I would have expected these to be two
different functions.  The pthread_key destructor should call
pcre2_match_data_free on the value passed as an argument, while the
library-level destructor should delete match_data_key.

>  int regex_match(struct regex_data *regex, char const *subject, int partial)
>  {
>  	int rc;
> -	pcre2_match_data *match_data;
> -	__pthread_mutex_lock(&regex->match_mutex);
> +	bool slow;
> +	pcre2_match_data *match_data = NULL;
> +
> +	if (match_data_key_initialized > 0) {
> +		if (match_data_initialized == 0) {
> +			match_data = pcre2_match_data_create(1, NULL);
> +			if (match_data) {
> +				match_data_initialized = 1;
> +				__selinux_setspecific(match_data_key,
> +							match_data);
> +				__pthread_mutex_lock(&key_mutex);
> +				match_data_key_initialized++;
> +				__pthread_mutex_unlock(&key_mutex);
> +			}
> +		} else
> +			match_data = __selinux_getspecific(match_data_key);
> +	}

Since match_data_key_initialized is checked without holding key_mutex, can't the
above code race with the below code in match_data_thread_free?

        __pthread_mutex_lock(&key_mutex);
        if (--match_data_key_initialized == 1) {
                __selinux_key_delete(match_data_key);
                match_data_key_initialized = -1;
        }
        __pthread_mutex_unlock(&key_mutex);

Perhaps deleting the pthread_key is just not something that should be done at
all?

- Eric



[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux