2017-03-16 22:18:50 -08:00
// SPDX-License-Identifier: GPL-2.0
# include "bcachefs.h"
# include "btree_update.h"
2022-07-18 19:42:58 -04:00
# include "errcode.h"
2023-04-28 03:50:57 -04:00
# include "error.h"
2017-03-16 22:18:50 -08:00
# include "inode.h"
# include "quota.h"
2023-08-16 16:54:33 -04:00
# include "snapshot.h"
2017-03-16 22:18:50 -08:00
# include "super-io.h"
2022-02-20 05:00:45 -05:00
static const char * const bch2_quota_types [ ] = {
" user " ,
" group " ,
" project " ,
} ;
static const char * const bch2_quota_counters [ ] = {
" space " ,
" inodes " ,
} ;
static int bch2_sb_quota_validate ( struct bch_sb * sb , struct bch_sb_field * f ,
2022-01-03 23:38:50 -05:00
struct printbuf * err )
2017-03-16 22:18:50 -08:00
{
struct bch_sb_field_quota * q = field_to_type ( f , quota ) ;
2022-01-03 23:38:50 -05:00
if ( vstruct_bytes ( & q - > field ) < sizeof ( * q ) ) {
2023-02-03 21:01:40 -05:00
prt_printf ( err , " wrong size (got %zu should be %zu) " ,
2022-01-03 23:38:50 -05:00
vstruct_bytes ( & q - > field ) , sizeof ( * q ) ) ;
2022-11-19 22:39:08 -05:00
return - BCH_ERR_invalid_sb_quota ;
2022-01-03 23:38:50 -05:00
}
2017-03-16 22:18:50 -08:00
2022-01-03 23:38:50 -05:00
return 0 ;
2017-03-16 22:18:50 -08:00
}
2022-02-20 05:00:45 -05:00
static void bch2_sb_quota_to_text ( struct printbuf * out , struct bch_sb * sb ,
struct bch_sb_field * f )
{
struct bch_sb_field_quota * q = field_to_type ( f , quota ) ;
unsigned qtyp , counter ;
for ( qtyp = 0 ; qtyp < ARRAY_SIZE ( q - > q ) ; qtyp + + ) {
2023-02-03 21:01:40 -05:00
prt_printf ( out , " %s: flags %llx " ,
2022-02-20 05:00:45 -05:00
bch2_quota_types [ qtyp ] ,
le64_to_cpu ( q - > q [ qtyp ] . flags ) ) ;
for ( counter = 0 ; counter < Q_COUNTERS ; counter + + )
2023-02-03 21:01:40 -05:00
prt_printf ( out , " %s timelimit %u warnlimit %u " ,
2022-02-20 05:00:45 -05:00
bch2_quota_counters [ counter ] ,
le32_to_cpu ( q - > q [ qtyp ] . c [ counter ] . timelimit ) ,
le32_to_cpu ( q - > q [ qtyp ] . c [ counter ] . warnlimit ) ) ;
2023-02-03 21:01:40 -05:00
prt_newline ( out ) ;
2022-02-20 05:00:45 -05:00
}
}
2017-03-16 22:18:50 -08:00
const struct bch_sb_field_ops bch_sb_field_ops_quota = {
2022-02-20 05:00:45 -05:00
. validate = bch2_sb_quota_validate ,
. to_text = bch2_sb_quota_to_text ,
2017-03-16 22:18:50 -08:00
} ;
2022-04-03 17:50:01 -04:00
int bch2_quota_invalid ( const struct bch_fs * c , struct bkey_s_c k ,
2023-07-06 21:16:10 -04:00
enum bkey_invalid_flags flags ,
struct printbuf * err )
2017-03-16 22:18:50 -08:00
{
2022-04-03 17:50:01 -04:00
if ( k . k - > p . inode > = QTYP_NR ) {
2023-02-03 21:01:40 -05:00
prt_printf ( err , " invalid quota type (%llu >= %u) " ,
2022-04-03 17:50:01 -04:00
k . k - > p . inode , QTYP_NR ) ;
2022-11-19 22:39:08 -05:00
return - BCH_ERR_invalid_bkey ;
2022-04-03 17:50:01 -04:00
}
2017-03-16 22:18:50 -08:00
2022-04-03 17:50:01 -04:00
return 0 ;
2017-03-16 22:18:50 -08:00
}
2018-11-09 01:24:07 -05:00
void bch2_quota_to_text ( struct printbuf * out , struct bch_fs * c ,
struct bkey_s_c k )
2017-03-16 22:18:50 -08:00
{
2018-11-01 15:10:01 -04:00
struct bkey_s_c_quota dq = bkey_s_c_to_quota ( k ) ;
2017-03-16 22:18:50 -08:00
unsigned i ;
2018-11-01 15:10:01 -04:00
for ( i = 0 ; i < Q_COUNTERS ; i + + )
2023-02-03 21:01:40 -05:00
prt_printf ( out , " %s hardlimit %llu softlimit %llu " ,
2018-11-01 15:10:01 -04:00
bch2_quota_counters [ i ] ,
le64_to_cpu ( dq . v - > c [ i ] . hardlimit ) ,
le64_to_cpu ( dq . v - > c [ i ] . softlimit ) ) ;
2017-03-16 22:18:50 -08:00
}
# ifdef CONFIG_BCACHEFS_QUOTA
# include <linux/cred.h>
# include <linux/fs.h>
# include <linux/quota.h>
2022-10-12 14:47:58 -04:00
static void qc_info_to_text ( struct printbuf * out , struct qc_info * i )
{
printbuf_tabstops_reset ( out ) ;
printbuf_tabstop_push ( out , 20 ) ;
prt_str ( out , " i_fieldmask " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %x " , i - > i_fieldmask ) ;
prt_newline ( out ) ;
prt_str ( out , " i_flags " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %u " , i - > i_flags ) ;
prt_newline ( out ) ;
prt_str ( out , " i_spc_timelimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %u " , i - > i_spc_timelimit ) ;
prt_newline ( out ) ;
prt_str ( out , " i_ino_timelimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %u " , i - > i_ino_timelimit ) ;
prt_newline ( out ) ;
prt_str ( out , " i_rt_spc_timelimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %u " , i - > i_rt_spc_timelimit ) ;
prt_newline ( out ) ;
prt_str ( out , " i_spc_warnlimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %u " , i - > i_spc_warnlimit ) ;
prt_newline ( out ) ;
prt_str ( out , " i_ino_warnlimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %u " , i - > i_ino_warnlimit ) ;
prt_newline ( out ) ;
prt_str ( out , " i_rt_spc_warnlimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %u " , i - > i_rt_spc_warnlimit ) ;
prt_newline ( out ) ;
}
static void qc_dqblk_to_text ( struct printbuf * out , struct qc_dqblk * q )
{
printbuf_tabstops_reset ( out ) ;
printbuf_tabstop_push ( out , 20 ) ;
prt_str ( out , " d_fieldmask " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %x " , q - > d_fieldmask ) ;
prt_newline ( out ) ;
prt_str ( out , " d_spc_hardlimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %llu " , q - > d_spc_hardlimit ) ;
prt_newline ( out ) ;
prt_str ( out , " d_spc_softlimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %llu " , q - > d_spc_softlimit ) ;
prt_newline ( out ) ;
prt_str ( out , " d_ino_hardlimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %llu " , q - > d_ino_hardlimit ) ;
prt_newline ( out ) ;
prt_str ( out , " d_ino_softlimit " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %llu " , q - > d_ino_softlimit ) ;
prt_newline ( out ) ;
prt_str ( out , " d_space " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %llu " , q - > d_space ) ;
prt_newline ( out ) ;
prt_str ( out , " d_ino_count " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %llu " , q - > d_ino_count ) ;
prt_newline ( out ) ;
prt_str ( out , " d_ino_timer " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %llu " , q - > d_ino_timer ) ;
prt_newline ( out ) ;
prt_str ( out , " d_spc_timer " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %llu " , q - > d_spc_timer ) ;
prt_newline ( out ) ;
prt_str ( out , " d_ino_warns " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %i " , q - > d_ino_warns ) ;
prt_newline ( out ) ;
prt_str ( out , " d_spc_warns " ) ;
prt_tab ( out ) ;
prt_printf ( out , " %i " , q - > d_spc_warns ) ;
prt_newline ( out ) ;
}
2017-03-16 22:18:50 -08:00
static inline unsigned __next_qtype ( unsigned i , unsigned qtypes )
{
qtypes > > = i ;
return qtypes ? i + __ffs ( qtypes ) : QTYP_NR ;
}
# define for_each_set_qtype(_c, _i, _q, _qtypes) \
for ( _i = 0 ; \
( _i = __next_qtype ( _i , _qtypes ) , \
_q = & ( _c ) - > quotas [ _i ] , \
_i < QTYP_NR ) ; \
_i + + )
static bool ignore_hardlimit ( struct bch_memquota_type * q )
{
if ( capable ( CAP_SYS_RESOURCE ) )
return true ;
#if 0
struct mem_dqinfo * info = & sb_dqopt ( dquot - > dq_sb ) - > info [ dquot - > dq_id . type ] ;
return capable ( CAP_SYS_RESOURCE ) & &
( info - > dqi_format - > qf_fmt_id ! = QFMT_VFS_OLD | |
! ( info - > dqi_flags & DQF_ROOT_SQUASH ) ) ;
# endif
return false ;
}
enum quota_msg {
SOFTWARN , /* Softlimit reached */
SOFTLONGWARN , /* Grace time expired */
HARDWARN , /* Hardlimit reached */
HARDBELOW , /* Usage got below inode hardlimit */
SOFTBELOW , /* Usage got below inode softlimit */
} ;
static int quota_nl [ ] [ Q_COUNTERS ] = {
[ HARDWARN ] [ Q_SPC ] = QUOTA_NL_BHARDWARN ,
[ SOFTLONGWARN ] [ Q_SPC ] = QUOTA_NL_BSOFTLONGWARN ,
[ SOFTWARN ] [ Q_SPC ] = QUOTA_NL_BSOFTWARN ,
[ HARDBELOW ] [ Q_SPC ] = QUOTA_NL_BHARDBELOW ,
[ SOFTBELOW ] [ Q_SPC ] = QUOTA_NL_BSOFTBELOW ,
[ HARDWARN ] [ Q_INO ] = QUOTA_NL_IHARDWARN ,
[ SOFTLONGWARN ] [ Q_INO ] = QUOTA_NL_ISOFTLONGWARN ,
[ SOFTWARN ] [ Q_INO ] = QUOTA_NL_ISOFTWARN ,
[ HARDBELOW ] [ Q_INO ] = QUOTA_NL_IHARDBELOW ,
[ SOFTBELOW ] [ Q_INO ] = QUOTA_NL_ISOFTBELOW ,
} ;
struct quota_msgs {
u8 nr ;
struct {
u8 qtype ;
u8 msg ;
} m [ QTYP_NR * Q_COUNTERS ] ;
} ;
static void prepare_msg ( unsigned qtype ,
enum quota_counters counter ,
struct quota_msgs * msgs ,
enum quota_msg msg_type )
{
BUG_ON ( msgs - > nr > = ARRAY_SIZE ( msgs - > m ) ) ;
msgs - > m [ msgs - > nr ] . qtype = qtype ;
msgs - > m [ msgs - > nr ] . msg = quota_nl [ msg_type ] [ counter ] ;
msgs - > nr + + ;
}
static void prepare_warning ( struct memquota_counter * qc ,
unsigned qtype ,
enum quota_counters counter ,
struct quota_msgs * msgs ,
enum quota_msg msg_type )
{
if ( qc - > warning_issued & ( 1 < < msg_type ) )
return ;
prepare_msg ( qtype , counter , msgs , msg_type ) ;
}
static void flush_warnings ( struct bch_qid qid ,
struct super_block * sb ,
struct quota_msgs * msgs )
{
unsigned i ;
for ( i = 0 ; i < msgs - > nr ; i + + )
quota_send_warning ( make_kqid ( & init_user_ns , msgs - > m [ i ] . qtype , qid . q [ i ] ) ,
sb - > s_dev , msgs - > m [ i ] . msg ) ;
}
static int bch2_quota_check_limit ( struct bch_fs * c ,
unsigned qtype ,
struct bch_memquota * mq ,
struct quota_msgs * msgs ,
enum quota_counters counter ,
s64 v ,
enum quota_acct_mode mode )
{
struct bch_memquota_type * q = & c - > quotas [ qtype ] ;
struct memquota_counter * qc = & mq - > c [ counter ] ;
u64 n = qc - > v + v ;
BUG_ON ( ( s64 ) n < 0 ) ;
2018-11-01 15:10:01 -04:00
if ( mode = = KEY_TYPE_QUOTA_NOCHECK )
2017-03-16 22:18:50 -08:00
return 0 ;
if ( v < = 0 ) {
if ( n < qc - > hardlimit & &
( qc - > warning_issued & ( 1 < < HARDWARN ) ) ) {
qc - > warning_issued & = ~ ( 1 < < HARDWARN ) ;
prepare_msg ( qtype , counter , msgs , HARDBELOW ) ;
}
if ( n < qc - > softlimit & &
( qc - > warning_issued & ( 1 < < SOFTWARN ) ) ) {
qc - > warning_issued & = ~ ( 1 < < SOFTWARN ) ;
prepare_msg ( qtype , counter , msgs , SOFTBELOW ) ;
}
qc - > warning_issued = 0 ;
return 0 ;
}
if ( qc - > hardlimit & &
qc - > hardlimit < n & &
! ignore_hardlimit ( q ) ) {
prepare_warning ( qc , qtype , counter , msgs , HARDWARN ) ;
2022-10-15 03:52:28 -04:00
return - EDQUOT ;
2017-03-16 22:18:50 -08:00
}
if ( qc - > softlimit & &
2022-10-15 03:52:28 -04:00
qc - > softlimit < n ) {
if ( qc - > timer = = 0 ) {
qc - > timer = ktime_get_real_seconds ( ) + q - > limits [ counter ] . timelimit ;
prepare_warning ( qc , qtype , counter , msgs , SOFTWARN ) ;
} else if ( ktime_get_real_seconds ( ) > = qc - > timer & &
! ignore_hardlimit ( q ) ) {
prepare_warning ( qc , qtype , counter , msgs , SOFTLONGWARN ) ;
2017-03-16 22:18:50 -08:00
return - EDQUOT ;
2022-10-15 03:52:28 -04:00
}
2017-03-16 22:18:50 -08:00
}
return 0 ;
}
int bch2_quota_acct ( struct bch_fs * c , struct bch_qid qid ,
enum quota_counters counter , s64 v ,
enum quota_acct_mode mode )
{
unsigned qtypes = enabled_qtypes ( c ) ;
struct bch_memquota_type * q ;
struct bch_memquota * mq [ QTYP_NR ] ;
struct quota_msgs msgs ;
unsigned i ;
int ret = 0 ;
memset ( & msgs , 0 , sizeof ( msgs ) ) ;
2022-11-13 22:35:55 -05:00
for_each_set_qtype ( c , i , q , qtypes ) {
mq [ i ] = genradix_ptr_alloc ( & q - > table , qid . q [ i ] , GFP_KERNEL ) ;
if ( ! mq [ i ] )
return - ENOMEM ;
}
2017-03-16 22:18:50 -08:00
for_each_set_qtype ( c , i , q , qtypes )
mutex_lock_nested ( & q - > lock , i ) ;
for_each_set_qtype ( c , i , q , qtypes ) {
ret = bch2_quota_check_limit ( c , i , mq [ i ] , & msgs , counter , v , mode ) ;
if ( ret )
goto err ;
}
for_each_set_qtype ( c , i , q , qtypes )
mq [ i ] - > c [ counter ] . v + = v ;
err :
for_each_set_qtype ( c , i , q , qtypes )
mutex_unlock ( & q - > lock ) ;
flush_warnings ( qid , c - > vfs_sb , & msgs ) ;
return ret ;
}
static void __bch2_quota_transfer ( struct bch_memquota * src_q ,
struct bch_memquota * dst_q ,
enum quota_counters counter , s64 v )
{
BUG_ON ( v > src_q - > c [ counter ] . v ) ;
BUG_ON ( v + dst_q - > c [ counter ] . v < v ) ;
src_q - > c [ counter ] . v - = v ;
dst_q - > c [ counter ] . v + = v ;
}
int bch2_quota_transfer ( struct bch_fs * c , unsigned qtypes ,
struct bch_qid dst ,
2018-12-17 05:43:00 -05:00
struct bch_qid src , u64 space ,
enum quota_acct_mode mode )
2017-03-16 22:18:50 -08:00
{
struct bch_memquota_type * q ;
struct bch_memquota * src_q [ 3 ] , * dst_q [ 3 ] ;
struct quota_msgs msgs ;
unsigned i ;
int ret = 0 ;
qtypes & = enabled_qtypes ( c ) ;
memset ( & msgs , 0 , sizeof ( msgs ) ) ;
2022-11-13 22:35:55 -05:00
for_each_set_qtype ( c , i , q , qtypes ) {
src_q [ i ] = genradix_ptr_alloc ( & q - > table , src . q [ i ] , GFP_KERNEL ) ;
dst_q [ i ] = genradix_ptr_alloc ( & q - > table , dst . q [ i ] , GFP_KERNEL ) ;
if ( ! src_q [ i ] | | ! dst_q [ i ] )
return - ENOMEM ;
}
2017-03-16 22:18:50 -08:00
for_each_set_qtype ( c , i , q , qtypes )
mutex_lock_nested ( & q - > lock , i ) ;
for_each_set_qtype ( c , i , q , qtypes ) {
ret = bch2_quota_check_limit ( c , i , dst_q [ i ] , & msgs , Q_SPC ,
dst_q [ i ] - > c [ Q_SPC ] . v + space ,
2018-12-17 05:43:00 -05:00
mode ) ;
2017-03-16 22:18:50 -08:00
if ( ret )
goto err ;
ret = bch2_quota_check_limit ( c , i , dst_q [ i ] , & msgs , Q_INO ,
dst_q [ i ] - > c [ Q_INO ] . v + 1 ,
2018-12-17 05:43:00 -05:00
mode ) ;
2017-03-16 22:18:50 -08:00
if ( ret )
goto err ;
}
for_each_set_qtype ( c , i , q , qtypes ) {
__bch2_quota_transfer ( src_q [ i ] , dst_q [ i ] , Q_SPC , space ) ;
__bch2_quota_transfer ( src_q [ i ] , dst_q [ i ] , Q_INO , 1 ) ;
}
err :
for_each_set_qtype ( c , i , q , qtypes )
mutex_unlock ( & q - > lock ) ;
flush_warnings ( dst , c - > vfs_sb , & msgs ) ;
return ret ;
}
2022-10-15 03:52:28 -04:00
static int __bch2_quota_set ( struct bch_fs * c , struct bkey_s_c k ,
struct qc_dqblk * qdq )
2017-03-16 22:18:50 -08:00
{
struct bkey_s_c_quota dq ;
struct bch_memquota_type * q ;
struct bch_memquota * mq ;
unsigned i ;
BUG_ON ( k . k - > p . inode > = QTYP_NR ) ;
2022-07-17 00:44:19 -04:00
if ( ! ( ( 1U < < k . k - > p . inode ) & enabled_qtypes ( c ) ) )
return 0 ;
2017-03-16 22:18:50 -08:00
switch ( k . k - > type ) {
2018-11-01 15:10:01 -04:00
case KEY_TYPE_quota :
2017-03-16 22:18:50 -08:00
dq = bkey_s_c_to_quota ( k ) ;
q = & c - > quotas [ k . k - > p . inode ] ;
mutex_lock ( & q - > lock ) ;
mq = genradix_ptr_alloc ( & q - > table , k . k - > p . offset , GFP_KERNEL ) ;
if ( ! mq ) {
mutex_unlock ( & q - > lock ) ;
return - ENOMEM ;
}
for ( i = 0 ; i < Q_COUNTERS ; i + + ) {
mq - > c [ i ] . hardlimit = le64_to_cpu ( dq . v - > c [ i ] . hardlimit ) ;
mq - > c [ i ] . softlimit = le64_to_cpu ( dq . v - > c [ i ] . softlimit ) ;
}
2022-10-15 03:52:28 -04:00
if ( qdq & & qdq - > d_fieldmask & QC_SPC_TIMER )
2023-07-06 22:47:42 -04:00
mq - > c [ Q_SPC ] . timer = qdq - > d_spc_timer ;
2022-10-15 03:52:28 -04:00
if ( qdq & & qdq - > d_fieldmask & QC_SPC_WARNS )
2023-07-06 22:47:42 -04:00
mq - > c [ Q_SPC ] . warns = qdq - > d_spc_warns ;
2022-10-15 03:52:28 -04:00
if ( qdq & & qdq - > d_fieldmask & QC_INO_TIMER )
2023-07-06 22:47:42 -04:00
mq - > c [ Q_INO ] . timer = qdq - > d_ino_timer ;
2022-10-15 03:52:28 -04:00
if ( qdq & & qdq - > d_fieldmask & QC_INO_WARNS )
2023-07-06 22:47:42 -04:00
mq - > c [ Q_INO ] . warns = qdq - > d_ino_warns ;
2022-10-15 03:52:28 -04:00
2017-03-16 22:18:50 -08:00
mutex_unlock ( & q - > lock ) ;
}
return 0 ;
}
void bch2_fs_quota_exit ( struct bch_fs * c )
{
unsigned i ;
for ( i = 0 ; i < ARRAY_SIZE ( c - > quotas ) ; i + + )
genradix_free ( & c - > quotas [ i ] . table ) ;
}
void bch2_fs_quota_init ( struct bch_fs * c )
{
unsigned i ;
for ( i = 0 ; i < ARRAY_SIZE ( c - > quotas ) ; i + + )
mutex_init ( & c - > quotas [ i ] . lock ) ;
}
2022-10-12 14:47:58 -04:00
static struct bch_sb_field_quota * bch2_sb_get_or_create_quota ( struct bch_sb_handle * sb )
{
2023-09-26 17:49:34 -04:00
struct bch_sb_field_quota * sb_quota = bch2_sb_field_get ( sb - > sb , quota ) ;
2022-10-12 14:47:58 -04:00
if ( sb_quota )
return sb_quota ;
2023-09-26 17:49:34 -04:00
sb_quota = bch2_sb_field_resize ( sb , quota , sizeof ( * sb_quota ) / sizeof ( u64 ) ) ;
2022-10-12 14:47:58 -04:00
if ( sb_quota ) {
unsigned qtype , qc ;
for ( qtype = 0 ; qtype < QTYP_NR ; qtype + + )
for ( qc = 0 ; qc < Q_COUNTERS ; qc + + )
sb_quota - > q [ qtype ] . c [ qc ] . timelimit =
cpu_to_le32 ( 7 * 24 * 60 * 60 ) ;
}
return sb_quota ;
}
2017-03-16 22:18:50 -08:00
static void bch2_sb_quota_read ( struct bch_fs * c )
{
struct bch_sb_field_quota * sb_quota ;
unsigned i , j ;
2023-09-26 17:49:34 -04:00
sb_quota = bch2_sb_field_get ( c - > disk_sb . sb , quota ) ;
2017-03-16 22:18:50 -08:00
if ( ! sb_quota )
return ;
for ( i = 0 ; i < QTYP_NR ; i + + ) {
struct bch_memquota_type * q = & c - > quotas [ i ] ;
for ( j = 0 ; j < Q_COUNTERS ; j + + ) {
q - > limits [ j ] . timelimit =
le32_to_cpu ( sb_quota - > q [ i ] . c [ j ] . timelimit ) ;
q - > limits [ j ] . warnlimit =
le32_to_cpu ( sb_quota - > q [ i ] . c [ j ] . warnlimit ) ;
}
}
}
2021-10-27 13:05:56 -04:00
static int bch2_fs_quota_read_inode ( struct btree_trans * trans ,
2022-07-15 20:51:09 -04:00
struct btree_iter * iter ,
struct bkey_s_c k )
2021-10-27 13:05:56 -04:00
{
struct bch_fs * c = trans - > c ;
struct bch_inode_unpacked u ;
2023-04-28 03:50:57 -04:00
struct bch_snapshot_tree s_t ;
2021-10-27 13:05:56 -04:00
int ret ;
2023-04-28 03:50:57 -04:00
ret = bch2_snapshot_tree_lookup ( trans ,
2023-07-12 13:55:03 -04:00
bch2_snapshot_tree ( c , k . k - > p . snapshot ) , & s_t ) ;
2023-04-28 03:50:57 -04:00
bch2_fs_inconsistent_on ( bch2_err_matches ( ret , ENOENT ) , c ,
" %s: snapshot tree %u not found " , __func__ ,
snapshot_t ( c , k . k - > p . snapshot ) - > tree ) ;
2021-10-27 13:05:56 -04:00
if ( ret )
return ret ;
2023-04-28 03:50:57 -04:00
if ( ! s_t . master_subvol )
2021-10-27 13:05:56 -04:00
goto advance ;
2023-09-10 22:05:50 -04:00
ret = bch2_inode_find_by_inum_nowarn_trans ( trans ,
2023-04-28 03:50:57 -04:00
( subvol_inum ) {
le32_to_cpu ( s_t . master_subvol ) ,
k . k - > p . offset ,
} , & u ) ;
2023-05-27 20:00:13 -04:00
/*
* Inode might be deleted in this snapshot - the easiest way to handle
* that is to just skip it here :
*/
if ( bch2_err_matches ( ret , ENOENT ) )
goto advance ;
2021-10-27 13:05:56 -04:00
if ( ret )
return ret ;
bch2_quota_acct ( c , bch_qid ( & u ) , Q_SPC , u . bi_sectors ,
KEY_TYPE_QUOTA_NOCHECK ) ;
bch2_quota_acct ( c , bch_qid ( & u ) , Q_INO , 1 ,
KEY_TYPE_QUOTA_NOCHECK ) ;
advance :
2023-04-28 03:50:57 -04:00
bch2_btree_iter_set_pos ( iter , bpos_nosnap_successor ( iter - > pos ) ) ;
2021-10-27 13:05:56 -04:00
return 0 ;
}
2017-03-16 22:18:50 -08:00
int bch2_fs_quota_read ( struct bch_fs * c )
{
2022-10-12 14:47:58 -04:00
struct bch_sb_field_quota * sb_quota ;
2023-09-12 17:16:02 -04:00
struct btree_trans * trans ;
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2022-07-15 20:51:09 -04:00
struct bkey_s_c k ;
2017-03-16 22:18:50 -08:00
int ret ;
mutex_lock ( & c - > sb_lock ) ;
2022-10-12 14:47:58 -04:00
sb_quota = bch2_sb_get_or_create_quota ( & c - > disk_sb ) ;
if ( ! sb_quota ) {
mutex_unlock ( & c - > sb_lock ) ;
return - BCH_ERR_ENOSPC_sb_quota ;
}
2017-03-16 22:18:50 -08:00
bch2_sb_quota_read ( c ) ;
mutex_unlock ( & c - > sb_lock ) ;
2023-09-12 17:16:02 -04:00
trans = bch2_trans_get ( c ) ;
2019-03-25 15:10:15 -04:00
2023-09-12 17:16:02 -04:00
ret = for_each_btree_key2 ( trans , iter , BTREE_ID_quotas ,
2022-07-17 00:44:19 -04:00
POS_MIN , BTREE_ITER_PREFETCH , k ,
2022-10-15 03:52:28 -04:00
__bch2_quota_set ( c , k , NULL ) ) ? :
2023-09-12 17:16:02 -04:00
for_each_btree_key2 ( trans , iter , BTREE_ID_inodes ,
2022-07-17 00:44:19 -04:00
POS_MIN , BTREE_ITER_PREFETCH | BTREE_ITER_ALL_SNAPSHOTS , k ,
2023-09-12 17:16:02 -04:00
bch2_fs_quota_read_inode ( trans , & iter , k ) ) ;
2021-03-19 20:29:11 -04:00
2023-09-12 17:16:02 -04:00
bch2_trans_put ( trans ) ;
2023-06-20 13:49:25 -04:00
if ( ret )
bch_err_fn ( c , ret ) ;
2022-07-15 20:51:09 -04:00
return ret ;
2017-03-16 22:18:50 -08:00
}
/* Enable/disable/delete quotas for an entire filesystem: */
static int bch2_quota_enable ( struct super_block * sb , unsigned uflags )
{
struct bch_fs * c = sb - > s_fs_info ;
2022-10-12 14:47:58 -04:00
struct bch_sb_field_quota * sb_quota ;
int ret = 0 ;
2017-03-16 22:18:50 -08:00
if ( sb - > s_flags & SB_RDONLY )
return - EROFS ;
/* Accounting must be enabled at mount time: */
if ( uflags & ( FS_QUOTA_UDQ_ACCT | FS_QUOTA_GDQ_ACCT | FS_QUOTA_PDQ_ACCT ) )
return - EINVAL ;
/* Can't enable enforcement without accounting: */
if ( ( uflags & FS_QUOTA_UDQ_ENFD ) & & ! c - > opts . usrquota )
return - EINVAL ;
if ( ( uflags & FS_QUOTA_GDQ_ENFD ) & & ! c - > opts . grpquota )
return - EINVAL ;
if ( uflags & FS_QUOTA_PDQ_ENFD & & ! c - > opts . prjquota )
return - EINVAL ;
mutex_lock ( & c - > sb_lock ) ;
2022-10-12 14:47:58 -04:00
sb_quota = bch2_sb_get_or_create_quota ( & c - > disk_sb ) ;
if ( ! sb_quota ) {
ret = - BCH_ERR_ENOSPC_sb_quota ;
goto unlock ;
}
2017-03-16 22:18:50 -08:00
if ( uflags & FS_QUOTA_UDQ_ENFD )
SET_BCH_SB_USRQUOTA ( c - > disk_sb . sb , true ) ;
if ( uflags & FS_QUOTA_GDQ_ENFD )
SET_BCH_SB_GRPQUOTA ( c - > disk_sb . sb , true ) ;
if ( uflags & FS_QUOTA_PDQ_ENFD )
SET_BCH_SB_PRJQUOTA ( c - > disk_sb . sb , true ) ;
bch2_write_super ( c ) ;
2022-10-12 14:47:58 -04:00
unlock :
2017-03-16 22:18:50 -08:00
mutex_unlock ( & c - > sb_lock ) ;
2022-10-12 14:47:58 -04:00
return bch2_err_class ( ret ) ;
2017-03-16 22:18:50 -08:00
}
static int bch2_quota_disable ( struct super_block * sb , unsigned uflags )
{
struct bch_fs * c = sb - > s_fs_info ;
if ( sb - > s_flags & SB_RDONLY )
return - EROFS ;
mutex_lock ( & c - > sb_lock ) ;
if ( uflags & FS_QUOTA_UDQ_ENFD )
SET_BCH_SB_USRQUOTA ( c - > disk_sb . sb , false ) ;
if ( uflags & FS_QUOTA_GDQ_ENFD )
SET_BCH_SB_GRPQUOTA ( c - > disk_sb . sb , false ) ;
if ( uflags & FS_QUOTA_PDQ_ENFD )
SET_BCH_SB_PRJQUOTA ( c - > disk_sb . sb , false ) ;
bch2_write_super ( c ) ;
mutex_unlock ( & c - > sb_lock ) ;
return 0 ;
}
static int bch2_quota_remove ( struct super_block * sb , unsigned uflags )
{
struct bch_fs * c = sb - > s_fs_info ;
int ret ;
if ( sb - > s_flags & SB_RDONLY )
return - EROFS ;
if ( uflags & FS_USER_QUOTA ) {
if ( c - > opts . usrquota )
return - EINVAL ;
2021-02-20 19:27:37 -05:00
ret = bch2_btree_delete_range ( c , BTREE_ID_quotas ,
2017-03-16 22:18:50 -08:00
POS ( QTYP_USR , 0 ) ,
2022-10-11 04:32:41 -04:00
POS ( QTYP_USR , U64_MAX ) ,
2021-12-29 13:49:34 -05:00
0 , NULL ) ;
2017-03-16 22:18:50 -08:00
if ( ret )
return ret ;
}
if ( uflags & FS_GROUP_QUOTA ) {
if ( c - > opts . grpquota )
return - EINVAL ;
2021-02-20 19:27:37 -05:00
ret = bch2_btree_delete_range ( c , BTREE_ID_quotas ,
2017-03-16 22:18:50 -08:00
POS ( QTYP_GRP , 0 ) ,
2022-10-11 04:32:41 -04:00
POS ( QTYP_GRP , U64_MAX ) ,
2021-12-29 13:49:34 -05:00
0 , NULL ) ;
2017-03-16 22:18:50 -08:00
if ( ret )
return ret ;
}
if ( uflags & FS_PROJ_QUOTA ) {
if ( c - > opts . prjquota )
return - EINVAL ;
2021-02-20 19:27:37 -05:00
ret = bch2_btree_delete_range ( c , BTREE_ID_quotas ,
2017-03-16 22:18:50 -08:00
POS ( QTYP_PRJ , 0 ) ,
2022-10-11 04:32:41 -04:00
POS ( QTYP_PRJ , U64_MAX ) ,
2021-12-29 13:49:34 -05:00
0 , NULL ) ;
2017-03-16 22:18:50 -08:00
if ( ret )
return ret ;
}
return 0 ;
}
/*
* Return quota status information , such as enforcements , quota file inode
* numbers etc .
*/
static int bch2_quota_get_state ( struct super_block * sb , struct qc_state * state )
{
struct bch_fs * c = sb - > s_fs_info ;
unsigned qtypes = enabled_qtypes ( c ) ;
unsigned i ;
memset ( state , 0 , sizeof ( * state ) ) ;
for ( i = 0 ; i < QTYP_NR ; i + + ) {
state - > s_state [ i ] . flags | = QCI_SYSFILE ;
if ( ! ( qtypes & ( 1 < < i ) ) )
continue ;
state - > s_state [ i ] . flags | = QCI_ACCT_ENABLED ;
state - > s_state [ i ] . spc_timelimit = c - > quotas [ i ] . limits [ Q_SPC ] . timelimit ;
state - > s_state [ i ] . spc_warnlimit = c - > quotas [ i ] . limits [ Q_SPC ] . warnlimit ;
state - > s_state [ i ] . ino_timelimit = c - > quotas [ i ] . limits [ Q_INO ] . timelimit ;
state - > s_state [ i ] . ino_warnlimit = c - > quotas [ i ] . limits [ Q_INO ] . warnlimit ;
}
return 0 ;
}
/*
* Adjust quota timers & warnings
*/
static int bch2_quota_set_info ( struct super_block * sb , int type ,
struct qc_info * info )
{
struct bch_fs * c = sb - > s_fs_info ;
struct bch_sb_field_quota * sb_quota ;
2022-10-12 14:47:58 -04:00
int ret = 0 ;
if ( 0 ) {
struct printbuf buf = PRINTBUF ;
qc_info_to_text ( & buf , info ) ;
pr_info ( " setting: \n %s " , buf . buf ) ;
printbuf_exit ( & buf ) ;
}
2017-03-16 22:18:50 -08:00
if ( sb - > s_flags & SB_RDONLY )
return - EROFS ;
if ( type > = QTYP_NR )
return - EINVAL ;
if ( ! ( ( 1 < < type ) & enabled_qtypes ( c ) ) )
return - ESRCH ;
if ( info - > i_fieldmask &
~ ( QC_SPC_TIMER | QC_INO_TIMER | QC_SPC_WARNS | QC_INO_WARNS ) )
return - EINVAL ;
mutex_lock ( & c - > sb_lock ) ;
2022-10-12 14:47:58 -04:00
sb_quota = bch2_sb_get_or_create_quota ( & c - > disk_sb ) ;
2017-03-16 22:18:50 -08:00
if ( ! sb_quota ) {
2022-10-12 14:47:58 -04:00
ret = - BCH_ERR_ENOSPC_sb_quota ;
goto unlock ;
2017-03-16 22:18:50 -08:00
}
if ( info - > i_fieldmask & QC_SPC_TIMER )
sb_quota - > q [ type ] . c [ Q_SPC ] . timelimit =
cpu_to_le32 ( info - > i_spc_timelimit ) ;
if ( info - > i_fieldmask & QC_SPC_WARNS )
sb_quota - > q [ type ] . c [ Q_SPC ] . warnlimit =
cpu_to_le32 ( info - > i_spc_warnlimit ) ;
if ( info - > i_fieldmask & QC_INO_TIMER )
sb_quota - > q [ type ] . c [ Q_INO ] . timelimit =
cpu_to_le32 ( info - > i_ino_timelimit ) ;
if ( info - > i_fieldmask & QC_INO_WARNS )
sb_quota - > q [ type ] . c [ Q_INO ] . warnlimit =
cpu_to_le32 ( info - > i_ino_warnlimit ) ;
bch2_sb_quota_read ( c ) ;
bch2_write_super ( c ) ;
2022-10-12 14:47:58 -04:00
unlock :
2017-03-16 22:18:50 -08:00
mutex_unlock ( & c - > sb_lock ) ;
2022-10-12 14:47:58 -04:00
return bch2_err_class ( ret ) ;
2017-03-16 22:18:50 -08:00
}
/* Get/set individual quotas: */
static void __bch2_quota_get ( struct qc_dqblk * dst , struct bch_memquota * src )
{
dst - > d_space = src - > c [ Q_SPC ] . v < < 9 ;
dst - > d_spc_hardlimit = src - > c [ Q_SPC ] . hardlimit < < 9 ;
dst - > d_spc_softlimit = src - > c [ Q_SPC ] . softlimit < < 9 ;
dst - > d_spc_timer = src - > c [ Q_SPC ] . timer ;
dst - > d_spc_warns = src - > c [ Q_SPC ] . warns ;
dst - > d_ino_count = src - > c [ Q_INO ] . v ;
dst - > d_ino_hardlimit = src - > c [ Q_INO ] . hardlimit ;
dst - > d_ino_softlimit = src - > c [ Q_INO ] . softlimit ;
dst - > d_ino_timer = src - > c [ Q_INO ] . timer ;
dst - > d_ino_warns = src - > c [ Q_INO ] . warns ;
}
static int bch2_get_quota ( struct super_block * sb , struct kqid kqid ,
struct qc_dqblk * qdq )
{
struct bch_fs * c = sb - > s_fs_info ;
struct bch_memquota_type * q = & c - > quotas [ kqid . type ] ;
qid_t qid = from_kqid ( & init_user_ns , kqid ) ;
struct bch_memquota * mq ;
memset ( qdq , 0 , sizeof ( * qdq ) ) ;
mutex_lock ( & q - > lock ) ;
mq = genradix_ptr ( & q - > table , qid ) ;
if ( mq )
__bch2_quota_get ( qdq , mq ) ;
mutex_unlock ( & q - > lock ) ;
return 0 ;
}
static int bch2_get_next_quota ( struct super_block * sb , struct kqid * kqid ,
struct qc_dqblk * qdq )
{
struct bch_fs * c = sb - > s_fs_info ;
struct bch_memquota_type * q = & c - > quotas [ kqid - > type ] ;
qid_t qid = from_kqid ( & init_user_ns , * kqid ) ;
2018-11-24 17:09:44 -05:00
struct genradix_iter iter ;
2017-03-16 22:18:50 -08:00
struct bch_memquota * mq ;
int ret = 0 ;
mutex_lock ( & q - > lock ) ;
2018-11-24 17:09:44 -05:00
genradix_for_each_from ( & q - > table , iter , mq , qid )
2017-03-16 22:18:50 -08:00
if ( memcmp ( mq , page_address ( ZERO_PAGE ( 0 ) ) , sizeof ( * mq ) ) ) {
__bch2_quota_get ( qdq , mq ) ;
* kqid = make_kqid ( current_user_ns ( ) , kqid - > type , iter . pos ) ;
goto found ;
}
ret = - ENOENT ;
found :
mutex_unlock ( & q - > lock ) ;
2023-05-27 19:59:59 -04:00
return bch2_err_class ( ret ) ;
2017-03-16 22:18:50 -08:00
}
2020-05-13 17:53:33 -04:00
static int bch2_set_quota_trans ( struct btree_trans * trans ,
struct bkey_i_quota * new_quota ,
struct qc_dqblk * qdq )
2017-03-16 22:18:50 -08:00
{
2021-08-30 15:18:31 -04:00
struct btree_iter iter ;
2017-03-16 22:18:50 -08:00
struct bkey_s_c k ;
int ret ;
2023-04-29 19:33:09 -04:00
k = bch2_bkey_get_iter ( trans , & iter , BTREE_ID_quotas , new_quota - > k . p ,
BTREE_ITER_SLOTS | BTREE_ITER_INTENT ) ;
2019-03-27 22:03:30 -04:00
ret = bkey_err ( k ) ;
2017-03-16 22:18:50 -08:00
if ( unlikely ( ret ) )
return ret ;
2020-05-13 17:53:33 -04:00
if ( k . k - > type = = KEY_TYPE_quota )
new_quota - > v = * bkey_s_c_to_quota ( k ) . v ;
2017-03-16 22:18:50 -08:00
if ( qdq - > d_fieldmask & QC_SPC_SOFT )
2020-05-13 17:53:33 -04:00
new_quota - > v . c [ Q_SPC ] . softlimit = cpu_to_le64 ( qdq - > d_spc_softlimit > > 9 ) ;
2017-03-16 22:18:50 -08:00
if ( qdq - > d_fieldmask & QC_SPC_HARD )
2020-05-13 17:53:33 -04:00
new_quota - > v . c [ Q_SPC ] . hardlimit = cpu_to_le64 ( qdq - > d_spc_hardlimit > > 9 ) ;
2017-03-16 22:18:50 -08:00
if ( qdq - > d_fieldmask & QC_INO_SOFT )
2020-05-13 17:53:33 -04:00
new_quota - > v . c [ Q_INO ] . softlimit = cpu_to_le64 ( qdq - > d_ino_softlimit ) ;
2017-03-16 22:18:50 -08:00
if ( qdq - > d_fieldmask & QC_INO_HARD )
2020-05-13 17:53:33 -04:00
new_quota - > v . c [ Q_INO ] . hardlimit = cpu_to_le64 ( qdq - > d_ino_hardlimit ) ;
2021-08-30 15:18:31 -04:00
ret = bch2_trans_update ( trans , & iter , & new_quota - > k_i , 0 ) ;
bch2_trans_iter_exit ( trans , & iter ) ;
2021-03-19 20:29:11 -04:00
return ret ;
2020-05-13 17:53:33 -04:00
}
2017-03-16 22:18:50 -08:00
2020-05-13 17:53:33 -04:00
static int bch2_set_quota ( struct super_block * sb , struct kqid qid ,
struct qc_dqblk * qdq )
{
struct bch_fs * c = sb - > s_fs_info ;
struct bkey_i_quota new_quota ;
int ret ;
2019-03-13 20:49:16 -04:00
2022-10-12 14:47:58 -04:00
if ( 0 ) {
struct printbuf buf = PRINTBUF ;
qc_dqblk_to_text ( & buf , qdq ) ;
pr_info ( " setting: \n %s " , buf . buf ) ;
printbuf_exit ( & buf ) ;
}
2020-05-13 17:53:33 -04:00
if ( sb - > s_flags & SB_RDONLY )
return - EROFS ;
2019-03-13 20:49:16 -04:00
2020-05-13 17:53:33 -04:00
bkey_quota_init ( & new_quota . k_i ) ;
new_quota . k . p = POS ( qid . type , from_kqid ( & init_user_ns , qid ) ) ;
2017-03-16 22:18:50 -08:00
2021-07-27 22:15:04 -04:00
ret = bch2_trans_do ( c , NULL , NULL , 0 ,
2023-09-12 17:16:02 -04:00
bch2_set_quota_trans ( trans , & new_quota , qdq ) ) ? :
2022-10-15 03:52:28 -04:00
__bch2_quota_set ( c , bkey_i_to_s_c ( & new_quota . k_i ) , qdq ) ;
2020-05-13 17:53:33 -04:00
2023-05-27 19:59:59 -04:00
return bch2_err_class ( ret ) ;
2017-03-16 22:18:50 -08:00
}
const struct quotactl_ops bch2_quotactl_operations = {
. quota_enable = bch2_quota_enable ,
. quota_disable = bch2_quota_disable ,
. rm_xquota = bch2_quota_remove ,
. get_state = bch2_quota_get_state ,
. set_info = bch2_quota_set_info ,
. get_dqblk = bch2_get_quota ,
. get_nextdqblk = bch2_get_next_quota ,
. set_dqblk = bch2_set_quota ,
} ;
# endif /* CONFIG_BCACHEFS_QUOTA */