Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 169196
b: refs/heads/master
c: e285c10
h: refs/heads/master
v: v3
  • Loading branch information
Steven Whitehouse committed Dec 3, 2009
1 parent d581de7 commit e527324
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 27 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 113d6b3c99bf30d8083068d00e3c7304d91d4845
refs/heads/master: e285c100362762f7440643be637dd332460fdc75
198 changes: 172 additions & 26 deletions trunk/fs/gfs2/quota.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,8 +615,9 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change)
* gfs2_adjust_quota - adjust record of current block usage
* @ip: The quota inode
* @loc: Offset of the entry in the quota file
* @change: The amount of change to record
* @change: The amount of usage change to record
* @qd: The quota data
* @fdq: The updated limits to record
*
* This function was mostly borrowed from gfs2_block_truncate_page which was
* in turn mostly borrowed from ext3
Expand All @@ -625,19 +626,21 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change)
*/

static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
s64 change, struct gfs2_quota_data *qd)
s64 change, struct gfs2_quota_data *qd,
struct fs_disk_quota *fdq)
{
struct inode *inode = &ip->i_inode;
struct address_space *mapping = inode->i_mapping;
unsigned long index = loc >> PAGE_CACHE_SHIFT;
unsigned offset = loc & (PAGE_CACHE_SIZE - 1);
unsigned blocksize, iblock, pos;
struct buffer_head *bh;
struct buffer_head *bh, *dibh;
struct page *page;
void *kaddr;
struct gfs2_quota *qp;
s64 value;
int err = -EIO;
u64 size;

if (gfs2_is_stuffed(ip))
gfs2_unstuff_dinode(ip, NULL);
Expand Down Expand Up @@ -683,9 +686,34 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc,
value = (s64)be64_to_cpu(qp->qu_value) + change;
qp->qu_value = cpu_to_be64(value);
qd->qd_qb.qb_value = qp->qu_value;
if (fdq) {
if (fdq->d_fieldmask & FS_DQ_BSOFT) {
qp->qu_warn = cpu_to_be64(fdq->d_blk_softlimit);
qd->qd_qb.qb_warn = qp->qu_warn;
}
if (fdq->d_fieldmask & FS_DQ_BHARD) {
qp->qu_limit = cpu_to_be64(fdq->d_blk_hardlimit);
qd->qd_qb.qb_limit = qp->qu_limit;
}
}
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
err = 0;

err = gfs2_meta_inode_buffer(ip, &dibh);
if (err)
goto unlock;

size = loc + sizeof(struct gfs2_quota);
if (size > inode->i_size) {
ip->i_disksize = size;
i_size_write(inode, size);
}
inode->i_mtime = inode->i_atime = CURRENT_TIME;
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_dinode_out(ip, dibh->b_data);
brelse(dibh);
mark_inode_dirty(inode);

unlock:
unlock_page(page);
page_cache_release(page);
Expand Down Expand Up @@ -713,6 +741,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
return -ENOMEM;

sort(qda, num_qd, sizeof(struct gfs2_quota_data *), sort_qd, NULL);
mutex_lock_nested(&ip->i_inode.i_mutex, I_MUTEX_QUOTA);
for (qx = 0; qx < num_qd; qx++) {
error = gfs2_glock_nq_init(qda[qx]->qd_gl, LM_ST_EXCLUSIVE,
GL_NOCACHE, &ghs[qx]);
Expand Down Expand Up @@ -768,8 +797,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
for (x = 0; x < num_qd; x++) {
qd = qda[x];
offset = qd2offset(qd);
error = gfs2_adjust_quota(ip, offset, qd->qd_change_sync,
(struct gfs2_quota_data *)qd);
error = gfs2_adjust_quota(ip, offset, qd->qd_change_sync, qd, NULL);
if (error)
goto out_end_trans;

Expand All @@ -789,20 +817,44 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
out:
while (qx--)
gfs2_glock_dq_uninit(&ghs[qx]);
mutex_unlock(&ip->i_inode.i_mutex);
kfree(ghs);
gfs2_log_flush(ip->i_gl->gl_sbd, ip->i_gl);
return error;
}

static int update_qd(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd)
{
struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
struct gfs2_quota q;
struct gfs2_quota_lvb *qlvb;
loff_t pos;
int error;

memset(&q, 0, sizeof(struct gfs2_quota));
pos = qd2offset(qd);
error = gfs2_internal_read(ip, NULL, (char *)&q, &pos, sizeof(q));
if (error < 0)
return error;

qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
qlvb->qb_magic = cpu_to_be32(GFS2_MAGIC);
qlvb->__pad = 0;
qlvb->qb_limit = q.qu_limit;
qlvb->qb_warn = q.qu_warn;
qlvb->qb_value = q.qu_value;
qd->qd_qb = *qlvb;

return 0;
}

static int do_glock(struct gfs2_quota_data *qd, int force_refresh,
struct gfs2_holder *q_gh)
{
struct gfs2_sbd *sdp = qd->qd_gl->gl_sbd;
struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
struct gfs2_holder i_gh;
struct gfs2_quota q;
int error;
struct gfs2_quota_lvb *qlvb;

restart:
error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_SHARED, 0, q_gh);
Expand All @@ -812,7 +864,6 @@ static int do_glock(struct gfs2_quota_data *qd, int force_refresh,
qd->qd_qb = *(struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;

if (force_refresh || qd->qd_qb.qb_magic != cpu_to_be32(GFS2_MAGIC)) {
loff_t pos;
gfs2_glock_dq_uninit(q_gh);
error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_EXCLUSIVE,
GL_NOCACHE, q_gh);
Expand All @@ -823,25 +874,11 @@ static int do_glock(struct gfs2_quota_data *qd, int force_refresh,
if (error)
goto fail;

memset(&q, 0, sizeof(struct gfs2_quota));
pos = qd2offset(qd);
error = gfs2_internal_read(ip, NULL, (char *)&q, &pos, sizeof(q));
if (error < 0)
goto fail_gunlock;
if ((error < sizeof(q)) && force_refresh) {
error = -ENOENT;
error = update_qd(sdp, qd);
if (error)
goto fail_gunlock;
}
gfs2_glock_dq_uninit(&i_gh);

qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
qlvb->qb_magic = cpu_to_be32(GFS2_MAGIC);
qlvb->__pad = 0;
qlvb->qb_limit = q.qu_limit;
qlvb->qb_warn = q.qu_warn;
qlvb->qb_value = q.qu_value;
qd->qd_qb = *qlvb;

gfs2_glock_dq_uninit(&i_gh);
gfs2_glock_dq_uninit(q_gh);
force_refresh = 0;
goto restart;
Expand Down Expand Up @@ -1409,9 +1446,118 @@ static int gfs2_xquota_get(struct super_block *sb, int type, qid_t id,
return error;
}

/* GFS2 only supports a subset of the XFS fields */
#define GFS2_FIELDMASK (FS_DQ_BSOFT|FS_DQ_BHARD)

static int gfs2_xquota_set(struct super_block *sb, int type, qid_t id,
struct fs_disk_quota *fdq)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
struct gfs2_quota_data *qd;
struct gfs2_holder q_gh, i_gh;
unsigned int data_blocks, ind_blocks;
unsigned int blocks = 0;
int alloc_required;
struct gfs2_alloc *al;
loff_t offset;
int error;

if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
return -ESRCH; /* Crazy XFS error code */

switch(type) {
case USRQUOTA:
type = QUOTA_USER;
if (fdq->d_flags != XFS_USER_QUOTA)
return -EINVAL;
break;
case GRPQUOTA:
type = QUOTA_GROUP;
if (fdq->d_flags != XFS_GROUP_QUOTA)
return -EINVAL;
break;
default:
return -EINVAL;
}

if (fdq->d_fieldmask & ~GFS2_FIELDMASK)
return -EINVAL;
if (fdq->d_id != id)
return -EINVAL;

error = qd_get(sdp, type, id, &qd);
if (error)
return error;

mutex_lock(&ip->i_inode.i_mutex);
error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_EXCLUSIVE, 0, &q_gh);
if (error)
goto out_put;
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
if (error)
goto out_q;

/* Check for existing entry, if none then alloc new blocks */
error = update_qd(sdp, qd);
if (error)
goto out_i;

/* If nothing has changed, this is a no-op */
if ((fdq->d_fieldmask & FS_DQ_BSOFT) &&
(fdq->d_blk_softlimit == be64_to_cpu(qd->qd_qb.qb_warn)))
fdq->d_fieldmask ^= FS_DQ_BSOFT;
if ((fdq->d_fieldmask & FS_DQ_BHARD) &&
(fdq->d_blk_hardlimit == be64_to_cpu(qd->qd_qb.qb_limit)))
fdq->d_fieldmask ^= FS_DQ_BHARD;
if (fdq->d_fieldmask == 0)
goto out_i;

offset = qd2offset(qd);
error = gfs2_write_alloc_required(ip, offset, sizeof(struct gfs2_quota),
&alloc_required);
if (error)
goto out_i;
if (alloc_required) {
al = gfs2_alloc_get(ip);
if (al == NULL)
goto out_i;
gfs2_write_calc_reserv(ip, sizeof(struct gfs2_quota),
&data_blocks, &ind_blocks);
blocks = al->al_requested = 1 + data_blocks + ind_blocks;
error = gfs2_inplace_reserve(ip);
if (error)
goto out_alloc;
}

error = gfs2_trans_begin(sdp, blocks + RES_DINODE + 1, 0);
if (error)
goto out_release;

/* Apply changes */
error = gfs2_adjust_quota(ip, offset, 0, qd, fdq);

gfs2_trans_end(sdp);
out_release:
if (alloc_required) {
gfs2_inplace_release(ip);
out_alloc:
gfs2_alloc_put(ip);
}
out_i:
gfs2_glock_dq_uninit(&i_gh);
out_q:
gfs2_glock_dq_uninit(&q_gh);
out_put:
mutex_unlock(&ip->i_inode.i_mutex);
qd_put(qd);
return error;
}

const struct quotactl_ops gfs2_quotactl_ops = {
.quota_sync = gfs2_quota_sync,
.get_xstate = gfs2_quota_get_xstate,
.get_xquota = gfs2_xquota_get,
.set_xquota = gfs2_xquota_set,
};

0 comments on commit e527324

Please sign in to comment.