Re: [RFC] quota: 64-bit limits with vfs, updated

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mar 15, 2008  21:58 +0300, Andrew Perepechko wrote:
> +static inline uint v2_dqblksz(uint rev)
> +{
> +	uint sz = 0; /* make the compiler happy */
> +
> +	switch (rev) {
> +	case 0:
> +		sz = sizeof(struct v2_disk_dqblk_r0);
> +		break;
> +	case 1:
> +		sz = sizeof(struct v2_disk_dqblk_r1);
> +		break;
> +	default:
> +		BUG();
> +	}
> +
> +	return sz;
> +}

Is this ever used on values that are read from the disk?  If yes, then
having it BUG() because there is corruption on the disk is bad.

> +static int v2_quota_file_revision(struct super_block *sb, int type)
>  {
>  	size = sb->s_op->quota_read(sb, type, (char *)&dqhead, sizeof(struct v2_disk_dqheader), 0);
>  	if (size != sizeof(struct v2_disk_dqheader)) {
>  		printk("quota_v2: failed read expected=%zd got=%zd\n",
>  			sizeof(struct v2_disk_dqheader), size);
> -		return 0;
> +		return -1;
>  	}
> -	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
> -	    le32_to_cpu(dqhead.dqh_version) != quota_versions[type])
> -		return 0;
> -	return 1;
> +	if (le32_to_cpu(dqhead.dqh_magic) == quota_magics[type]) {
> +		if (le32_to_cpu(dqhead.dqh_version) == quota_versions_r0[type])
> +			return 0;
> +		if (le32_to_cpu(dqhead.dqh_version) == quota_versions_r1[type])
> +			return 1;
> +	}
> +	return -1;
> +}
> @@ -51,6 +92,10 @@ static int v2_read_file_info(struct supe
> +
> +	rev = v2_quota_file_revision(sb, type);
> +	BUG_ON(rev < 0);
 
This is no good - if there is an error from ->quota_read() then the kernel
will BUG() here.  You need proper (non fatal) error handling when you are
first loading the quota information into memory.

> +/* Check whether given file is really vfsv0 quotafile */
> +static int v2_check_quota_file(struct super_block *sb, int type)
> +{
> +	return v2_quota_file_revision(sb, type) != -1;
>  }

This could be inline.

> +#define DQ2MQ(v) (sizeof(v) == sizeof(__u64) ? \
> +		  (qsize_t)le64_to_cpu(v) : \
> +		  (qsize_t)le32_to_cpu(v))
> +
> +#define MQ2DQ(v, newv) (sizeof(v) == sizeof(__u64) ? \
> +			(v = cpu_to_le64((__u64)newv)) : \
> +			(v = cpu_to_le32((__u32)newv)))
> +
> +#define DQF_GET(var, rev, field) (rev == 0 ? \
> +		DQ2MQ((var)->disk_dqblk_r0.field) : \
> +		DQ2MQ((var)->disk_dqblk_r1.field))
> +
> +#define DQF_PUT(var, rev, field, val) (rev == 0 ? \
> +		MQ2DQ((var)->disk_dqblk_r0.field, val) : \
> +		MQ2DQ((var)->disk_dqblk_r1.field, val))
> +
> +void disk2memdqb(struct mem_dqblk *m, union v2_disk_dqblk *d,
> +		 uint rev)
> +{
> +	m->dqb_ihardlimit = DQF_GET(d, rev, dqb_ihardlimit);
> +	m->dqb_isoftlimit = DQF_GET(d, rev, dqb_isoftlimit);
> +	m->dqb_curinodes = DQF_GET(d, rev, dqb_curinodes);
> +	m->dqb_itime = DQF_GET(d, rev, dqb_itime);
> +	m->dqb_bhardlimit = DQF_GET(d, rev, dqb_bhardlimit);
> +	m->dqb_bsoftlimit = DQF_GET(d, rev, dqb_bsoftlimit);
> +	m->dqb_curspace = DQF_GET(d, rev, dqb_curspace);
> +	m->dqb_btime = DQF_GET(d, rev, dqb_btime);
> +}

While this produces nice looking code, it isn't very efficient to run.
It has 8 checks for "rev" per call, and it would have an additional
8 checks for "sizeof" except those are constant and optimized away
by the compiler.  If you can convince me that the compiler will also
optimize the 8 checks for "rev" into a single branch then it can
stay, otherwise you should split it into 2 large sections based on
"rev" and then decode r0 and r1 structures separately.

> @@ -301,17 +371,18 @@ static uint find_free_dqentry(struct dqu
> +	/* Block will be full? */
> +	if (le16_to_cpu(dh->dqdh_entries)+1 >= dqstrinblk)
>  		if ((*err = remove_free_dqentry(sb, dquot->dq_type, buf, blk)) < 0) {
>  			printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk);
>  			goto out_buf;
>  		}

Can you please clean this up a bit while here:

	/* Block will be full? */
	if (le16_to_cpu(dh->dqdh_entries) + 1 >= dqstrinblk &&
	    (*err = remove_free_dqentry(sb, dquot->dq_type, buf, blk)) < 0) {
		printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove "
				"block (%u) from entry free list.\n", blk);
		goto out_buf;
	}

> +	for (i = 0; i < dqstrinblk && memcmp(&emptydquot,
> +			ddquot+i, dqblksz); i++);

