2010-09-23 15:01:41 +02:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2010 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
2012-04-12 00:20:58 +02:00
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
2010-09-23 15:01:41 +02:00
( at your option ) any later version .
systemd 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
2012-04-12 00:20:58 +02:00
Lesser General Public License for more details .
2010-09-23 15:01:41 +02:00
2012-04-12 00:20:58 +02:00
You should have received a copy of the GNU Lesser General Public License
2010-09-23 15:01:41 +02:00
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <errno.h>
# include <stdlib.h>
# include <string.h>
2010-09-25 13:32:54 +02:00
# include <sys/sysinfo.h>
2010-09-26 15:50:14 +02:00
# include <sys/inotify.h>
2010-09-28 21:46:14 +02:00
# include <fcntl.h>
# include <sys/mman.h>
# include <unistd.h>
2010-10-26 21:28:39 +02:00
# include <libudev.h>
2010-09-23 15:01:41 +02:00
# include "log.h"
# include "readahead-common.h"
# include "util.h"
2010-09-25 15:39:38 +02:00
int file_verify ( int fd , const char * fn , off_t file_size_max , struct stat * st ) {
2010-09-23 15:01:41 +02:00
assert ( fd > = 0 ) ;
assert ( fn ) ;
assert ( st ) ;
if ( fstat ( fd , st ) < 0 ) {
log_warning ( " fstat(%s) failed: %m " , fn ) ;
return - errno ;
}
if ( ! S_ISREG ( st - > st_mode ) ) {
log_debug ( " Not preloading special file %s " , fn ) ;
return 0 ;
}
2010-09-25 15:39:38 +02:00
if ( st - > st_size < = 0 | | st - > st_size > file_size_max ) {
2010-10-05 21:49:17 +02:00
log_debug ( " Not preloading file %s with size out of bounds %llu " , fn , ( unsigned long long ) st - > st_size ) ;
2010-09-23 15:01:41 +02:00
return 0 ;
}
return 1 ;
}
int fs_on_ssd ( const char * p ) {
struct stat st ;
struct udev * udev = NULL ;
struct udev_device * udev_device = NULL , * look_at = NULL ;
bool b = false ;
const char * devtype , * rotational , * model , * id ;
assert ( p ) ;
if ( stat ( p , & st ) < 0 )
return - errno ;
2010-10-26 21:28:39 +02:00
if ( major ( st . st_dev ) = = 0 )
return false ;
2010-09-23 15:01:41 +02:00
if ( ! ( udev = udev_new ( ) ) )
return - ENOMEM ;
if ( ! ( udev_device = udev_device_new_from_devnum ( udev , ' b ' , st . st_dev ) ) )
goto finish ;
if ( ( devtype = udev_device_get_property_value ( udev_device , " DEVTYPE " ) ) & &
streq ( devtype , " partition " ) )
look_at = udev_device_get_parent ( udev_device ) ;
else
look_at = udev_device ;
if ( ! look_at )
goto finish ;
/* First, try high-level property */
if ( ( id = udev_device_get_property_value ( look_at , " ID_SSD " ) ) ) {
b = streq ( id , " 1 " ) ;
goto finish ;
}
/* Second, try kernel attribute */
if ( ( rotational = udev_device_get_sysattr_value ( look_at , " queue/rotational " ) ) )
if ( ( b = streq ( rotational , " 0 " ) ) )
goto finish ;
/* Finally, fallback to heuristics */
if ( ! ( look_at = udev_device_get_parent ( look_at ) ) )
goto finish ;
if ( ( model = udev_device_get_sysattr_value ( look_at , " model " ) ) )
b = ! ! strstr ( model , " SSD " ) ;
finish :
if ( udev_device )
udev_device_unref ( udev_device ) ;
if ( udev )
udev_unref ( udev ) ;
return b ;
}
2010-09-25 13:32:54 +02:00
2011-03-03 23:03:26 +01:00
int fs_on_read_only ( const char * p ) {
struct stat st ;
struct udev * udev = NULL ;
struct udev_device * udev_device = NULL ;
bool b = false ;
const char * read_only ;
assert ( p ) ;
if ( stat ( p , & st ) < 0 )
return - errno ;
if ( major ( st . st_dev ) = = 0 )
return false ;
if ( ! ( udev = udev_new ( ) ) )
return - ENOMEM ;
if ( ! ( udev_device = udev_device_new_from_devnum ( udev , ' b ' , st . st_dev ) ) )
goto finish ;
if ( ( read_only = udev_device_get_sysattr_value ( udev_device , " ro " ) ) )
if ( ( b = streq ( read_only , " 1 " ) ) )
goto finish ;
finish :
if ( udev_device )
udev_device_unref ( udev_device ) ;
if ( udev )
udev_unref ( udev ) ;
return b ;
}
2010-09-25 13:32:54 +02:00
bool enough_ram ( void ) {
struct sysinfo si ;
assert_se ( sysinfo ( & si ) > = 0 ) ;
2011-06-10 14:37:21 +02:00
/* Enable readahead only with at least 128MB memory */
return si . totalram > 127 * 1024 * 1024 / si . mem_unit ;
2010-09-25 13:32:54 +02:00
}
2010-09-26 15:50:14 +02:00
int open_inotify ( void ) {
int fd ;
if ( ( fd = inotify_init1 ( IN_CLOEXEC | IN_NONBLOCK ) ) < 0 ) {
log_error ( " Failed to create inotify handle: %m " ) ;
return - errno ;
}
2011-03-25 05:07:20 +01:00
mkdir ( " /run/systemd " , 0755 ) ;
mkdir ( " /run/systemd/readahead " , 0755 ) ;
2010-09-26 15:50:14 +02:00
2011-03-25 05:07:20 +01:00
if ( inotify_add_watch ( fd , " /run/systemd/readahead " , IN_CREATE ) < 0 ) {
log_error ( " Failed to watch /run/systemd/readahead: %m " ) ;
2010-09-26 15:50:14 +02:00
close_nointr_nofail ( fd ) ;
return - errno ;
}
return fd ;
}
2010-09-28 21:46:14 +02:00
ReadaheadShared * shared_get ( void ) {
int fd ;
ReadaheadShared * m = NULL ;
2011-03-25 05:07:20 +01:00
mkdir ( " /run/systemd " , 0755 ) ;
mkdir ( " /run/systemd/readahead " , 0755 ) ;
2010-09-29 02:54:09 +02:00
2011-03-25 05:07:20 +01:00
if ( ( fd = open ( " /run/systemd/readahead/shared " , O_CREAT | O_RDWR | O_CLOEXEC , 0644 ) ) < 0 ) {
2010-09-28 21:46:14 +02:00
log_error ( " Failed to create shared memory segment: %m " ) ;
goto finish ;
}
if ( ftruncate ( fd , sizeof ( ReadaheadShared ) ) < 0 ) {
log_error ( " Failed to truncate shared memory segment: %m " ) ;
goto finish ;
}
if ( ( m = mmap ( NULL , sizeof ( ReadaheadShared ) , PROT_WRITE | PROT_READ , MAP_SHARED , fd , 0 ) ) = = MAP_FAILED ) {
log_error ( " Failed to mmap shared memory segment: %m " ) ;
m = NULL ;
goto finish ;
}
finish :
if ( fd > = 0 )
close_nointr_nofail ( fd ) ;
return m ;
}
2010-10-26 21:28:39 +02:00
# define BUMP_REQUEST_NR (16*1024)
2012-05-04 00:13:20 +02:00
int block_bump_request_nr ( const char * p ) {
2010-10-26 21:28:39 +02:00
struct stat st ;
uint64_t u ;
2011-08-21 20:20:41 +02:00
char * ap = NULL , * line = NULL ;
2010-10-26 21:28:39 +02:00
int r ;
2011-08-21 20:20:41 +02:00
dev_t d ;
2010-10-26 21:28:39 +02:00
assert ( p ) ;
if ( stat ( p , & st ) < 0 )
return - errno ;
if ( major ( st . st_dev ) = = 0 )
return 0 ;
2011-08-21 20:20:41 +02:00
d = st . st_dev ;
block_get_whole_disk ( d , & d ) ;
2010-10-26 21:28:39 +02:00
2011-08-21 20:20:41 +02:00
if ( asprintf ( & ap , " /sys/dev/block/%u:%u/queue/nr_requests " , major ( d ) , minor ( d ) ) < 0 ) {
r = - ENOMEM ;
2010-10-26 21:28:39 +02:00
goto finish ;
}
2011-08-21 20:20:41 +02:00
r = read_one_line_file ( ap , & line ) ;
if ( r < 0 ) {
if ( r = = - ENOENT )
r = 0 ;
2010-10-26 21:28:39 +02:00
goto finish ;
}
2011-08-21 20:20:41 +02:00
r = safe_atou64 ( line , & u ) ;
if ( r > = 0 & & u > = BUMP_REQUEST_NR ) {
2010-10-26 21:28:39 +02:00
r = 0 ;
goto finish ;
}
2011-08-21 20:20:41 +02:00
free ( line ) ;
line = NULL ;
if ( asprintf ( & line , " %lu " , ( unsigned long ) BUMP_REQUEST_NR ) < 0 ) {
2010-10-26 21:28:39 +02:00
r = - ENOMEM ;
goto finish ;
}
2011-08-21 20:20:41 +02:00
r = write_one_line_file ( ap , line ) ;
if ( r < 0 )
2010-10-26 21:28:39 +02:00
goto finish ;
2011-08-21 20:20:41 +02:00
log_info ( " Bumped block_nr parameter of %u:%u to %lu. This is a temporary hack and should be removed one day. " , major ( d ) , minor ( d ) , ( unsigned long ) BUMP_REQUEST_NR ) ;
2010-10-26 21:28:39 +02:00
r = 1 ;
finish :
free ( ap ) ;
2011-08-21 20:20:41 +02:00
free ( line ) ;
2010-10-26 21:28:39 +02:00
return r ;
}
2012-05-04 00:13:20 +02:00
int block_get_readahead ( const char * p , uint64_t * bytes ) {
struct stat st ;
char * ap = NULL , * line = NULL ;
int r ;
dev_t d ;
uint64_t u ;
assert ( p ) ;
assert ( bytes ) ;
if ( stat ( p , & st ) < 0 )
return - errno ;
if ( major ( st . st_dev ) = = 0 )
return 0 ;
d = st . st_dev ;
block_get_whole_disk ( d , & d ) ;
if ( asprintf ( & ap , " /sys/dev/block/%u:%u/bdi/read_ahead_kb " , major ( d ) , minor ( d ) ) < 0 ) {
r = - ENOMEM ;
goto finish ;
}
r = read_one_line_file ( ap , & line ) ;
if ( r < 0 )
goto finish ;
r = safe_atou64 ( line , & u ) ;
if ( r < 0 )
goto finish ;
* bytes = u * 1024ULL ;
finish :
free ( ap ) ;
free ( line ) ;
return r ;
}
int block_set_readahead ( const char * p , uint64_t bytes ) {
struct stat st ;
char * ap = NULL , * line = NULL ;
int r ;
dev_t d ;
assert ( p ) ;
assert ( bytes ) ;
if ( stat ( p , & st ) < 0 )
return - errno ;
if ( major ( st . st_dev ) = = 0 )
return 0 ;
d = st . st_dev ;
block_get_whole_disk ( d , & d ) ;
if ( asprintf ( & ap , " /sys/dev/block/%u:%u/bdi/read_ahead_kb " , major ( d ) , minor ( d ) ) < 0 ) {
r = - ENOMEM ;
goto finish ;
}
if ( asprintf ( & line , " %llu " , ( unsigned long long ) bytes / 1024ULL ) < 0 ) {
r = - ENOMEM ;
goto finish ;
}
r = write_one_line_file ( ap , line ) ;
if ( r < 0 )
goto finish ;
finish :
free ( ap ) ;
free ( line ) ;
return r ;
}