2008-08-25 21:56:50 +04:00
/*
* Implementation of operations over global quota file
*/
2008-10-20 17:36:47 +04:00
# include <linux/spinlock.h>
2008-08-25 21:56:50 +04:00
# include <linux/fs.h>
# include <linux/quota.h>
# include <linux/quotaops.h>
# include <linux/dqblk_qtree.h>
2008-10-20 17:36:47 +04:00
# include <linux/jiffies.h>
# include <linux/writeback.h>
# include <linux/workqueue.h>
2008-08-25 21:56:50 +04:00
# define MLOG_MASK_PREFIX ML_QUOTA
# include <cluster/masklog.h>
# include "ocfs2_fs.h"
# include "ocfs2.h"
# include "alloc.h"
2008-10-18 01:55:01 +04:00
# include "blockcheck.h"
2008-08-25 21:56:50 +04:00
# include "inode.h"
# include "journal.h"
# include "file.h"
# include "sysfile.h"
# include "dlmglue.h"
# include "uptodate.h"
2009-08-03 20:24:21 +04:00
# include "super.h"
2008-08-25 21:56:50 +04:00
# include "quota.h"
2008-10-20 17:36:47 +04:00
static struct workqueue_struct * ocfs2_quota_wq = NULL ;
static void qsync_work_fn ( struct work_struct * work ) ;
2008-08-25 21:56:50 +04:00
static void ocfs2_global_disk2memdqb ( struct dquot * dquot , void * dp )
{
struct ocfs2_global_disk_dqblk * d = dp ;
struct mem_dqblk * m = & dquot - > dq_dqb ;
/* Update from disk only entries not set by the admin */
if ( ! test_bit ( DQ_LASTSET_B + QIF_ILIMITS_B , & dquot - > dq_flags ) ) {
m - > dqb_ihardlimit = le64_to_cpu ( d - > dqb_ihardlimit ) ;
m - > dqb_isoftlimit = le64_to_cpu ( d - > dqb_isoftlimit ) ;
}
if ( ! test_bit ( DQ_LASTSET_B + QIF_INODES_B , & dquot - > dq_flags ) )
m - > dqb_curinodes = le64_to_cpu ( d - > dqb_curinodes ) ;
if ( ! test_bit ( DQ_LASTSET_B + QIF_BLIMITS_B , & dquot - > dq_flags ) ) {
m - > dqb_bhardlimit = le64_to_cpu ( d - > dqb_bhardlimit ) ;
m - > dqb_bsoftlimit = le64_to_cpu ( d - > dqb_bsoftlimit ) ;
}
if ( ! test_bit ( DQ_LASTSET_B + QIF_SPACE_B , & dquot - > dq_flags ) )
m - > dqb_curspace = le64_to_cpu ( d - > dqb_curspace ) ;
if ( ! test_bit ( DQ_LASTSET_B + QIF_BTIME_B , & dquot - > dq_flags ) )
m - > dqb_btime = le64_to_cpu ( d - > dqb_btime ) ;
if ( ! test_bit ( DQ_LASTSET_B + QIF_ITIME_B , & dquot - > dq_flags ) )
m - > dqb_itime = le64_to_cpu ( d - > dqb_itime ) ;
OCFS2_DQUOT ( dquot ) - > dq_use_count = le32_to_cpu ( d - > dqb_use_count ) ;
}
static void ocfs2_global_mem2diskdqb ( void * dp , struct dquot * dquot )
{
struct ocfs2_global_disk_dqblk * d = dp ;
struct mem_dqblk * m = & dquot - > dq_dqb ;
d - > dqb_id = cpu_to_le32 ( dquot - > dq_id ) ;
d - > dqb_use_count = cpu_to_le32 ( OCFS2_DQUOT ( dquot ) - > dq_use_count ) ;
d - > dqb_ihardlimit = cpu_to_le64 ( m - > dqb_ihardlimit ) ;
d - > dqb_isoftlimit = cpu_to_le64 ( m - > dqb_isoftlimit ) ;
d - > dqb_curinodes = cpu_to_le64 ( m - > dqb_curinodes ) ;
d - > dqb_bhardlimit = cpu_to_le64 ( m - > dqb_bhardlimit ) ;
d - > dqb_bsoftlimit = cpu_to_le64 ( m - > dqb_bsoftlimit ) ;
d - > dqb_curspace = cpu_to_le64 ( m - > dqb_curspace ) ;
d - > dqb_btime = cpu_to_le64 ( m - > dqb_btime ) ;
d - > dqb_itime = cpu_to_le64 ( m - > dqb_itime ) ;
2009-07-22 15:17:18 +04:00
d - > dqb_pad1 = d - > dqb_pad2 = 0 ;
2008-08-25 21:56:50 +04:00
}
static int ocfs2_global_is_id ( void * dp , struct dquot * dquot )
{
struct ocfs2_global_disk_dqblk * d = dp ;
struct ocfs2_mem_dqinfo * oinfo =
sb_dqinfo ( dquot - > dq_sb , dquot - > dq_type ) - > dqi_priv ;
if ( qtree_entry_unused ( & oinfo - > dqi_gi , dp ) )
return 0 ;
return le32_to_cpu ( d - > dqb_id ) = = dquot - > dq_id ;
}
struct qtree_fmt_operations ocfs2_global_ops = {
. mem2disk_dqblk = ocfs2_global_mem2diskdqb ,
. disk2mem_dqblk = ocfs2_global_disk2memdqb ,
. is_id = ocfs2_global_is_id ,
} ;
2008-12-03 04:44:05 +03:00
static int ocfs2_validate_quota_block ( struct super_block * sb ,
struct buffer_head * bh )
{
2008-10-18 01:55:01 +04:00
struct ocfs2_disk_dqtrailer * dqt =
ocfs2_block_dqtrailer ( sb - > s_blocksize , bh - > b_data ) ;
2008-12-03 04:44:05 +03:00
mlog ( 0 , " Validating quota block %llu \n " ,
( unsigned long long ) bh - > b_blocknr ) ;
2008-10-18 01:55:01 +04:00
BUG_ON ( ! buffer_uptodate ( bh ) ) ;
/*
* If the ecc fails , we return the error but otherwise
* leave the filesystem running . We know any error is
* local to this block .
*/
return ocfs2_validate_meta_ecc ( sb , bh - > b_data , & dqt - > dq_check ) ;
2008-12-03 04:44:05 +03:00
}
2008-11-25 17:31:27 +03:00
int ocfs2_read_quota_block ( struct inode * inode , u64 v_block ,
struct buffer_head * * bh )
2008-08-25 21:56:50 +04:00
{
2008-11-25 17:31:27 +03:00
int rc = 0 ;
struct buffer_head * tmp = * bh ;
2008-08-25 21:56:50 +04:00
2009-08-03 20:24:21 +04:00
if ( i_size_read ( inode ) > > inode - > i_sb - > s_blocksize_bits < = v_block ) {
ocfs2_error ( inode - > i_sb ,
" Quota file %llu is probably corrupted! Requested "
" to read block %Lu but file has size only %Lu \n " ,
( unsigned long long ) OCFS2_I ( inode ) - > ip_blkno ,
( unsigned long long ) v_block ,
( unsigned long long ) i_size_read ( inode ) ) ;
return - EIO ;
}
2008-12-03 04:44:05 +03:00
rc = ocfs2_read_virt_blocks ( inode , v_block , 1 , & tmp , 0 ,
ocfs2_validate_quota_block ) ;
2008-11-25 17:31:27 +03:00
if ( rc )
mlog_errno ( rc ) ;
/* If ocfs2_read_virt_blocks() got us a new bh, pass it up. */
if ( ! rc & & ! * bh )
* bh = tmp ;
2008-08-25 21:56:50 +04:00
2008-11-25 17:31:27 +03:00
return rc ;
2008-08-25 21:56:50 +04:00
}
2008-11-25 17:31:29 +03:00
static int ocfs2_get_quota_block ( struct inode * inode , int block ,
struct buffer_head * * bh )
2008-08-25 21:56:50 +04:00
{
u64 pblock , pcount ;
2008-11-25 17:31:29 +03:00
int err ;
2008-08-25 21:56:50 +04:00
down_read ( & OCFS2_I ( inode ) - > ip_alloc_sem ) ;
2008-11-25 17:31:29 +03:00
err = ocfs2_extent_map_get_blocks ( inode , block , & pblock , & pcount , NULL ) ;
2008-08-25 21:56:50 +04:00
up_read ( & OCFS2_I ( inode ) - > ip_alloc_sem ) ;
2008-11-25 17:31:29 +03:00
if ( err ) {
mlog_errno ( err ) ;
return err ;
2008-08-25 21:56:50 +04:00
}
2008-11-25 17:31:29 +03:00
* bh = sb_getblk ( inode - > i_sb , pblock ) ;
if ( ! * bh ) {
err = - EIO ;
mlog_errno ( err ) ;
2008-08-25 21:56:50 +04:00
}
2008-11-25 17:31:29 +03:00
return err ; ;
2008-08-25 21:56:50 +04:00
}
/* Read data from global quotafile - avoid pagecache and such because we cannot
* afford acquiring the locks . . . We use quota cluster lock to serialize
* operations . Caller is responsible for acquiring it . */
ssize_t ocfs2_quota_read ( struct super_block * sb , int type , char * data ,
size_t len , loff_t off )
{
struct ocfs2_mem_dqinfo * oinfo = sb_dqinfo ( sb , type ) - > dqi_priv ;
struct inode * gqinode = oinfo - > dqi_gqinode ;
loff_t i_size = i_size_read ( gqinode ) ;
int offset = off & ( sb - > s_blocksize - 1 ) ;
sector_t blk = off > > sb - > s_blocksize_bits ;
int err = 0 ;
struct buffer_head * bh ;
size_t toread , tocopy ;
if ( off > i_size )
return 0 ;
if ( off + len > i_size )
len = i_size - off ;
toread = len ;
while ( toread > 0 ) {
2008-12-25 03:33:08 +03:00
tocopy = min_t ( size_t , ( sb - > s_blocksize - offset ) , toread ) ;
2008-11-25 17:31:27 +03:00
bh = NULL ;
err = ocfs2_read_quota_block ( gqinode , blk , & bh ) ;
if ( err ) {
2008-08-25 21:56:50 +04:00
mlog_errno ( err ) ;
return err ;
}
memcpy ( data , bh - > b_data + offset , tocopy ) ;
brelse ( bh ) ;
offset = 0 ;
toread - = tocopy ;
data + = tocopy ;
blk + + ;
}
return len ;
}
/* Write to quotafile (we know the transaction is already started and has
* enough credits ) */
ssize_t ocfs2_quota_write ( struct super_block * sb , int type ,
const char * data , size_t len , loff_t off )
{
struct mem_dqinfo * info = sb_dqinfo ( sb , type ) ;
struct ocfs2_mem_dqinfo * oinfo = info - > dqi_priv ;
struct inode * gqinode = oinfo - > dqi_gqinode ;
int offset = off & ( sb - > s_blocksize - 1 ) ;
sector_t blk = off > > sb - > s_blocksize_bits ;
2008-11-25 17:31:28 +03:00
int err = 0 , new = 0 , ja_type ;
2008-11-25 17:31:27 +03:00
struct buffer_head * bh = NULL ;
2008-08-25 21:56:50 +04:00
handle_t * handle = journal_current_handle ( ) ;
if ( ! handle ) {
mlog ( ML_ERROR , " Quota write (off=%llu, len=%llu) cancelled "
" because transaction was not started. \n " ,
( unsigned long long ) off , ( unsigned long long ) len ) ;
return - EIO ;
}
if ( len > sb - > s_blocksize - OCFS2_QBLK_RESERVED_SPACE - offset ) {
WARN_ON ( 1 ) ;
len = sb - > s_blocksize - OCFS2_QBLK_RESERVED_SPACE - offset ;
}
mutex_lock_nested ( & gqinode - > i_mutex , I_MUTEX_QUOTA ) ;
if ( gqinode - > i_size < off + len ) {
2009-07-22 15:17:15 +04:00
loff_t rounded_end =
ocfs2_align_bytes_to_blocks ( sb , off + len ) ;
2009-08-07 01:29:34 +04:00
/* Space is already allocated in ocfs2_global_read_dquot() */
2008-08-25 21:56:50 +04:00
err = ocfs2_simple_size_update ( gqinode ,
oinfo - > dqi_gqi_bh ,
2009-07-22 15:17:15 +04:00
rounded_end ) ;
2008-08-25 21:56:50 +04:00
if ( err < 0 )
goto out ;
new = 1 ;
}
/* Not rewriting whole block? */
if ( ( offset | | len < sb - > s_blocksize - OCFS2_QBLK_RESERVED_SPACE ) & &
! new ) {
2008-11-25 17:31:27 +03:00
err = ocfs2_read_quota_block ( gqinode , blk , & bh ) ;
2008-11-25 17:31:28 +03:00
ja_type = OCFS2_JOURNAL_ACCESS_WRITE ;
2008-08-25 21:56:50 +04:00
} else {
2008-11-25 17:31:29 +03:00
err = ocfs2_get_quota_block ( gqinode , blk , & bh ) ;
2008-11-25 17:31:28 +03:00
ja_type = OCFS2_JOURNAL_ACCESS_CREATE ;
2008-08-25 21:56:50 +04:00
}
2008-11-25 17:31:28 +03:00
if ( err ) {
mlog_errno ( err ) ;
2009-07-30 12:07:10 +04:00
goto out ;
2008-08-25 21:56:50 +04:00
}
lock_buffer ( bh ) ;
if ( new )
memset ( bh - > b_data , 0 , sb - > s_blocksize ) ;
memcpy ( bh - > b_data + offset , data , len ) ;
flush_dcache_page ( bh - > b_page ) ;
2008-11-25 17:31:28 +03:00
set_buffer_uptodate ( bh ) ;
2008-08-25 21:56:50 +04:00
unlock_buffer ( bh ) ;
ocfs2_set_buffer_uptodate ( gqinode , bh ) ;
2008-10-18 06:25:01 +04:00
err = ocfs2_journal_access_dq ( handle , gqinode , bh , ja_type ) ;
2008-11-25 17:31:28 +03:00
if ( err < 0 ) {
brelse ( bh ) ;
goto out ;
}
2008-08-25 21:56:50 +04:00
err = ocfs2_journal_dirty ( handle , bh ) ;
brelse ( bh ) ;
if ( err < 0 )
goto out ;
out :
if ( err ) {
mutex_unlock ( & gqinode - > i_mutex ) ;
mlog_errno ( err ) ;
return err ;
}
gqinode - > i_version + + ;
ocfs2_mark_inode_dirty ( handle , gqinode , oinfo - > dqi_gqi_bh ) ;
mutex_unlock ( & gqinode - > i_mutex ) ;
return len ;
}
int ocfs2_lock_global_qf ( struct ocfs2_mem_dqinfo * oinfo , int ex )
{
int status ;
struct buffer_head * bh = NULL ;
status = ocfs2_inode_lock ( oinfo - > dqi_gqinode , & bh , ex ) ;
if ( status < 0 )
return status ;
spin_lock ( & dq_data_lock ) ;
if ( ! oinfo - > dqi_gqi_count + + )
oinfo - > dqi_gqi_bh = bh ;
else
WARN_ON ( bh ! = oinfo - > dqi_gqi_bh ) ;
spin_unlock ( & dq_data_lock ) ;
return 0 ;
}
void ocfs2_unlock_global_qf ( struct ocfs2_mem_dqinfo * oinfo , int ex )
{
ocfs2_inode_unlock ( oinfo - > dqi_gqinode , ex ) ;
brelse ( oinfo - > dqi_gqi_bh ) ;
spin_lock ( & dq_data_lock ) ;
if ( ! - - oinfo - > dqi_gqi_count )
oinfo - > dqi_gqi_bh = NULL ;
spin_unlock ( & dq_data_lock ) ;
}
/* Read information header from global quota file */
int ocfs2_global_read_info ( struct super_block * sb , int type )
{
struct inode * gqinode = NULL ;
unsigned int ino [ MAXQUOTAS ] = { USER_QUOTA_SYSTEM_INODE ,
GROUP_QUOTA_SYSTEM_INODE } ;
struct ocfs2_global_disk_dqinfo dinfo ;
struct mem_dqinfo * info = sb_dqinfo ( sb , type ) ;
struct ocfs2_mem_dqinfo * oinfo = info - > dqi_priv ;
int status ;
mlog_entry_void ( ) ;
/* Read global header */
gqinode = ocfs2_get_system_file_inode ( OCFS2_SB ( sb ) , ino [ type ] ,
OCFS2_INVALID_SLOT ) ;
if ( ! gqinode ) {
mlog ( ML_ERROR , " failed to get global quota inode (type=%d) \n " ,
type ) ;
status = - EINVAL ;
goto out_err ;
}
oinfo - > dqi_gi . dqi_sb = sb ;
oinfo - > dqi_gi . dqi_type = type ;
ocfs2_qinfo_lock_res_init ( & oinfo - > dqi_gqlock , oinfo ) ;
oinfo - > dqi_gi . dqi_entry_size = sizeof ( struct ocfs2_global_disk_dqblk ) ;
oinfo - > dqi_gi . dqi_ops = & ocfs2_global_ops ;
oinfo - > dqi_gqi_bh = NULL ;
oinfo - > dqi_gqi_count = 0 ;
oinfo - > dqi_gqinode = gqinode ;
status = ocfs2_lock_global_qf ( oinfo , 0 ) ;
if ( status < 0 ) {
mlog_errno ( status ) ;
goto out_err ;
}
status = sb - > s_op - > quota_read ( sb , type , ( char * ) & dinfo ,
sizeof ( struct ocfs2_global_disk_dqinfo ) ,
OCFS2_GLOBAL_INFO_OFF ) ;
ocfs2_unlock_global_qf ( oinfo , 0 ) ;
if ( status ! = sizeof ( struct ocfs2_global_disk_dqinfo ) ) {
mlog ( ML_ERROR , " Cannot read global quota info (%d). \n " ,
status ) ;
if ( status > = 0 )
status = - EIO ;
mlog_errno ( status ) ;
goto out_err ;
}
info - > dqi_bgrace = le32_to_cpu ( dinfo . dqi_bgrace ) ;
info - > dqi_igrace = le32_to_cpu ( dinfo . dqi_igrace ) ;
oinfo - > dqi_syncms = le32_to_cpu ( dinfo . dqi_syncms ) ;
oinfo - > dqi_gi . dqi_blocks = le32_to_cpu ( dinfo . dqi_blocks ) ;
oinfo - > dqi_gi . dqi_free_blk = le32_to_cpu ( dinfo . dqi_free_blk ) ;
oinfo - > dqi_gi . dqi_free_entry = le32_to_cpu ( dinfo . dqi_free_entry ) ;
oinfo - > dqi_gi . dqi_blocksize_bits = sb - > s_blocksize_bits ;
oinfo - > dqi_gi . dqi_usable_bs = sb - > s_blocksize -
OCFS2_QBLK_RESERVED_SPACE ;
oinfo - > dqi_gi . dqi_qtree_depth = qtree_depth ( & oinfo - > dqi_gi ) ;
2008-10-20 17:36:47 +04:00
INIT_DELAYED_WORK ( & oinfo - > dqi_sync_work , qsync_work_fn ) ;
queue_delayed_work ( ocfs2_quota_wq , & oinfo - > dqi_sync_work ,
2009-07-22 15:17:20 +04:00
msecs_to_jiffies ( oinfo - > dqi_syncms ) ) ;
2008-10-20 17:36:47 +04:00
2008-08-25 21:56:50 +04:00
out_err :
mlog_exit ( status ) ;
return status ;
}
/* Write information to global quota file. Expects exlusive lock on quota
* file inode and quota info */
static int __ocfs2_global_write_info ( struct super_block * sb , int type )
{
struct mem_dqinfo * info = sb_dqinfo ( sb , type ) ;
struct ocfs2_mem_dqinfo * oinfo = info - > dqi_priv ;
struct ocfs2_global_disk_dqinfo dinfo ;
ssize_t size ;
spin_lock ( & dq_data_lock ) ;
info - > dqi_flags & = ~ DQF_INFO_DIRTY ;
dinfo . dqi_bgrace = cpu_to_le32 ( info - > dqi_bgrace ) ;
dinfo . dqi_igrace = cpu_to_le32 ( info - > dqi_igrace ) ;
spin_unlock ( & dq_data_lock ) ;
dinfo . dqi_syncms = cpu_to_le32 ( oinfo - > dqi_syncms ) ;
dinfo . dqi_blocks = cpu_to_le32 ( oinfo - > dqi_gi . dqi_blocks ) ;
dinfo . dqi_free_blk = cpu_to_le32 ( oinfo - > dqi_gi . dqi_free_blk ) ;
dinfo . dqi_free_entry = cpu_to_le32 ( oinfo - > dqi_gi . dqi_free_entry ) ;
size = sb - > s_op - > quota_write ( sb , type , ( char * ) & dinfo ,
sizeof ( struct ocfs2_global_disk_dqinfo ) ,
OCFS2_GLOBAL_INFO_OFF ) ;
if ( size ! = sizeof ( struct ocfs2_global_disk_dqinfo ) ) {
mlog ( ML_ERROR , " Cannot write global quota info structure \n " ) ;
if ( size > = 0 )
size = - EIO ;
return size ;
}
return 0 ;
}
int ocfs2_global_write_info ( struct super_block * sb , int type )
{
int err ;
struct ocfs2_mem_dqinfo * info = sb_dqinfo ( sb , type ) - > dqi_priv ;
err = ocfs2_qinfo_lock ( info , 1 ) ;
if ( err < 0 )
return err ;
err = __ocfs2_global_write_info ( sb , type ) ;
ocfs2_qinfo_unlock ( info , 1 ) ;
return err ;
}
2009-08-07 01:29:34 +04:00
static int ocfs2_global_qinit_alloc ( struct super_block * sb , int type )
{
struct ocfs2_mem_dqinfo * oinfo = sb_dqinfo ( sb , type ) - > dqi_priv ;
/*
* We may need to allocate tree blocks and a leaf block but not the
* root block
*/
return oinfo - > dqi_gi . dqi_qtree_depth ;
}
static int ocfs2_calc_global_qinit_credits ( struct super_block * sb , int type )
{
/* We modify all the allocated blocks, tree root, and info block */
return ( ocfs2_global_qinit_alloc ( sb , type ) + 2 ) *
OCFS2_QUOTA_BLOCK_UPDATE_CREDITS ;
}
2008-08-25 21:56:50 +04:00
/* Read in information from global quota file and acquire a reference to it.
* dquot_acquire ( ) has already started the transaction and locked quota file */
int ocfs2_global_read_dquot ( struct dquot * dquot )
{
int err , err2 , ex = 0 ;
2009-08-07 01:29:34 +04:00
struct super_block * sb = dquot - > dq_sb ;
int type = dquot - > dq_type ;
struct ocfs2_mem_dqinfo * info = sb_dqinfo ( sb , type ) - > dqi_priv ;
struct ocfs2_super * osb = OCFS2_SB ( sb ) ;
struct inode * gqinode = info - > dqi_gqinode ;
int need_alloc = ocfs2_global_qinit_alloc ( sb , type ) ;
handle_t * handle = NULL ;
2008-08-25 21:56:50 +04:00
err = ocfs2_qinfo_lock ( info , 0 ) ;
if ( err < 0 )
goto out ;
err = qtree_read_dquot ( & info - > dqi_gi , dquot ) ;
if ( err < 0 )
goto out_qlock ;
OCFS2_DQUOT ( dquot ) - > dq_use_count + + ;
OCFS2_DQUOT ( dquot ) - > dq_origspace = dquot - > dq_dqb . dqb_curspace ;
OCFS2_DQUOT ( dquot ) - > dq_originodes = dquot - > dq_dqb . dqb_curinodes ;
2009-08-07 01:29:34 +04:00
ocfs2_qinfo_unlock ( info , 0 ) ;
2008-08-25 21:56:50 +04:00
if ( ! dquot - > dq_off ) { /* No real quota entry? */
ex = 1 ;
2009-08-07 01:29:34 +04:00
/*
* Add blocks to quota file before we start a transaction since
* locking allocators ranks above a transaction start
*/
WARN_ON ( journal_current_handle ( ) ) ;
down_write ( & OCFS2_I ( gqinode ) - > ip_alloc_sem ) ;
err = ocfs2_extend_no_holes ( gqinode ,
gqinode - > i_size + ( need_alloc < < sb - > s_blocksize_bits ) ,
gqinode - > i_size ) ;
up_write ( & OCFS2_I ( gqinode ) - > ip_alloc_sem ) ;
if ( err < 0 )
goto out ;
2008-08-25 21:56:50 +04:00
}
2009-08-07 01:29:34 +04:00
handle = ocfs2_start_trans ( osb ,
ocfs2_calc_global_qinit_credits ( sb , type ) ) ;
if ( IS_ERR ( handle ) ) {
err = PTR_ERR ( handle ) ;
goto out ;
}
err = ocfs2_qinfo_lock ( info , ex ) ;
if ( err < 0 )
goto out_trans ;
2008-08-25 21:56:50 +04:00
err = qtree_write_dquot ( & info - > dqi_gi , dquot ) ;
if ( ex & & info_dirty ( sb_dqinfo ( dquot - > dq_sb , dquot - > dq_type ) ) ) {
err2 = __ocfs2_global_write_info ( dquot - > dq_sb , dquot - > dq_type ) ;
if ( ! err )
err = err2 ;
}
out_qlock :
if ( ex )
ocfs2_qinfo_unlock ( info , 1 ) ;
2009-06-02 16:23:59 +04:00
else
ocfs2_qinfo_unlock ( info , 0 ) ;
2009-08-07 01:29:34 +04:00
out_trans :
if ( handle )
ocfs2_commit_trans ( osb , handle ) ;
2008-08-25 21:56:50 +04:00
out :
if ( err < 0 )
mlog_errno ( err ) ;
return err ;
}
/* Sync local information about quota modifications with global quota file.
* Caller must have started the transaction and obtained exclusive lock for
* global quota file inode */
int __ocfs2_sync_dquot ( struct dquot * dquot , int freeing )
{
int err , err2 ;
struct super_block * sb = dquot - > dq_sb ;
int type = dquot - > dq_type ;
struct ocfs2_mem_dqinfo * info = sb_dqinfo ( sb , type ) - > dqi_priv ;
struct ocfs2_global_disk_dqblk dqblk ;
s64 spacechange , inodechange ;
time_t olditime , oldbtime ;
err = sb - > s_op - > quota_read ( sb , type , ( char * ) & dqblk ,
sizeof ( struct ocfs2_global_disk_dqblk ) ,
dquot - > dq_off ) ;
if ( err ! = sizeof ( struct ocfs2_global_disk_dqblk ) ) {
if ( err > = 0 ) {
mlog ( ML_ERROR , " Short read from global quota file "
" (%u read) \n " , err ) ;
err = - EIO ;
}
goto out ;
}
/* Update space and inode usage. Get also other information from
* global quota file so that we don ' t overwrite any changes there .
* We are */
spin_lock ( & dq_data_lock ) ;
spacechange = dquot - > dq_dqb . dqb_curspace -
OCFS2_DQUOT ( dquot ) - > dq_origspace ;
inodechange = dquot - > dq_dqb . dqb_curinodes -
OCFS2_DQUOT ( dquot ) - > dq_originodes ;
olditime = dquot - > dq_dqb . dqb_itime ;
oldbtime = dquot - > dq_dqb . dqb_btime ;
ocfs2_global_disk2memdqb ( dquot , & dqblk ) ;
ocfs2: Fix build warnings (64-bit types vs long long)
fs/ocfs2/quota_local.c: In function 'olq_set_dquot':
fs/ocfs2/quota_local.c:844: warning: format '%lld' expects type 'long long int', but argument 7 has type '__le64'
fs/ocfs2/quota_local.c:844: warning: format '%lld' expects type 'long long int', but argument 8 has type '__le64'
fs/ocfs2/quota_local.c:844: warning: format '%lld' expects type 'long long int', but argument 7 has type '__le64'
fs/ocfs2/quota_local.c:844: warning: format '%lld' expects type 'long long int', but argument 8 has type '__le64'
fs/ocfs2/quota_local.c:844: warning: format '%lld' expects type 'long long int', but argument 7 has type '__le64'
fs/ocfs2/quota_local.c:844: warning: format '%lld' expects type 'long long int', but argument 8 has type '__le64'
fs/ocfs2/quota_global.c: In function '__ocfs2_sync_dquot':
fs/ocfs2/quota_global.c:457: warning: format '%lld' expects type 'long long int', but argument 8 has type 's64'
fs/ocfs2/quota_global.c:457: warning: format '%lld' expects type 'long long int', but argument 10 has type 's64'
fs/ocfs2/quota_global.c:457: warning: format '%lld' expects type 'long long int', but argument 8 has type 's64'
fs/ocfs2/quota_global.c:457: warning: format '%lld' expects type 'long long int', but argument 10 has type 's64'
fs/ocfs2/quota_global.c:457: warning: format '%lld' expects type 'long long int', but argument 8 has type 's64'
fs/ocfs2/quota_global.c:457: warning: format '%lld' expects type 'long long int', but argument 10 has type 's64'
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
2008-11-25 17:31:30 +03:00
mlog ( 0 , " Syncing global dquot %u space %lld+%lld, inodes %lld+%lld \n " ,
dquot - > dq_id , dquot - > dq_dqb . dqb_curspace , ( long long ) spacechange ,
dquot - > dq_dqb . dqb_curinodes , ( long long ) inodechange ) ;
2008-08-25 21:56:50 +04:00
if ( ! test_bit ( DQ_LASTSET_B + QIF_SPACE_B , & dquot - > dq_flags ) )
dquot - > dq_dqb . dqb_curspace + = spacechange ;
if ( ! test_bit ( DQ_LASTSET_B + QIF_INODES_B , & dquot - > dq_flags ) )
dquot - > dq_dqb . dqb_curinodes + = inodechange ;
/* Set properly space grace time... */
if ( dquot - > dq_dqb . dqb_bsoftlimit & &
dquot - > dq_dqb . dqb_curspace > dquot - > dq_dqb . dqb_bsoftlimit ) {
if ( ! test_bit ( DQ_LASTSET_B + QIF_BTIME_B , & dquot - > dq_flags ) & &
oldbtime > 0 ) {
if ( dquot - > dq_dqb . dqb_btime > 0 )
dquot - > dq_dqb . dqb_btime =
min ( dquot - > dq_dqb . dqb_btime , oldbtime ) ;
else
dquot - > dq_dqb . dqb_btime = oldbtime ;
}
} else {
dquot - > dq_dqb . dqb_btime = 0 ;
clear_bit ( DQ_BLKS_B , & dquot - > dq_flags ) ;
}
/* Set properly inode grace time... */
if ( dquot - > dq_dqb . dqb_isoftlimit & &
dquot - > dq_dqb . dqb_curinodes > dquot - > dq_dqb . dqb_isoftlimit ) {
if ( ! test_bit ( DQ_LASTSET_B + QIF_ITIME_B , & dquot - > dq_flags ) & &
olditime > 0 ) {
if ( dquot - > dq_dqb . dqb_itime > 0 )
dquot - > dq_dqb . dqb_itime =
min ( dquot - > dq_dqb . dqb_itime , olditime ) ;
else
dquot - > dq_dqb . dqb_itime = olditime ;
}
} else {
dquot - > dq_dqb . dqb_itime = 0 ;
clear_bit ( DQ_INODES_B , & dquot - > dq_flags ) ;
}
/* All information is properly updated, clear the flags */
__clear_bit ( DQ_LASTSET_B + QIF_SPACE_B , & dquot - > dq_flags ) ;
__clear_bit ( DQ_LASTSET_B + QIF_INODES_B , & dquot - > dq_flags ) ;
__clear_bit ( DQ_LASTSET_B + QIF_BLIMITS_B , & dquot - > dq_flags ) ;
__clear_bit ( DQ_LASTSET_B + QIF_ILIMITS_B , & dquot - > dq_flags ) ;
__clear_bit ( DQ_LASTSET_B + QIF_BTIME_B , & dquot - > dq_flags ) ;
__clear_bit ( DQ_LASTSET_B + QIF_ITIME_B , & dquot - > dq_flags ) ;
OCFS2_DQUOT ( dquot ) - > dq_origspace = dquot - > dq_dqb . dqb_curspace ;
OCFS2_DQUOT ( dquot ) - > dq_originodes = dquot - > dq_dqb . dqb_curinodes ;
spin_unlock ( & dq_data_lock ) ;
err = ocfs2_qinfo_lock ( info , freeing ) ;
if ( err < 0 ) {
mlog ( ML_ERROR , " Failed to lock quota info, loosing quota write "
" (type=%d, id=%u) \n " , dquot - > dq_type ,
( unsigned ) dquot - > dq_id ) ;
goto out ;
}
if ( freeing )
OCFS2_DQUOT ( dquot ) - > dq_use_count - - ;
err = qtree_write_dquot ( & info - > dqi_gi , dquot ) ;
if ( err < 0 )
goto out_qlock ;
if ( freeing & & ! OCFS2_DQUOT ( dquot ) - > dq_use_count ) {
err = qtree_release_dquot ( & info - > dqi_gi , dquot ) ;
if ( info_dirty ( sb_dqinfo ( sb , type ) ) ) {
err2 = __ocfs2_global_write_info ( sb , type ) ;
if ( ! err )
err = err2 ;
}
}
out_qlock :
ocfs2_qinfo_unlock ( info , freeing ) ;
out :
if ( err < 0 )
mlog_errno ( err ) ;
return err ;
}
2008-10-20 17:36:47 +04:00
/*
* Functions for periodic syncing of dquots with global file
*/
static int ocfs2_sync_dquot_helper ( struct dquot * dquot , unsigned long type )
{
handle_t * handle ;
struct super_block * sb = dquot - > dq_sb ;
struct ocfs2_mem_dqinfo * oinfo = sb_dqinfo ( sb , type ) - > dqi_priv ;
struct ocfs2_super * osb = OCFS2_SB ( sb ) ;
int status = 0 ;
mlog_entry ( " id=%u qtype=%u type=%lu device=%s \n " , dquot - > dq_id ,
dquot - > dq_type , type , sb - > s_id ) ;
if ( type ! = dquot - > dq_type )
goto out ;
status = ocfs2_lock_global_qf ( oinfo , 1 ) ;
if ( status < 0 )
goto out ;
handle = ocfs2_start_trans ( osb , OCFS2_QSYNC_CREDITS ) ;
if ( IS_ERR ( handle ) ) {
status = PTR_ERR ( handle ) ;
mlog_errno ( status ) ;
goto out_ilock ;
}
mutex_lock ( & sb_dqopt ( sb ) - > dqio_mutex ) ;
status = ocfs2_sync_dquot ( dquot ) ;
mutex_unlock ( & sb_dqopt ( sb ) - > dqio_mutex ) ;
if ( status < 0 )
mlog_errno ( status ) ;
/* We have to write local structure as well... */
dquot_mark_dquot_dirty ( dquot ) ;
status = dquot_commit ( dquot ) ;
if ( status < 0 )
mlog_errno ( status ) ;
ocfs2_commit_trans ( osb , handle ) ;
out_ilock :
ocfs2_unlock_global_qf ( oinfo , 1 ) ;
out :
mlog_exit ( status ) ;
return status ;
}
static void qsync_work_fn ( struct work_struct * work )
{
struct ocfs2_mem_dqinfo * oinfo = container_of ( work ,
struct ocfs2_mem_dqinfo ,
dqi_sync_work . work ) ;
struct super_block * sb = oinfo - > dqi_gqinode - > i_sb ;
dquot_scan_active ( sb , ocfs2_sync_dquot_helper , oinfo - > dqi_type ) ;
queue_delayed_work ( ocfs2_quota_wq , & oinfo - > dqi_sync_work ,
2009-07-22 15:17:20 +04:00
msecs_to_jiffies ( oinfo - > dqi_syncms ) ) ;
2008-10-20 17:36:47 +04:00
}
2008-08-25 21:56:50 +04:00
/*
* Wrappers for generic quota functions
*/
static int ocfs2_write_dquot ( struct dquot * dquot )
{
handle_t * handle ;
struct ocfs2_super * osb = OCFS2_SB ( dquot - > dq_sb ) ;
int status = 0 ;
mlog_entry ( " id=%u, type=%d " , dquot - > dq_id , dquot - > dq_type ) ;
handle = ocfs2_start_trans ( osb , OCFS2_QWRITE_CREDITS ) ;
if ( IS_ERR ( handle ) ) {
status = PTR_ERR ( handle ) ;
mlog_errno ( status ) ;
goto out ;
}
status = dquot_commit ( dquot ) ;
ocfs2_commit_trans ( osb , handle ) ;
out :
mlog_exit ( status ) ;
return status ;
}
2009-08-07 01:29:34 +04:00
static int ocfs2_calc_qdel_credits ( struct super_block * sb , int type )
2008-08-25 21:56:50 +04:00
{
2009-08-07 01:29:34 +04:00
struct ocfs2_mem_dqinfo * oinfo = sb_dqinfo ( sb , type ) - > dqi_priv ;
2009-07-22 15:17:21 +04:00
/*
* We modify tree , leaf block , global info , local chunk header ,
* global and local inode ; OCFS2_QINFO_WRITE_CREDITS already
* accounts for inode update
*/
2009-08-07 01:29:34 +04:00
return ( oinfo - > dqi_gi . dqi_qtree_depth + 2 ) *
OCFS2_QUOTA_BLOCK_UPDATE_CREDITS +
2009-07-22 15:17:21 +04:00
OCFS2_QINFO_WRITE_CREDITS +
OCFS2_INODE_UPDATE_CREDITS ;
2008-08-25 21:56:50 +04:00
}
static int ocfs2_release_dquot ( struct dquot * dquot )
{
handle_t * handle ;
struct ocfs2_mem_dqinfo * oinfo =
sb_dqinfo ( dquot - > dq_sb , dquot - > dq_type ) - > dqi_priv ;
struct ocfs2_super * osb = OCFS2_SB ( dquot - > dq_sb ) ;
int status = 0 ;
mlog_entry ( " id=%u, type=%d " , dquot - > dq_id , dquot - > dq_type ) ;
status = ocfs2_lock_global_qf ( oinfo , 1 ) ;
if ( status < 0 )
goto out ;
handle = ocfs2_start_trans ( osb ,
ocfs2_calc_qdel_credits ( dquot - > dq_sb , dquot - > dq_type ) ) ;
if ( IS_ERR ( handle ) ) {
status = PTR_ERR ( handle ) ;
mlog_errno ( status ) ;
goto out_ilock ;
}
status = dquot_release ( dquot ) ;
ocfs2_commit_trans ( osb , handle ) ;
out_ilock :
ocfs2_unlock_global_qf ( oinfo , 1 ) ;
out :
mlog_exit ( status ) ;
return status ;
}
static int ocfs2_acquire_dquot ( struct dquot * dquot )
{
struct ocfs2_mem_dqinfo * oinfo =
sb_dqinfo ( dquot - > dq_sb , dquot - > dq_type ) - > dqi_priv ;
int status = 0 ;
mlog_entry ( " id=%u, type=%d " , dquot - > dq_id , dquot - > dq_type ) ;
/* We need an exclusive lock, because we're going to update use count
* and instantiate possibly new dquot structure */
status = ocfs2_lock_global_qf ( oinfo , 1 ) ;
if ( status < 0 )
goto out ;
status = dquot_acquire ( dquot ) ;
ocfs2_unlock_global_qf ( oinfo , 1 ) ;
out :
mlog_exit ( status ) ;
return status ;
}
static int ocfs2_mark_dquot_dirty ( struct dquot * dquot )
{
unsigned long mask = ( 1 < < ( DQ_LASTSET_B + QIF_ILIMITS_B ) ) |
( 1 < < ( DQ_LASTSET_B + QIF_BLIMITS_B ) ) |
( 1 < < ( DQ_LASTSET_B + QIF_INODES_B ) ) |
( 1 < < ( DQ_LASTSET_B + QIF_SPACE_B ) ) |
( 1 < < ( DQ_LASTSET_B + QIF_BTIME_B ) ) |
( 1 < < ( DQ_LASTSET_B + QIF_ITIME_B ) ) ;
int sync = 0 ;
int status ;
struct super_block * sb = dquot - > dq_sb ;
int type = dquot - > dq_type ;
struct ocfs2_mem_dqinfo * oinfo = sb_dqinfo ( sb , type ) - > dqi_priv ;
handle_t * handle ;
struct ocfs2_super * osb = OCFS2_SB ( sb ) ;
mlog_entry ( " id=%u, type=%d " , dquot - > dq_id , type ) ;
dquot_mark_dquot_dirty ( dquot ) ;
/* In case user set some limits, sync dquot immediately to global
* quota file so that information propagates quicker */
spin_lock ( & dq_data_lock ) ;
if ( dquot - > dq_flags & mask )
sync = 1 ;
spin_unlock ( & dq_data_lock ) ;
2009-01-13 01:20:32 +03:00
/* This is a slight hack but we can't afford getting global quota
* lock if we already have a transaction started . */
if ( ! sync | | journal_current_handle ( ) ) {
2008-08-25 21:56:50 +04:00
status = ocfs2_write_dquot ( dquot ) ;
goto out ;
}
status = ocfs2_lock_global_qf ( oinfo , 1 ) ;
if ( status < 0 )
goto out ;
handle = ocfs2_start_trans ( osb , OCFS2_QSYNC_CREDITS ) ;
if ( IS_ERR ( handle ) ) {
status = PTR_ERR ( handle ) ;
mlog_errno ( status ) ;
goto out_ilock ;
}
status = ocfs2_sync_dquot ( dquot ) ;
if ( status < 0 ) {
mlog_errno ( status ) ;
goto out_trans ;
}
/* Now write updated local dquot structure */
status = dquot_commit ( dquot ) ;
out_trans :
ocfs2_commit_trans ( osb , handle ) ;
out_ilock :
ocfs2_unlock_global_qf ( oinfo , 1 ) ;
out :
mlog_exit ( status ) ;
return status ;
}
/* This should happen only after set_dqinfo(). */
static int ocfs2_write_info ( struct super_block * sb , int type )
{
handle_t * handle ;
int status = 0 ;
struct ocfs2_mem_dqinfo * oinfo = sb_dqinfo ( sb , type ) - > dqi_priv ;
mlog_entry_void ( ) ;
status = ocfs2_lock_global_qf ( oinfo , 1 ) ;
if ( status < 0 )
goto out ;
handle = ocfs2_start_trans ( OCFS2_SB ( sb ) , OCFS2_QINFO_WRITE_CREDITS ) ;
if ( IS_ERR ( handle ) ) {
status = PTR_ERR ( handle ) ;
mlog_errno ( status ) ;
goto out_ilock ;
}
status = dquot_commit_info ( sb , type ) ;
ocfs2_commit_trans ( OCFS2_SB ( sb ) , handle ) ;
out_ilock :
ocfs2_unlock_global_qf ( oinfo , 1 ) ;
out :
mlog_exit ( status ) ;
return status ;
}
static struct dquot * ocfs2_alloc_dquot ( struct super_block * sb , int type )
{
struct ocfs2_dquot * dquot =
kmem_cache_zalloc ( ocfs2_dquot_cachep , GFP_NOFS ) ;
if ( ! dquot )
return NULL ;
return & dquot - > dq_dquot ;
}
static void ocfs2_destroy_dquot ( struct dquot * dquot )
{
kmem_cache_free ( ocfs2_dquot_cachep , dquot ) ;
}
struct dquot_operations ocfs2_quota_operations = {
2009-01-12 19:44:34 +03:00
. initialize = dquot_initialize ,
. drop = dquot_drop ,
2008-08-25 21:56:50 +04:00
. alloc_space = dquot_alloc_space ,
. alloc_inode = dquot_alloc_inode ,
. free_space = dquot_free_space ,
. free_inode = dquot_free_inode ,
. transfer = dquot_transfer ,
. write_dquot = ocfs2_write_dquot ,
. acquire_dquot = ocfs2_acquire_dquot ,
. release_dquot = ocfs2_release_dquot ,
. mark_dirty = ocfs2_mark_dquot_dirty ,
. write_info = ocfs2_write_info ,
. alloc_dquot = ocfs2_alloc_dquot ,
. destroy_dquot = ocfs2_destroy_dquot ,
} ;
2008-10-20 17:36:47 +04:00
int ocfs2_quota_setup ( void )
{
ocfs2_quota_wq = create_workqueue ( " o2quot " ) ;
if ( ! ocfs2_quota_wq )
return - ENOMEM ;
return 0 ;
}
void ocfs2_quota_shutdown ( void )
{
if ( ocfs2_quota_wq ) {
flush_workqueue ( ocfs2_quota_wq ) ;
destroy_workqueue ( ocfs2_quota_wq ) ;
ocfs2_quota_wq = NULL ;
}
}