This is also quite easy to be confused, please write it as:

	for (i = 0; i < dqstrinblk && memcmp(&emptydquot,ddquot+i,dqblksz); i++)
		/* empty loop to count used dquot structs in block */;

In fact, it would be even better to just increment "ddquot" in the loop to
avoid having to re-do the offset each time:

	for (i = 0; i < dqstrinblk && memcmp(&emptydquot, ddquot, dqblksz);
	     i++, ddquot++)
		/* empty loop to count used dquot structs in block */;

> -	dquot->dq_off = (blk<<V2_DQBLKSIZE_BITS)+sizeof(struct v2_disk_dqdbheader)+i*sizeof(struct v2_disk_dqblk);
> +	dquot->dq_off = (blk<<V2_DQBLKSIZE_BITS)+
> +			sizeof(struct v2_disk_dqdbheader)+i*dqblksz;

With the above change to ddquot you can then use it directly here:

	dquot->dq_off = (blk << V2_DQBLKSIZE_BITS) +
			((char *)ddquot - (char *)buf);

> @@ -395,7 +467,9 @@ static int v2_write_dquot(struct dquot *
>  {
>  	int type = dquot->dq_type;

type is only used twice in this function, please remove it from the stack.

> @@ -538,27 +613,29 @@ static loff_t find_block_dqentry(struct
> +	union v2_disk_dqblk *ddquot = GETENTRIES(buf);
> +	int type = dquot->dq_type;

type only used twice in this function, please remove from stack.

> +	uint rev = sb_dqopt(dquot->dq_sb)->info[type].u.v2_i.dqi_revision;
> +	uint dqblksz = v2_dqblksz(rev), dqstrinblk = v2_dqstrinblk(rev);
>  
>  	if (dquot->dq_id)
> -		for (i = 0; i < V2_DQSTRINBLK &&
> -		     le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++);
> +		for (i = 0; i < dqstrinblk &&
> +		     DQF_GET(ddquot+i, rev, dqb_id) != dquot->dq_id; i++);
>  	else {	/* ID 0 as a bit more complicated searching... */

Spaces around " + ", and please add a { } around this block because second
part has { } already.

> +		for (i = 0; i < dqstrinblk; i++)
> +			if (!DQF_GET(ddquot+i, rev, dqb_id) &&
> +			    memcmp(&emptydquot, ddquot+i, dqblksz))
>  				break;
>  	}

Spaces around " + " here also.  Actually, please run the patch through
"checkpatch.pl" to find any similar issues.

> @@ -608,7 +685,7 @@ static int v2_read_dquot(struct dquot *d
>  {
>  	int type = dquot->dq_type;
>  	loff_t offset;
> -	struct v2_disk_dqblk ddquot, empty;
> +	union v2_disk_dqblk ddquot;

Please remove type from stack.

> @@ -629,25 +706,25 @@ static int v2_read_dquot(struct dquot *d
> +		uint rev = sb_dqopt(dquot->dq_sb)->info[type].u.v2_i.
> +			   dqi_revision, dqblksz = v2_dqblksz(rev);

Use another line with "uint dqblksz = v2_dqblksz(rev);" here.

>  struct mem_dqblk {
> -	__u32 dqb_bhardlimit;	/* absolute limit on disk blks alloc */
> -	__u32 dqb_bsoftlimit;	/* preferred limit on disk blks */
> +	qsize_t dqb_bhardlimit;	/* absolute limit on disk blks alloc */
> +	qsize_t dqb_bsoftlimit;	/* preferred limit on disk blks */
>  	qsize_t dqb_curspace;	/* current used space */
> -	__u32 dqb_ihardlimit;	/* absolute limit on allocated inodes */
> -	__u32 dqb_isoftlimit;	/* preferred inode limit */
> -	__u32 dqb_curinodes;	/* current # allocated inodes */
> +	qsize_t dqb_ihardlimit;	/* absolute limit on allocated inodes */
> +	qsize_t dqb_isoftlimit;	/* preferred inode limit */
> +	qsize_t dqb_curinodes;	/* current # allocated inodes */
>  	time_t dqb_btime;	/* time limit for excessive disk use */
>  	time_t dqb_itime;	/* time limit for excessive inode use */
>  };

Is there a reason to use "qsize_t" instead of just using __u64 directly?

> +++ linux-2.6.24.3/include/linux/quotaio_v2.h	2008-03-13 22:01:48.000000000 +0300
> @@ -16,28 +16,51 @@
> @@ -59,7 +82,7 @@ struct v2_disk_dqinfo {
>  
>  /*
>   *  Structure of header of block with quota structures. It is padded to 16 bytes so
> - *  there will be space for exactly 21 quota-entries in a block
> + *  there will be space for exactly 21 (r0) or 14 (r1) quota-entries in a block
>   */

Hmm, when this comment says "block" does it mean "1024-byte block" or
filesystem block?  For 1024-byte block it is true that both 48-byte (r0) and
72-byte (r1) records fit evenly, but for 4096-byte blocks 72-byte records
do not fit evenly.  Is that a problem?

Please line wrap or adjust comment to fit in 80 colums.

Cheers, Andreas
--
Andreas Dilger
Sr. Staff Engineer, Lustre Group
Sun Microsystems of Canada, Inc.

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux