RCU stalls when running out of memory on 3.14-rc4 w/ NFS and kernel threads priorities changed

From: Florian Fainelli
Date: Tue Mar 04 2014 - 18:56:17 EST


Hi all,

I am seeing the following RCU stalls messages appearing on an ARMv7
4xCPUs system running 3.14-rc4:

[ 42.974327] INFO: rcu_sched detected stalls on CPUs/tasks:
[ 42.979839] (detected by 0, t=2102 jiffies, g=4294967082,
c=4294967081, q=516)
[ 42.987169] INFO: Stall ended before state dump start

this is happening under the following conditions:

- the attached bumper.c binary alters various kernel thread priorities
based on the contents of bumpup.cfg and
- malloc_crazy is running from a NFS share
- malloc_crazy.c is running in a loop allocating chunks of memory but
never freeing it

when the priorities are altered, instead of getting the OOM killer to
be invoked, the RCU stalls are happening. Taking NFS out of the
equation does not allow me to reproduce the problem even with the
priorities altered.

This "problem" seems to have been there for quite a while now since I
was able to get 3.8.13 to trigger that bug as well, with a slightly
more detailed RCU debugging trace which points the finger at kswapd0.

You should be able to get that reproduced under QEMU with the
Versatile Express platform emulating a Cortex A15 CPU and the attached
files.

Any help or suggestions would be greatly appreciated. Thanks!
--
Florian
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
int i;
int *buf;
int max = 3048;

if (argc == 2)
max = atoi(argv[1]);

for(i = 0; i < max; i++) {
buf = (void *)malloc(1048576);
if(! buf) {
printf("malloc returned NULL\n");
return(0);
}
memset(buf, 0x11, 1048576);
*buf = i;
printf("%d\n", i);
}
printf("finished\n");

return(1);
}

Attachment: rcu_test.sh
Description: Bourne shell script

Attachment: bumpup.cfg
Description: Binary data


/***************************************************************************
* Copyright (C) 2010 by BSkyB
* Richard Parsons
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/

/* N.B. */
/* this program is supplied WITHOUT any warranty whatsoever */

/*==========================================================================

This program is used for the mass changing of process priorities by name
The syntax is as follows
./bumber [cfgfile]

The config file uses the following syntax
#this is a comment
process_name,POLICY,priority

Default is RR , but valid policies are
RR, FIFO and OTHER

priority is a value from 0..99

e.g.

#Telnet priority
telnetd,RR,37

=============================================================================*/


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <sched.h>
#include "bumper.h"

int main ( int argc, char *argv[] )
{

if ( argc != 2 )
{
printf ( "Error: Usage is %s [config file]\n",argv[0] );
return EXIT_FAILURE;
}
processList = init_application();
if ( readInputFile ( argv[1] ) < 0 )
{
printf ( "Error: Could not open %s\n",argv[1] );
return EXIT_FAILURE;
}
getProcessID ( processList );
destory_application(processList);
return EXIT_SUCCESS;
}

void *init_application ( void )
{
return NULL;
}

void destory_application(struct params *head)
{
struct params *next;

if (head == NULL)
return;

next = head;

while (next != NULL)
{
void *nptr = next->next;
free(next->process);
free(next);
next = nptr;
}
}

int readInputFile ( char *inFile )
{
FILE *fp;
int rc = -1;
char inLine[101];

if ( ( fp = fopen ( inFile, "r" ) ) == NULL )
return rc;

while ( fgets ( inLine, 100, fp ) !=NULL )
{
if ( inLine[0] == '#' )
continue;

processList = addEntry ( inLine,processList );
}
fclose ( fp );
return 1;
}


