From: Bob Peterson <rpeterso@redhat.com>
To: cluster-devel.redhat.com
Subject: [Cluster-devel] [GFS2 PATCH 1/2] gfs2: fix quota updates on block boundaries
Date: Mon, 1 Jun 2015 11:25:45 -0400 (EDT) [thread overview]
Message-ID: <1262749450.8306869.1433172345478.JavaMail.zimbra@redhat.com> (raw)
In-Reply-To: <1433131627-61141-2-git-send-email-adas@redhat.com>
Hi,
Some comments embedded.
----- Original Message -----
> For smaller block sizes (512B, 1K, 2K), some quotas straddle block
> boundaries such that the usage value is on one block and the rest
> of the quota is on the previous block. In such cases, the value
> does not get updated correctly. This patch fixes that by addressing
> the boundary conditions correctly.
>
> This patch also adds a (s64) cast that was missing in a call to
> gfs2_quota_change() in inode.c
>
> Resolves: rhbz#1174295
> Signed-off-by: Abhi Das <adas@redhat.com>
> ---
> fs/gfs2/inode.c | 2 +-
> fs/gfs2/quota.c | 197
> +++++++++++++++++++++++++++++++++-----------------------
> 2 files changed, 119 insertions(+), 80 deletions(-)
>
> diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
> index 4d809eb..a088e54 100644
> --- a/fs/gfs2/inode.c
> +++ b/fs/gfs2/inode.c
> @@ -1889,7 +1889,7 @@ static int setattr_chown(struct inode *inode, struct
> iattr *attr)
>
> if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||
> !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) {
> - gfs2_quota_change(ip, -ap.target, ouid, ogid);
> + gfs2_quota_change(ip, -(s64)ap.target, ouid, ogid);
> gfs2_quota_change(ip, ap.target, nuid, ngid);
> }
>
> diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
> index 5c27e48..01f4d40 100644
> --- a/fs/gfs2/quota.c
> +++ b/fs/gfs2/quota.c
> @@ -652,6 +652,112 @@ static void do_qc(struct gfs2_quota_data *qd, s64
> change)
> mutex_unlock(&sdp->sd_quota_mutex);
> }
>
> +static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long
> index,
> + unsigned off, void *buf, unsigned bytes)
> +{
> + struct inode *inode = &ip->i_inode;
> + struct gfs2_sbd *sdp = GFS2_SB(inode);
> + struct address_space *mapping = inode->i_mapping;
> + struct page *page;
> + struct buffer_head *bh;
> + void *kaddr;
> + unsigned long fs_blk;
If this is truly a file system block, it should probably be u64, not
unsigned long.
> + unsigned bsize = sdp->sd_sb.sb_bsize, bnum = 0, boff = 0;
> + unsigned to_write = bytes, pg_off = off;
> + int done = 0;
> +
> + fs_blk = index << (PAGE_CACHE_SHIFT - sdp->sd_sb.sb_bsize_shift);
> + boff = off % bsize;
> +
> + page = find_or_create_page(mapping, index, GFP_NOFS);
> + if (!page)
> + return -ENOMEM;
> + if (!page_has_buffers(page))
> + create_empty_buffers(page, bsize, 0);
> +
> + bh = page_buffers(page);
> + while (!done) {
> + /* Find the beginning block within the page */
> + if (pg_off >= ((bnum * bsize) + bsize)) {
> + bh = bh->b_this_page;
> + bnum++;
> + fs_blk++;
> + continue;
> + }
> + if (!buffer_mapped(bh)) {
> + gfs2_block_map(inode, fs_blk, bh, 1);
> + if (!buffer_mapped(bh))
> + goto unlock_out;
> + /* If it's a newly allocated disk block, zero it */
> + if (buffer_new(bh))
> + zero_user(page, bnum * bsize, bh->b_size);
> + }
> + if (PageUptodate(page))
> + set_buffer_uptodate(bh);
> + if (!buffer_uptodate(bh)) {
> + ll_rw_block(READ | REQ_META, 1, &bh);
I know this was just copied from the section below, but I don't think this read
should have REQ_META set because it's not metadata, right? At any rate, it's
treated as data in the trans_add_data below.
> + wait_on_buffer(bh);
> + if (!buffer_uptodate(bh))
> + goto unlock_out;
> + }
> + gfs2_trans_add_data(ip->i_gl, bh);
> +
> + /* If we need to write to the next block as well */
> + if (to_write > (bsize - boff)) {
> + pg_off += (bsize - boff);
> + to_write -= (bsize - boff);
> + boff = pg_off % bsize;
> + continue;
> + }
> + done = 1;
> + }
> +
> + /* Write to the page, now that we have setup the buffer(s) */
> + kaddr = kmap_atomic(page);
> + memcpy(kaddr + off, buf, bytes);
> + flush_dcache_page(page);
> + kunmap_atomic(kaddr);
> + unlock_page(page);
> + page_cache_release(page);
> +
> + return 0;
> +
> +unlock_out:
> + unlock_page(page);
> + page_cache_release(page);
> + return -EIO;
> +}
> +
> +static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota
> *qp,
> + loff_t loc)
> +{
> + unsigned long pg_beg;
> + unsigned pg_off, nbytes, overflow = 0;
> + int pg_oflow = 0, error;
> + void *ptr;
> +
> + nbytes = sizeof(struct gfs2_quota);
> +
> + pg_beg = loc >> PAGE_CACHE_SHIFT;
> + pg_off = loc % PAGE_CACHE_SIZE;
> +
> + /* If the quota straddles a page boundary, split the write in two */
> + if ((pg_off + nbytes) > PAGE_CACHE_SIZE) {
> + pg_oflow = 1;
> + overflow = (pg_off + nbytes) - PAGE_CACHE_SIZE;
> + }
> +
> + ptr = qp;
> + error = gfs2_write_buf_to_page(ip, pg_beg, pg_off, ptr,
> + nbytes - overflow);
> + /* If there's an overflow, write the remaining bytes to the next page */
> + if (!error && pg_oflow)
> + error = gfs2_write_buf_to_page(ip, pg_beg + 1, 0,
> + ptr + nbytes - overflow,
> + overflow);
> + return error;
> +}
> +
> /**
> * gfs2_adjust_quota - adjust record of current block usage
> * @ip: The quota inode
> @@ -672,15 +778,8 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip,
> loff_t loc,
> {
> struct inode *inode = &ip->i_inode;
> struct gfs2_sbd *sdp = GFS2_SB(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 page *page;
> - void *kaddr, *ptr;
> struct gfs2_quota q;
> - int err, nbytes;
> + int err;
> u64 size;
>
> if (gfs2_is_stuffed(ip)) {
> @@ -694,8 +793,11 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip,
> loff_t loc,
> if (err < 0)
> return err;
>
> + loc -= sizeof(q); /* gfs2_internal_read would've advanced the loc ptr */
> err = -EIO;
> be64_add_cpu(&q.qu_value, change);
> + if (be64_to_cpu(q.qu_value) < 0)
> + q.qu_value = 0; /* Never go negative on quota usage */
> qd->qd_qb.qb_value = q.qu_value;
> if (fdq) {
> if (fdq->d_fieldmask & QC_SPC_SOFT) {
> @@ -712,79 +814,16 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip,
> loff_t loc,
> }
> }
>
> - /* Write the quota into the quota file on disk */
> - ptr = &q;
> - nbytes = sizeof(struct gfs2_quota);
> -get_a_page:
> - page = find_or_create_page(mapping, index, GFP_NOFS);
> - if (!page)
> - return -ENOMEM;
> -
> - blocksize = inode->i_sb->s_blocksize;
> - iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
> -
> - if (!page_has_buffers(page))
> - create_empty_buffers(page, blocksize, 0);
> -
> - bh = page_buffers(page);
> - pos = blocksize;
> - while (offset >= pos) {
> - bh = bh->b_this_page;
> - iblock++;
> - pos += blocksize;
> - }
> -
> - if (!buffer_mapped(bh)) {
> - gfs2_block_map(inode, iblock, bh, 1);
> - if (!buffer_mapped(bh))
> - goto unlock_out;
> - /* If it's a newly allocated disk block for quota, zero it */
> - if (buffer_new(bh))
> - zero_user(page, pos - blocksize, bh->b_size);
> - }
> -
> - if (PageUptodate(page))
> - set_buffer_uptodate(bh);
> -
> - if (!buffer_uptodate(bh)) {
> - ll_rw_block(READ | REQ_META, 1, &bh);
> - wait_on_buffer(bh);
> - if (!buffer_uptodate(bh))
> - goto unlock_out;
> - }
> -
> - gfs2_trans_add_data(ip->i_gl, bh);
> -
> - kaddr = kmap_atomic(page);
> - if (offset + sizeof(struct gfs2_quota) > PAGE_CACHE_SIZE)
> - nbytes = PAGE_CACHE_SIZE - offset;
> - memcpy(kaddr + offset, ptr, nbytes);
> - flush_dcache_page(page);
> - kunmap_atomic(kaddr);
> - unlock_page(page);
> - page_cache_release(page);
> -
> - /* If quota straddles page boundary, we need to update the rest of the
> - * quota at the beginning of the next page */
> - if ((offset + sizeof(struct gfs2_quota)) > PAGE_CACHE_SIZE) {
> - ptr = ptr + nbytes;
> - nbytes = sizeof(struct gfs2_quota) - nbytes;
> - offset = 0;
> - index++;
> - goto get_a_page;
> + err = gfs2_write_disk_quota(ip, &q, loc);
> + if (!err) {
> + size = loc + sizeof(struct gfs2_quota);
> + if (size > inode->i_size)
> + i_size_write(inode, size);
> + inode->i_mtime = inode->i_atime = CURRENT_TIME;
> + mark_inode_dirty(inode);
> + set_bit(QDF_REFRESH, &qd->qd_flags);
> }
>
> - size = loc + sizeof(struct gfs2_quota);
> - if (size > inode->i_size)
> - i_size_write(inode, size);
> - inode->i_mtime = inode->i_atime = CURRENT_TIME;
> - mark_inode_dirty(inode);
> - set_bit(QDF_REFRESH, &qd->qd_flags);
> - return 0;
> -
> -unlock_out:
> - unlock_page(page);
> - page_cache_release(page);
> return err;
> }
>
> --
> 1.8.1.4
>
>
next prev parent reply other threads:[~2015-06-01 15:25 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-06-01 4:07 [Cluster-devel] [GFS2 PATCH 0/2] More quota fixes Abhi Das
2015-06-01 4:07 ` [Cluster-devel] [GFS2 PATCH 1/2] gfs2: fix quota updates on block boundaries Abhi Das
2015-06-01 15:25 ` Bob Peterson [this message]
2015-06-01 16:40 ` Abhijith Das
2015-06-01 4:07 ` [Cluster-devel] [GFS2 PATCH 2/2] gfs2: limit quota log messages Abhi Das
2015-06-01 16:27 ` Bob Peterson
2015-06-01 16:51 ` Abhijith Das
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1262749450.8306869.1433172345478.JavaMail.zimbra@redhat.com \
--to=rpeterso@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).