2005-06-27 12:55:12 +04:00
/*
* fs / ioprio . c
*
2006-09-04 17:41:16 +04:00
* Copyright ( C ) 2004 Jens Axboe < axboe @ kernel . dk >
2005-06-27 12:55:12 +04:00
*
* Helper functions for setting / querying io priorities of processes . The
* system calls closely mimmick getpriority / setpriority , see the man page for
* those . The prio argument is a composite of prio class and prio data , where
* the data argument has meaning within that class . The standard scheduling
* classes have 8 distinct prio levels , with 0 being the highest prio and 7
* being the lowest .
*
* IOW , setting BE scheduling class with prio 2 is done ala :
*
* unsigned int prio = ( IOPRIO_CLASS_BE < < IOPRIO_CLASS_SHIFT ) | 2 ;
*
* ioprio_set ( PRIO_PROCESS , pid , prio ) ;
*
* See also Documentation / block / ioprio . txt
*
*/
# include <linux/kernel.h>
# include <linux/ioprio.h>
# include <linux/blkdev.h>
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-11-08 18:57:02 +03:00
# include <linux/syscalls.h>
2006-06-23 13:03:58 +04:00
# include <linux/security.h>
2007-10-19 10:40:14 +04:00
# include <linux/pid_namespace.h>
2005-06-27 12:55:12 +04:00
static int set_task_ioprio ( struct task_struct * task , int ioprio )
{
2006-06-23 13:03:58 +04:00
int err ;
2005-06-27 12:55:12 +04:00
struct io_context * ioc ;
if ( task - > uid ! = current - > euid & &
task - > uid ! = current - > uid & & ! capable ( CAP_SYS_NICE ) )
return - EPERM ;
2006-06-23 13:03:58 +04:00
err = security_task_setioprio ( task , ioprio ) ;
if ( err )
return err ;
2005-06-27 12:55:12 +04:00
task_lock ( task ) ;
2008-01-24 10:52:45 +03:00
do {
ioc = task - > io_context ;
/* see wmb() in current_io_context() */
smp_read_barrier_depends ( ) ;
if ( ioc )
break ;
2005-06-27 12:55:12 +04:00
2008-01-24 10:52:45 +03:00
ioc = alloc_io_context ( GFP_ATOMIC , - 1 ) ;
if ( ! ioc ) {
err = - ENOMEM ;
break ;
}
task - > io_context = ioc ;
} while ( 1 ) ;
2006-08-21 10:34:15 +04:00
2008-01-24 10:52:45 +03:00
if ( ! err ) {
ioc - > ioprio = ioprio ;
2006-08-29 11:05:44 +04:00
ioc - > ioprio_changed = 1 ;
2008-01-24 10:52:45 +03:00
}
2005-06-27 12:55:12 +04:00
task_unlock ( task ) ;
2008-01-24 10:52:45 +03:00
return err ;
2005-06-27 12:55:12 +04:00
}
2005-07-08 04:56:13 +04:00
asmlinkage long sys_ioprio_set ( int which , int who , int ioprio )
2005-06-27 12:55:12 +04:00
{
int class = IOPRIO_PRIO_CLASS ( ioprio ) ;
int data = IOPRIO_PRIO_DATA ( ioprio ) ;
struct task_struct * p , * g ;
struct user_struct * user ;
2007-02-12 11:53:01 +03:00
struct pid * pgrp ;
2005-06-27 12:55:12 +04:00
int ret ;
switch ( class ) {
case IOPRIO_CLASS_RT :
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
/* fall through, rt has prio field too */
case IOPRIO_CLASS_BE :
if ( data > = IOPRIO_BE_NR | | data < 0 )
return - EINVAL ;
break ;
case IOPRIO_CLASS_IDLE :
break ;
2007-11-07 15:54:07 +03:00
case IOPRIO_CLASS_NONE :
if ( data )
return - EINVAL ;
break ;
2005-06-27 12:55:12 +04:00
default :
return - EINVAL ;
}
ret = - ESRCH ;
2006-08-29 11:17:41 +04:00
/*
* We want IOPRIO_WHO_PGRP / IOPRIO_WHO_USER to be " atomic " ,
* so we can ' t use rcu_read_lock ( ) . See re - copy of - > ioprio
* in copy_process ( ) .
*/
read_lock ( & tasklist_lock ) ;
2005-06-27 12:55:12 +04:00
switch ( which ) {
case IOPRIO_WHO_PROCESS :
if ( ! who )
p = current ;
else
2007-10-19 10:40:16 +04:00
p = find_task_by_vpid ( who ) ;
2005-06-27 12:55:12 +04:00
if ( p )
ret = set_task_ioprio ( p , ioprio ) ;
break ;
case IOPRIO_WHO_PGRP :
if ( ! who )
2007-02-12 11:53:01 +03:00
pgrp = task_pgrp ( current ) ;
else
2007-10-19 10:40:14 +04:00
pgrp = find_vpid ( who ) ;
2007-02-12 11:53:01 +03:00
do_each_pid_task ( pgrp , PIDTYPE_PGID , p ) {
2005-06-27 12:55:12 +04:00
ret = set_task_ioprio ( p , ioprio ) ;
if ( ret )
break ;
2007-02-12 11:53:01 +03:00
} while_each_pid_task ( pgrp , PIDTYPE_PGID , p ) ;
2005-06-27 12:55:12 +04:00
break ;
case IOPRIO_WHO_USER :
if ( ! who )
user = current - > user ;
else
user = find_user ( who ) ;
if ( ! user )
break ;
do_each_thread ( g , p ) {
if ( p - > uid ! = who )
continue ;
ret = set_task_ioprio ( p , ioprio ) ;
if ( ret )
2006-08-21 10:33:23 +04:00
goto free_uid ;
2005-06-27 12:55:12 +04:00
} while_each_thread ( g , p ) ;
2006-08-21 10:33:23 +04:00
free_uid :
2005-06-27 12:55:12 +04:00
if ( who )
free_uid ( user ) ;
break ;
default :
ret = - EINVAL ;
}
2006-08-29 11:17:41 +04:00
read_unlock ( & tasklist_lock ) ;
2005-06-27 12:55:12 +04:00
return ret ;
}
2006-06-30 12:55:49 +04:00
static int get_task_ioprio ( struct task_struct * p )
{
int ret ;
ret = security_task_getioprio ( p ) ;
if ( ret )
goto out ;
2008-01-24 10:52:45 +03:00
ret = IOPRIO_PRIO_VALUE ( IOPRIO_CLASS_NONE , IOPRIO_NORM ) ;
if ( p - > io_context )
ret = p - > io_context - > ioprio ;
2006-06-30 12:55:49 +04:00
out :
return ret ;
}
2006-08-21 12:02:50 +04:00
int ioprio_best ( unsigned short aprio , unsigned short bprio )
{
unsigned short aclass = IOPRIO_PRIO_CLASS ( aprio ) ;
unsigned short bclass = IOPRIO_PRIO_CLASS ( bprio ) ;
if ( aclass = = IOPRIO_CLASS_NONE )
aclass = IOPRIO_CLASS_BE ;
if ( bclass = = IOPRIO_CLASS_NONE )
bclass = IOPRIO_CLASS_BE ;
if ( aclass = = bclass )
return min ( aprio , bprio ) ;
if ( aclass > bclass )
return bprio ;
else
return aprio ;
}
2005-07-08 04:56:13 +04:00
asmlinkage long sys_ioprio_get ( int which , int who )
2005-06-27 12:55:12 +04:00
{
struct task_struct * g , * p ;
struct user_struct * user ;
2007-02-12 11:53:01 +03:00
struct pid * pgrp ;
2005-06-27 12:55:12 +04:00
int ret = - ESRCH ;
2006-06-30 12:55:49 +04:00
int tmpio ;
2005-06-27 12:55:12 +04:00
2006-08-29 11:17:41 +04:00
read_lock ( & tasklist_lock ) ;
2005-06-27 12:55:12 +04:00
switch ( which ) {
case IOPRIO_WHO_PROCESS :
if ( ! who )
p = current ;
else
2007-10-19 10:40:16 +04:00
p = find_task_by_vpid ( who ) ;
2005-06-27 12:55:12 +04:00
if ( p )
2006-06-30 12:55:49 +04:00
ret = get_task_ioprio ( p ) ;
2005-06-27 12:55:12 +04:00
break ;
case IOPRIO_WHO_PGRP :
if ( ! who )
2007-02-12 11:53:01 +03:00
pgrp = task_pgrp ( current ) ;
else
2007-10-19 10:40:14 +04:00
pgrp = find_vpid ( who ) ;
2007-02-12 11:53:01 +03:00
do_each_pid_task ( pgrp , PIDTYPE_PGID , p ) {
2006-06-30 12:55:49 +04:00
tmpio = get_task_ioprio ( p ) ;
if ( tmpio < 0 )
continue ;
2005-06-27 12:55:12 +04:00
if ( ret = = - ESRCH )
2006-06-30 12:55:49 +04:00
ret = tmpio ;
2005-06-27 12:55:12 +04:00
else
2006-06-30 12:55:49 +04:00
ret = ioprio_best ( ret , tmpio ) ;
2007-02-12 11:53:01 +03:00
} while_each_pid_task ( pgrp , PIDTYPE_PGID , p ) ;
2005-06-27 12:55:12 +04:00
break ;
case IOPRIO_WHO_USER :
if ( ! who )
user = current - > user ;
else
user = find_user ( who ) ;
if ( ! user )
break ;
do_each_thread ( g , p ) {
if ( p - > uid ! = user - > uid )
continue ;
2006-06-30 12:55:49 +04:00
tmpio = get_task_ioprio ( p ) ;
if ( tmpio < 0 )
continue ;
2005-06-27 12:55:12 +04:00
if ( ret = = - ESRCH )
2006-06-30 12:55:49 +04:00
ret = tmpio ;
2005-06-27 12:55:12 +04:00
else
2006-06-30 12:55:49 +04:00
ret = ioprio_best ( ret , tmpio ) ;
2005-06-27 12:55:12 +04:00
} while_each_thread ( g , p ) ;
if ( who )
free_uid ( user ) ;
break ;
default :
ret = - EINVAL ;
}
2006-08-29 11:17:41 +04:00
read_unlock ( & tasklist_lock ) ;
2005-06-27 12:55:12 +04:00
return ret ;
}