void *addEntry ( char *szParameters,struct params *head )
{
struct params *entry;
struct params *lastPtr=head;
void *retPtr=head;
char process[512];
char policy[512];
char prio[512];
if ( strlen ( szParameters ) > 511 ) // small saniy check..
return head;
if ( strlen ( szParameters ) < 5 ) // small saniy check..
return head;

int rc = sscanf ( szParameters,"%[^','],%[^','],%[^',']",process,policy,prio );
if ( rc == 3 )
{
int pVal = atoi(prio);
if ( ( pVal < 0 ) || ( pVal > 100 ) )
{
printf ( "Error : Process %s Priority %i OUT OF BOUNDS of 0..99\n",process,pVal );
return head;
}
entry = malloc ( sizeof ( struct params ) );
entry->next=head;
entry->process = malloc ( strlen ( process ) +1 );
strcpy ( entry->process,process );
entry->prio = pVal;
entry->policy = SCHED_RR;
entry->processCount=0;

if ( !strcasecmp ( policy,"RR" ) )
{
entry->policy = SCHED_RR;
}
else if ( !strcasecmp ( policy,"OTHER" ) )
{
entry->policy = SCHED_OTHER;
}
else if ( !strcasecmp ( policy,"FIFO" ) )
{
entry->policy = SCHED_FIFO;
}
printf ( "Process %s [Priority %i] [Policy %s (%i)]\n",entry->process,entry->prio,policy,entry->policy );
}
else
{
return head;
}
return entry;
}


int checkProcessName ( struct params *head, char *process, int *prio, int *policy )
{
int rc = -1;
struct params *itr = head;

for ( itr=head;itr != NULL;itr=itr->next )
{
if ( strstr ( process,itr->process ) != NULL )
{
*prio = itr->prio;
*policy = itr->policy;
itr->processCount++;
rc = 1;
break;
}
}
return rc;
}


int findChar ( char *inString,char character )
{
int a;

for ( a=0;a<strlen ( inString );a++ )
{
if ( inString[a] == character )
return a;
}
return -1;
}


int readStat ( char *exe, char *target_name )
{
FILE *fp;
int rc = -1;
char inLine[512];



if ( ( fp = fopen ( exe, "r" ) ) == NULL )
{
printf ( "Failed to open %s\n",exe );
return rc;
}

char *retChar = fgets ( inLine, 100, fp );
if ( retChar == NULL )
{
close ( fp );
return rc;
}

int start = findChar ( inLine,'(' );
int end = findChar ( inLine,')' );

if ( ( start > 0 ) && ( end >0 ) )
{
memset ( target_name,0, ( end-start ) +1 );
memcpy ( target_name,inLine+start+1, ( end-start )-1 );
rc = 1;
}

fclose ( fp );
return rc;
}


unsigned int getProcessID ( struct params *head )
{
DIR *dir_p;
struct dirent *dir_entry_p;
char dir_name[512]; // Horrible.. but will do
char target_name[512]; // Horrible.. but will do
int target_result;
char exe_link[512]; // yuk
int errorcount;
int result;
int prio;
int policy;

errorcount=0;
result=0;
dir_p = opendir ( "/proc/" );
while ( NULL != ( dir_entry_p = readdir ( dir_p ) ) )
{
if ( strspn ( dir_entry_p->d_name, "0123456789" ) == strlen ( dir_entry_p->d_name ) )
{
strcpy ( dir_name, "/proc/" );
strcat ( dir_name, dir_entry_p->d_name );
strcat ( dir_name, "/" );
exe_link[0] = 0;
strcat ( exe_link, dir_name );
strcat ( exe_link, "stat" );
target_result = readStat ( exe_link, target_name );
if ( target_result > 0 )
{
//printf ( "Checking for %s\n",target_name );

if ( checkProcessName ( head,target_name,&prio,&policy ) > 0 )
{
result = atoi ( dir_entry_p->d_name );
struct sched_param sp;
sp.sched_priority = prio;
if ( sched_setscheduler ( result, policy, &sp ) < 0 )
{
printf ( "Failed to set %s (%i) [Policy %i] [Priority %i]\n", target_name, result,policy,prio );
}
else
printf ( "Priority set %s (%i)\n", target_name, result );
}
}
}
}
closedir ( dir_p );
return result;
}
#ifndef _BUMPER_H
#define _BUMPER_H


struct params {
char *process;
int prio;
int policy;
int processCount;
struct params *next; // single entry linked list
};

unsigned int getProcessID ( struct params *head );
void *addEntry ( char *szParameters,struct params *head );
int checkProcessName ( struct params *head, char *process, int *prio, int *policy );
int readInputFile ( char *inFile );
void *init_application ( void );
void destory_application(struct params *head);

// Globals
struct params *processList;


#endif