162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * In memory quota format relies on quota infrastructure to store dquot 462306a36Sopenharmony_ci * information for us. While conventional quota formats for file systems 562306a36Sopenharmony_ci * with persistent storage can load quota information into dquot from the 662306a36Sopenharmony_ci * storage on-demand and hence quota dquot shrinker can free any dquot 762306a36Sopenharmony_ci * that is not currently being used, it must be avoided here. Otherwise we 862306a36Sopenharmony_ci * can lose valuable information, user provided limits, because there is 962306a36Sopenharmony_ci * no persistent storage to load the information from afterwards. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * One information that in-memory quota format needs to keep track of is 1262306a36Sopenharmony_ci * a sorted list of ids for each quota type. This is done by utilizing 1362306a36Sopenharmony_ci * an rb tree which root is stored in mem_dqinfo->dqi_priv for each quota 1462306a36Sopenharmony_ci * type. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * This format can be used to support quota on file system without persistent 1762306a36Sopenharmony_ci * storage such as tmpfs. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Author: Lukas Czerner <lczerner@redhat.com> 2062306a36Sopenharmony_ci * Carlos Maiolino <cmaiolino@redhat.com> 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Copyright (C) 2023 Red Hat, Inc. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#include <linux/errno.h> 2562306a36Sopenharmony_ci#include <linux/fs.h> 2662306a36Sopenharmony_ci#include <linux/mount.h> 2762306a36Sopenharmony_ci#include <linux/kernel.h> 2862306a36Sopenharmony_ci#include <linux/init.h> 2962306a36Sopenharmony_ci#include <linux/module.h> 3062306a36Sopenharmony_ci#include <linux/slab.h> 3162306a36Sopenharmony_ci#include <linux/rbtree.h> 3262306a36Sopenharmony_ci#include <linux/shmem_fs.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/quotaops.h> 3562306a36Sopenharmony_ci#include <linux/quota.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#ifdef CONFIG_TMPFS_QUOTA 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * The following constants define the amount of time given a user 4162306a36Sopenharmony_ci * before the soft limits are treated as hard limits (usually resulting 4262306a36Sopenharmony_ci * in an allocation failure). The timer is started when the user crosses 4362306a36Sopenharmony_ci * their soft limit, it is reset when they go below their soft limit. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci#define SHMEM_MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */ 4662306a36Sopenharmony_ci#define SHMEM_MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct quota_id { 4962306a36Sopenharmony_ci struct rb_node node; 5062306a36Sopenharmony_ci qid_t id; 5162306a36Sopenharmony_ci qsize_t bhardlimit; 5262306a36Sopenharmony_ci qsize_t bsoftlimit; 5362306a36Sopenharmony_ci qsize_t ihardlimit; 5462306a36Sopenharmony_ci qsize_t isoftlimit; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int shmem_check_quota_file(struct super_block *sb, int type) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci /* There is no real quota file, nothing to do */ 6062306a36Sopenharmony_ci return 1; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * There is no real quota file. Just allocate rb_root for quota ids and 6562306a36Sopenharmony_ci * set limits 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_cistatic int shmem_read_file_info(struct super_block *sb, int type) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct quota_info *dqopt = sb_dqopt(sb); 7062306a36Sopenharmony_ci struct mem_dqinfo *info = &dqopt->info[type]; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci info->dqi_priv = kzalloc(sizeof(struct rb_root), GFP_NOFS); 7362306a36Sopenharmony_ci if (!info->dqi_priv) 7462306a36Sopenharmony_ci return -ENOMEM; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci info->dqi_max_spc_limit = SHMEM_QUOTA_MAX_SPC_LIMIT; 7762306a36Sopenharmony_ci info->dqi_max_ino_limit = SHMEM_QUOTA_MAX_INO_LIMIT; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci info->dqi_bgrace = SHMEM_MAX_DQ_TIME; 8062306a36Sopenharmony_ci info->dqi_igrace = SHMEM_MAX_IQ_TIME; 8162306a36Sopenharmony_ci info->dqi_flags = 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int shmem_write_file_info(struct super_block *sb, int type) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci /* There is no real quota file, nothing to do */ 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * Free all the quota_id entries in the rb tree and rb_root. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistatic int shmem_free_file_info(struct super_block *sb, int type) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct mem_dqinfo *info = &sb_dqopt(sb)->info[type]; 9862306a36Sopenharmony_ci struct rb_root *root = info->dqi_priv; 9962306a36Sopenharmony_ci struct quota_id *entry; 10062306a36Sopenharmony_ci struct rb_node *node; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci info->dqi_priv = NULL; 10362306a36Sopenharmony_ci node = rb_first(root); 10462306a36Sopenharmony_ci while (node) { 10562306a36Sopenharmony_ci entry = rb_entry(node, struct quota_id, node); 10662306a36Sopenharmony_ci node = rb_next(&entry->node); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci rb_erase(&entry->node, root); 10962306a36Sopenharmony_ci kfree(entry); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci kfree(root); 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int shmem_get_next_id(struct super_block *sb, struct kqid *qid) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct mem_dqinfo *info = sb_dqinfo(sb, qid->type); 11962306a36Sopenharmony_ci struct rb_node *node = ((struct rb_root *)info->dqi_priv)->rb_node; 12062306a36Sopenharmony_ci qid_t id = from_kqid(&init_user_ns, *qid); 12162306a36Sopenharmony_ci struct quota_info *dqopt = sb_dqopt(sb); 12262306a36Sopenharmony_ci struct quota_id *entry = NULL; 12362306a36Sopenharmony_ci int ret = 0; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (!sb_has_quota_active(sb, qid->type)) 12662306a36Sopenharmony_ci return -ESRCH; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci down_read(&dqopt->dqio_sem); 12962306a36Sopenharmony_ci while (node) { 13062306a36Sopenharmony_ci entry = rb_entry(node, struct quota_id, node); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (id < entry->id) 13362306a36Sopenharmony_ci node = node->rb_left; 13462306a36Sopenharmony_ci else if (id > entry->id) 13562306a36Sopenharmony_ci node = node->rb_right; 13662306a36Sopenharmony_ci else 13762306a36Sopenharmony_ci goto got_next_id; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!entry) { 14162306a36Sopenharmony_ci ret = -ENOENT; 14262306a36Sopenharmony_ci goto out_unlock; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (id > entry->id) { 14662306a36Sopenharmony_ci node = rb_next(&entry->node); 14762306a36Sopenharmony_ci if (!node) { 14862306a36Sopenharmony_ci ret = -ENOENT; 14962306a36Sopenharmony_ci goto out_unlock; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci entry = rb_entry(node, struct quota_id, node); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cigot_next_id: 15562306a36Sopenharmony_ci *qid = make_kqid(&init_user_ns, qid->type, entry->id); 15662306a36Sopenharmony_ciout_unlock: 15762306a36Sopenharmony_ci up_read(&dqopt->dqio_sem); 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * Load dquot with limits from existing entry, or create the new entry if 16362306a36Sopenharmony_ci * it does not exist. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_cistatic int shmem_acquire_dquot(struct dquot *dquot) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct mem_dqinfo *info = sb_dqinfo(dquot->dq_sb, dquot->dq_id.type); 16862306a36Sopenharmony_ci struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node; 16962306a36Sopenharmony_ci struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info; 17062306a36Sopenharmony_ci struct rb_node *parent = NULL, *new_node = NULL; 17162306a36Sopenharmony_ci struct quota_id *new_entry, *entry; 17262306a36Sopenharmony_ci qid_t id = from_kqid(&init_user_ns, dquot->dq_id); 17362306a36Sopenharmony_ci struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); 17462306a36Sopenharmony_ci int ret = 0; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci mutex_lock(&dquot->dq_lock); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci down_write(&dqopt->dqio_sem); 17962306a36Sopenharmony_ci while (*n) { 18062306a36Sopenharmony_ci parent = *n; 18162306a36Sopenharmony_ci entry = rb_entry(parent, struct quota_id, node); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (id < entry->id) 18462306a36Sopenharmony_ci n = &(*n)->rb_left; 18562306a36Sopenharmony_ci else if (id > entry->id) 18662306a36Sopenharmony_ci n = &(*n)->rb_right; 18762306a36Sopenharmony_ci else 18862306a36Sopenharmony_ci goto found; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* We don't have entry for this id yet, create it */ 19262306a36Sopenharmony_ci new_entry = kzalloc(sizeof(struct quota_id), GFP_NOFS); 19362306a36Sopenharmony_ci if (!new_entry) { 19462306a36Sopenharmony_ci ret = -ENOMEM; 19562306a36Sopenharmony_ci goto out_unlock; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci new_entry->id = id; 19962306a36Sopenharmony_ci if (dquot->dq_id.type == USRQUOTA) { 20062306a36Sopenharmony_ci new_entry->bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; 20162306a36Sopenharmony_ci new_entry->ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; 20262306a36Sopenharmony_ci } else if (dquot->dq_id.type == GRPQUOTA) { 20362306a36Sopenharmony_ci new_entry->bhardlimit = sbinfo->qlimits.grpquota_bhardlimit; 20462306a36Sopenharmony_ci new_entry->ihardlimit = sbinfo->qlimits.grpquota_ihardlimit; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci new_node = &new_entry->node; 20862306a36Sopenharmony_ci rb_link_node(new_node, parent, n); 20962306a36Sopenharmony_ci rb_insert_color(new_node, (struct rb_root *)info->dqi_priv); 21062306a36Sopenharmony_ci entry = new_entry; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cifound: 21362306a36Sopenharmony_ci /* Load the stored limits from the tree */ 21462306a36Sopenharmony_ci spin_lock(&dquot->dq_dqb_lock); 21562306a36Sopenharmony_ci dquot->dq_dqb.dqb_bhardlimit = entry->bhardlimit; 21662306a36Sopenharmony_ci dquot->dq_dqb.dqb_bsoftlimit = entry->bsoftlimit; 21762306a36Sopenharmony_ci dquot->dq_dqb.dqb_ihardlimit = entry->ihardlimit; 21862306a36Sopenharmony_ci dquot->dq_dqb.dqb_isoftlimit = entry->isoftlimit; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!dquot->dq_dqb.dqb_bhardlimit && 22162306a36Sopenharmony_ci !dquot->dq_dqb.dqb_bsoftlimit && 22262306a36Sopenharmony_ci !dquot->dq_dqb.dqb_ihardlimit && 22362306a36Sopenharmony_ci !dquot->dq_dqb.dqb_isoftlimit) 22462306a36Sopenharmony_ci set_bit(DQ_FAKE_B, &dquot->dq_flags); 22562306a36Sopenharmony_ci spin_unlock(&dquot->dq_dqb_lock); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Make sure flags update is visible after dquot has been filled */ 22862306a36Sopenharmony_ci smp_mb__before_atomic(); 22962306a36Sopenharmony_ci set_bit(DQ_ACTIVE_B, &dquot->dq_flags); 23062306a36Sopenharmony_ciout_unlock: 23162306a36Sopenharmony_ci up_write(&dqopt->dqio_sem); 23262306a36Sopenharmony_ci mutex_unlock(&dquot->dq_lock); 23362306a36Sopenharmony_ci return ret; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic bool shmem_is_empty_dquot(struct dquot *dquot) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info; 23962306a36Sopenharmony_ci qsize_t bhardlimit; 24062306a36Sopenharmony_ci qsize_t ihardlimit; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (dquot->dq_id.type == USRQUOTA) { 24362306a36Sopenharmony_ci bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; 24462306a36Sopenharmony_ci ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; 24562306a36Sopenharmony_ci } else if (dquot->dq_id.type == GRPQUOTA) { 24662306a36Sopenharmony_ci bhardlimit = sbinfo->qlimits.grpquota_bhardlimit; 24762306a36Sopenharmony_ci ihardlimit = sbinfo->qlimits.grpquota_ihardlimit; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (test_bit(DQ_FAKE_B, &dquot->dq_flags) || 25162306a36Sopenharmony_ci (dquot->dq_dqb.dqb_curspace == 0 && 25262306a36Sopenharmony_ci dquot->dq_dqb.dqb_curinodes == 0 && 25362306a36Sopenharmony_ci dquot->dq_dqb.dqb_bhardlimit == bhardlimit && 25462306a36Sopenharmony_ci dquot->dq_dqb.dqb_ihardlimit == ihardlimit)) 25562306a36Sopenharmony_ci return true; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return false; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci/* 26062306a36Sopenharmony_ci * Store limits from dquot in the tree unless it's fake. If it is fake 26162306a36Sopenharmony_ci * remove the id from the tree since there is no useful information in 26262306a36Sopenharmony_ci * there. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_cistatic int shmem_release_dquot(struct dquot *dquot) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct mem_dqinfo *info = sb_dqinfo(dquot->dq_sb, dquot->dq_id.type); 26762306a36Sopenharmony_ci struct rb_node *node = ((struct rb_root *)info->dqi_priv)->rb_node; 26862306a36Sopenharmony_ci qid_t id = from_kqid(&init_user_ns, dquot->dq_id); 26962306a36Sopenharmony_ci struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); 27062306a36Sopenharmony_ci struct quota_id *entry = NULL; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci mutex_lock(&dquot->dq_lock); 27362306a36Sopenharmony_ci /* Check whether we are not racing with some other dqget() */ 27462306a36Sopenharmony_ci if (dquot_is_busy(dquot)) 27562306a36Sopenharmony_ci goto out_dqlock; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci down_write(&dqopt->dqio_sem); 27862306a36Sopenharmony_ci while (node) { 27962306a36Sopenharmony_ci entry = rb_entry(node, struct quota_id, node); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (id < entry->id) 28262306a36Sopenharmony_ci node = node->rb_left; 28362306a36Sopenharmony_ci else if (id > entry->id) 28462306a36Sopenharmony_ci node = node->rb_right; 28562306a36Sopenharmony_ci else 28662306a36Sopenharmony_ci goto found; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* We should always find the entry in the rb tree */ 29062306a36Sopenharmony_ci WARN_ONCE(1, "quota id %u from dquot %p, not in rb tree!\n", id, dquot); 29162306a36Sopenharmony_ci up_write(&dqopt->dqio_sem); 29262306a36Sopenharmony_ci mutex_unlock(&dquot->dq_lock); 29362306a36Sopenharmony_ci return -ENOENT; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cifound: 29662306a36Sopenharmony_ci if (shmem_is_empty_dquot(dquot)) { 29762306a36Sopenharmony_ci /* Remove entry from the tree */ 29862306a36Sopenharmony_ci rb_erase(&entry->node, info->dqi_priv); 29962306a36Sopenharmony_ci kfree(entry); 30062306a36Sopenharmony_ci } else { 30162306a36Sopenharmony_ci /* Store the limits in the tree */ 30262306a36Sopenharmony_ci spin_lock(&dquot->dq_dqb_lock); 30362306a36Sopenharmony_ci entry->bhardlimit = dquot->dq_dqb.dqb_bhardlimit; 30462306a36Sopenharmony_ci entry->bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit; 30562306a36Sopenharmony_ci entry->ihardlimit = dquot->dq_dqb.dqb_ihardlimit; 30662306a36Sopenharmony_ci entry->isoftlimit = dquot->dq_dqb.dqb_isoftlimit; 30762306a36Sopenharmony_ci spin_unlock(&dquot->dq_dqb_lock); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci clear_bit(DQ_ACTIVE_B, &dquot->dq_flags); 31162306a36Sopenharmony_ci up_write(&dqopt->dqio_sem); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciout_dqlock: 31462306a36Sopenharmony_ci mutex_unlock(&dquot->dq_lock); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int shmem_mark_dquot_dirty(struct dquot *dquot) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int shmem_dquot_write_info(struct super_block *sb, int type) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic const struct quota_format_ops shmem_format_ops = { 32962306a36Sopenharmony_ci .check_quota_file = shmem_check_quota_file, 33062306a36Sopenharmony_ci .read_file_info = shmem_read_file_info, 33162306a36Sopenharmony_ci .write_file_info = shmem_write_file_info, 33262306a36Sopenharmony_ci .free_file_info = shmem_free_file_info, 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistruct quota_format_type shmem_quota_format = { 33662306a36Sopenharmony_ci .qf_fmt_id = QFMT_SHMEM, 33762306a36Sopenharmony_ci .qf_ops = &shmem_format_ops, 33862306a36Sopenharmony_ci .qf_owner = THIS_MODULE 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ciconst struct dquot_operations shmem_quota_operations = { 34262306a36Sopenharmony_ci .acquire_dquot = shmem_acquire_dquot, 34362306a36Sopenharmony_ci .release_dquot = shmem_release_dquot, 34462306a36Sopenharmony_ci .alloc_dquot = dquot_alloc, 34562306a36Sopenharmony_ci .destroy_dquot = dquot_destroy, 34662306a36Sopenharmony_ci .write_info = shmem_dquot_write_info, 34762306a36Sopenharmony_ci .mark_dirty = shmem_mark_dquot_dirty, 34862306a36Sopenharmony_ci .get_next_id = shmem_get_next_id, 34962306a36Sopenharmony_ci}; 35062306a36Sopenharmony_ci#endif /* CONFIG_TMPFS_QUOTA */ 351