mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	ceph: quota: support for ceph.quota.max_files
This patch adds support for the max_files quota. It hooks into all the ceph functions that add new filesystem objects that need to be checked against the quota limits. When these limits are hit, -EDQUOT is returned. Note that we're not checking quotas on ceph_link(). ceph_link doesn't really create a new inode, and since the MDS doesn't update the directory statistics when a new (hard) link is created (only with symlinks), they are not accounted as a new file. Signed-off-by: Luis Henriques <lhenriques@suse.com> Reviewed-by: "Yan, Zheng" <zyan@redhat.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
		
							parent
							
								
									fb18a57568
								
							
						
					
					
						commit
						b7a2921765
					
				
					 4 changed files with 95 additions and 1 deletions
				
			
		| 
						 | 
					@ -828,6 +828,9 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
	if (ceph_snap(dir) != CEPH_NOSNAP)
 | 
						if (ceph_snap(dir) != CEPH_NOSNAP)
 | 
				
			||||||
		return -EROFS;
 | 
							return -EROFS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ceph_quota_is_max_files_exceeded(dir))
 | 
				
			||||||
 | 
							return -EDQUOT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = ceph_pre_init_acls(dir, &mode, &acls);
 | 
						err = ceph_pre_init_acls(dir, &mode, &acls);
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
| 
						 | 
					@ -881,6 +884,9 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
	if (ceph_snap(dir) != CEPH_NOSNAP)
 | 
						if (ceph_snap(dir) != CEPH_NOSNAP)
 | 
				
			||||||
		return -EROFS;
 | 
							return -EROFS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ceph_quota_is_max_files_exceeded(dir))
 | 
				
			||||||
 | 
							return -EDQUOT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest);
 | 
						dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest);
 | 
				
			||||||
	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS);
 | 
						req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS);
 | 
				
			||||||
	if (IS_ERR(req)) {
 | 
						if (IS_ERR(req)) {
 | 
				
			||||||
| 
						 | 
					@ -930,6 +936,11 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ceph_quota_is_max_files_exceeded(dir)) {
 | 
				
			||||||
 | 
							err = -EDQUOT;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mode |= S_IFDIR;
 | 
						mode |= S_IFDIR;
 | 
				
			||||||
	err = ceph_pre_init_acls(dir, &mode, &acls);
 | 
						err = ceph_pre_init_acls(dir, &mode, &acls);
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -413,6 +413,8 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
		return -ENAMETOOLONG;
 | 
							return -ENAMETOOLONG;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & O_CREAT) {
 | 
						if (flags & O_CREAT) {
 | 
				
			||||||
 | 
							if (ceph_quota_is_max_files_exceeded(dir))
 | 
				
			||||||
 | 
								return -EDQUOT;
 | 
				
			||||||
		err = ceph_pre_init_acls(dir, &mode, &acls);
 | 
							err = ceph_pre_init_acls(dir, &mode, &acls);
 | 
				
			||||||
		if (err < 0)
 | 
							if (err < 0)
 | 
				
			||||||
			return err;
 | 
								return err;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,3 +63,83 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	iput(inode);
 | 
						iput(inode);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum quota_check_op {
 | 
				
			||||||
 | 
						QUOTA_CHECK_MAX_FILES_OP	/* check quota max_files limit */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each
 | 
				
			||||||
 | 
					 * realm, it will execute quota check operation defined by the 'op' parameter.
 | 
				
			||||||
 | 
					 * The snaprealm walk is interrupted if the quota check detects that the quota
 | 
				
			||||||
 | 
					 * is exceeded or if the root inode is reached.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
 | 
				
			||||||
 | 
									 loff_t delta)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
 | 
				
			||||||
 | 
						struct ceph_inode_info *ci;
 | 
				
			||||||
 | 
						struct ceph_snap_realm *realm, *next;
 | 
				
			||||||
 | 
						struct ceph_vino vino;
 | 
				
			||||||
 | 
						struct inode *in;
 | 
				
			||||||
 | 
						u64 max, rvalue;
 | 
				
			||||||
 | 
						bool is_root;
 | 
				
			||||||
 | 
						bool exceeded = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						down_read(&mdsc->snap_rwsem);
 | 
				
			||||||
 | 
						realm = ceph_inode(inode)->i_snap_realm;
 | 
				
			||||||
 | 
						ceph_get_snap_realm(mdsc, realm);
 | 
				
			||||||
 | 
						while (realm) {
 | 
				
			||||||
 | 
							vino.ino = realm->ino;
 | 
				
			||||||
 | 
							vino.snap = CEPH_NOSNAP;
 | 
				
			||||||
 | 
							in = ceph_find_inode(inode->i_sb, vino);
 | 
				
			||||||
 | 
							if (!in) {
 | 
				
			||||||
 | 
								pr_warn("Failed to find inode for %llu\n", vino.ino);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ci = ceph_inode(in);
 | 
				
			||||||
 | 
							spin_lock(&ci->i_ceph_lock);
 | 
				
			||||||
 | 
							if (op == QUOTA_CHECK_MAX_FILES_OP) {
 | 
				
			||||||
 | 
								max = ci->i_max_files;
 | 
				
			||||||
 | 
								rvalue = ci->i_rfiles + ci->i_rsubdirs;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							is_root = (ci->i_vino.ino == CEPH_INO_ROOT);
 | 
				
			||||||
 | 
							spin_unlock(&ci->i_ceph_lock);
 | 
				
			||||||
 | 
							switch (op) {
 | 
				
			||||||
 | 
							case QUOTA_CHECK_MAX_FILES_OP:
 | 
				
			||||||
 | 
								exceeded = (max && (rvalue >= max));
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								/* Shouldn't happen */
 | 
				
			||||||
 | 
								pr_warn("Invalid quota check op (%d)\n", op);
 | 
				
			||||||
 | 
								exceeded = true; /* Just break the loop */
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							iput(in);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (is_root || exceeded)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							next = realm->parent;
 | 
				
			||||||
 | 
							ceph_get_snap_realm(mdsc, next);
 | 
				
			||||||
 | 
							ceph_put_snap_realm(mdsc, realm);
 | 
				
			||||||
 | 
							realm = next;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ceph_put_snap_realm(mdsc, realm);
 | 
				
			||||||
 | 
						up_read(&mdsc->snap_rwsem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return exceeded;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * ceph_quota_is_max_files_exceeded - check if we can create a new file
 | 
				
			||||||
 | 
					 * @inode:	directory where a new file is being created
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This functions returns true is max_files quota allows a new file to be
 | 
				
			||||||
 | 
					 * created.  It is necessary to walk through the snaprealm hierarchy (until the
 | 
				
			||||||
 | 
					 * FS root) to check all realms with quotas set.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool ceph_quota_is_max_files_exceeded(struct inode *inode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						WARN_ON(!S_ISDIR(inode->i_mode));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1077,5 +1077,6 @@ extern void ceph_fs_debugfs_cleanup(struct ceph_fs_client *client);
 | 
				
			||||||
extern void ceph_handle_quota(struct ceph_mds_client *mdsc,
 | 
					extern void ceph_handle_quota(struct ceph_mds_client *mdsc,
 | 
				
			||||||
			      struct ceph_mds_session *session,
 | 
								      struct ceph_mds_session *session,
 | 
				
			||||||
			      struct ceph_msg *msg);
 | 
								      struct ceph_msg *msg);
 | 
				
			||||||
 | 
					extern bool ceph_quota_is_max_files_exceeded(struct inode *inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* _FS_CEPH_SUPER_H */
 | 
					#endif /* _FS_CEPH_SUPER_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue