diff options
author | Karel Zak | 2014-03-03 10:36:15 +0100 |
---|---|---|
committer | Karel Zak | 2014-03-03 10:36:15 +0100 |
commit | 6a52473ecd877227f6f7da2b95da0b51593ffec1 (patch) | |
tree | 6dd6778cf47560258450468de30c6e49c4adc169 /libmount/src/context_umount.c | |
parent | docs: refresh TODO (diff) | |
download | kernel-qcow2-util-linux-6a52473ecd877227f6f7da2b95da0b51593ffec1.tar.gz kernel-qcow2-util-linux-6a52473ecd877227f6f7da2b95da0b51593ffec1.tar.xz kernel-qcow2-util-linux-6a52473ecd877227f6f7da2b95da0b51593ffec1.zip |
umount: don't use mountinfo if possible
The umount(8) always parses /proc/self/mountinfo to get fstype and to
merge kernel mount options with userspace mount options from
/run/mount/utab. This behavior is overkill in many cases and it's
pretty expensive as kernel has to always compose *whole* mountinfo.
This performance disadvantage is visible for crazy use-cases with huge
number of mountpoints and frequently called umount(8).
It seems that we can bypass /proc/self/mountinfo by statfs() to get
filesystem type (statfs.f_type magic) and analyze /run/mount/utab
before we parse mountinfo.
This optimization is not used when:
* umount(8) executed by non-root (as user= in utab is expected)
* umount --lazy / --force (target is probably unreachable NFS, then
use statfs() is pretty bad idea)
* target is not a directory (e.g. umount /dev/sda1)
* there is (deprecated) writeable mtab
Reported-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'libmount/src/context_umount.c')
-rw-r--r-- | libmount/src/context_umount.c | 85 |
1 files changed, 83 insertions, 2 deletions
diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c index dc16852c0..2e22389f4 100644 --- a/libmount/src/context_umount.c +++ b/libmount/src/context_umount.c @@ -98,7 +98,6 @@ int mnt_context_find_umount_fs(struct libmnt_context *cxt, struct stat st; if (stat(tgt, &st) == 0 && S_ISDIR(st.st_mode)) { - /* we'll canonicalize /proc/self/mountinfo */ cache = mnt_context_get_cache(cxt); cn_tgt = mnt_resolve_path(tgt, cache); if (cn_tgt) @@ -190,14 +189,52 @@ err: return rc; } +/* Check if there is something important in the utab file. The parsed utab is + * stored in context->utab and deallocated by mnt_free_context(). + */ +static int has_utab_entry(struct libmnt_context *cxt, const char *target) +{ + struct libmnt_cache *cache = NULL; + struct libmnt_fs *fs; + struct libmnt_iter itr; + char *cn = NULL; + + assert(cxt); + + if (!cxt->utab) { + const char *path = mnt_get_utab_path(); + + if (!path || is_file_empty(path)) + return 0; + cxt->utab = mnt_new_table(); + if (!cxt->utab) + return 0; + cxt->utab->fmt = MNT_FMT_UTAB; + if (mnt_table_parse_file(cxt->utab, path)) + return 0; + } + + /* paths in utab are canonicalized */ + cache = mnt_context_get_cache(cxt); + cn = mnt_resolve_path(target, cache); + mnt_reset_iter(&itr, MNT_ITER_BACKWARD); + + while (mnt_table_next_fs(cxt->utab, &itr, &fs) == 0) { + if (mnt_fs_streq_target(fs, cn)) + return 1; + } + return 0; +} + /* this is umount replacement to mnt_context_apply_fstab(), use * mnt_context_tab_applied() to check result. */ static int lookup_umount_fs(struct libmnt_context *cxt) { const char *tgt; + struct stat st; struct libmnt_fs *fs = NULL; - int rc; + int rc = 0; assert(cxt); assert(cxt->fs); @@ -208,9 +245,53 @@ static int lookup_umount_fs(struct libmnt_context *cxt) return -EINVAL; } + /* + * Let's try to avoid mountinfo usage at all to minimize performance + * degradation. Don't forget that kernel has to compose *whole* + * mountinfo about all mountpoints although we look for only one entry. + * + * All we need is fstype and to check if there is no userspace mount + * options for the target (e.g. helper=udisks to call /sbin/umount.udisks). + * + * So, let's use statfs() if possible (it's bad idea for --lazy/--force + * umounts as target is probably unreachable NFS). + */ + if (!mnt_context_is_restricted(cxt) + && *tgt == '/' + && !(cxt->flags & MNT_FL_HELPER) + && !cxt->mtab_writable + && !mnt_context_is_force(cxt) + && !mnt_context_is_lazy(cxt) + && stat(tgt, &st) == 0 && S_ISDIR(st.st_mode) + && !has_utab_entry(cxt, tgt)) { + + const char *type = mnt_fs_get_fstype(cxt->fs); + + /* !cxt->mtab_writable && has_utab_entry() verified that there + * is no stuff in utab, so disable all mtab/utab related actions */ + mnt_context_disable_mtab(cxt, TRUE); + + if (!type) { + struct statfs vfs; + if (statfs(tgt, &vfs) == 0) + type = mnt_statfs_get_fstype(&vfs); + if (type) { + rc = mnt_fs_set_fstype(cxt->fs, type); + if (rc) + return rc; + } + } + if (type) { + DBG(CXT, mnt_debug_h(cxt, + "umount: mountinfo unnecessary [type=%s]", type)); + return 0; + } + } + rc = mnt_context_find_umount_fs(cxt, tgt, &fs); if (rc < 0) return rc; + if (rc == 1 || !fs) { DBG(CXT, mnt_debug_h(cxt, "umount: cannot find '%s' in mtab", tgt)); return 0; /* this is correct! */ |