Whamcloud - gitweb
LU-9091 obdclass: allow bare KMGTPE param suffix
[fs/lustre-release.git] / lustre / obdclass / class_obd.c
index e20723f..8543769 100644 (file)
@@ -34,9 +34,7 @@
 
 #include <linux/miscdevice.h>
 #include <linux/user_namespace.h>
-#ifdef HAVE_UIDGID_HEADER
-# include <linux/uidgid.h>
-#endif
+#include <linux/uidgid.h>
 #include <linux/atomic.h>
 #include <linux/list.h>
 
@@ -98,9 +96,6 @@ EXPORT_SYMBOL(at_early_margin);
 int at_extra = 30;
 EXPORT_SYMBOL(at_extra);
 
-atomic_long_t obd_dirty_transit_pages;
-EXPORT_SYMBOL(obd_dirty_transit_pages);
-
 #ifdef CONFIG_PROC_FS
 struct lprocfs_stats *obd_memory = NULL;
 EXPORT_SYMBOL(obd_memory);
@@ -290,27 +285,18 @@ EXPORT_SYMBOL(obd_ioctl_getdata);
 
 int class_handle_ioctl(unsigned int cmd, unsigned long arg)
 {
-        char *buf = NULL;
-        struct obd_ioctl_data *data;
-        struct libcfs_debug_ioctl_data *debug_data;
-        struct obd_device *obd = NULL;
-        int err = 0, len = 0;
-        ENTRY;
-
-        /* only for debugging */
-        if (cmd == LIBCFS_IOC_DEBUG_MASK) {
-                debug_data = (struct libcfs_debug_ioctl_data*)arg;
-                libcfs_subsystem_debug = debug_data->subs;
-                libcfs_debug = debug_data->debug;
-                return 0;
-        }
+       char *buf = NULL;
+       struct obd_ioctl_data *data;
+       struct obd_device *obd = NULL;
+       int err = 0, len = 0;
 
-        CDEBUG(D_IOCTL, "cmd = %x\n", cmd);
+       ENTRY;
+       CDEBUG(D_IOCTL, "cmd = %x\n", cmd);
        if (obd_ioctl_getdata(&buf, &len, (void __user *)arg)) {
-                CERROR("OBD ioctl: data error\n");
-                RETURN(-EINVAL);
-        }
-        data = (struct obd_ioctl_data *)buf;
+               CERROR("OBD ioctl: data error\n");
+               RETURN(-EINVAL);
+       }
+       data = (struct obd_ioctl_data *)buf;
 
         switch (cmd) {
         case OBD_IOC_PROCESS_CFG: {
@@ -506,23 +492,6 @@ out:
        RETURN(err);
 } /* class_handle_ioctl */
 
-/*  opening /dev/obd */
-static int obd_class_open(struct inode * inode, struct file * file)
-{
-       ENTRY;
-       try_module_get(THIS_MODULE);
-       RETURN(0);
-}
-
-/*  closing /dev/obd */
-static int obd_class_release(struct inode * inode, struct file * file)
-{
-       ENTRY;
-
-       module_put(THIS_MODULE);
-       RETURN(0);
-}
-
 /* to control /dev/obd */
 static long obd_class_ioctl(struct file *filp, unsigned int cmd,
                            unsigned long arg)
@@ -530,8 +499,8 @@ static long obd_class_ioctl(struct file *filp, unsigned int cmd,
        int err = 0;
 
        ENTRY;
-       /* Allow non-root access for OBD_IOC_PING_TARGET - used by lfs check */
-       if (!cfs_capable(CFS_CAP_SYS_ADMIN) && (cmd != OBD_IOC_PING_TARGET))
+       /* Allow non-root access for some limited ioctls */
+       if (!cfs_capable(CFS_CAP_SYS_ADMIN))
                RETURN(err = -EACCES);
 
        if ((cmd & 0xffffff00) == ((int)'T') << 8) /* ignore all tty ioctls */
@@ -546,8 +515,6 @@ static long obd_class_ioctl(struct file *filp, unsigned int cmd,
 static struct file_operations obd_psdev_fops = {
        .owner          = THIS_MODULE,
        .unlocked_ioctl = obd_class_ioctl,      /* unlocked_ioctl */
-       .open           = obd_class_open,       /* open */
-       .release        = obd_class_release,    /* release */
 };
 
 /* modules setup */
@@ -557,70 +524,152 @@ struct miscdevice obd_psdev = {
        .fops   = &obd_psdev_fops,
 };
 
-static int obd_init_checks(void)
+#define test_string_to_size_err(value, expect, def_unit, __rc)                \
+({                                                                            \
+       u64 __size;                                                            \
+       int __ret;                                                             \
+                                                                              \
+       BUILD_BUG_ON(sizeof(value) >= 23);                                     \
+       __ret = sysfs_memparse(value, sizeof(value) - 1, &__size, def_unit);   \
+       if (__ret != __rc)                                                     \
+               CERROR("string_helper: parsing '%s' expect rc %d != got %d\n", \
+                      value, __rc, __ret);                                    \
+       else if (!__ret && (u64)expect != __size)                              \
+               CERROR("string_helper: parsing '%s' expect %llu != got %llu\n",\
+                      value, (u64)expect, __size);                            \
+       __ret;                                                                 \
+})
+#define test_string_to_size_one(value, expect, def_unit)                      \
+       test_string_to_size_err(value, expect, def_unit, 0)
+
+static int __init obd_init_checks(void)
 {
-        __u64 u64val, div64val;
-        char buf[64];
-        int len, ret = 0;
+       __u64 u64val, div64val;
+       char buf[64];
+       int len, ret = 0;
 
        CDEBUG(D_INFO, "OBD_OBJECT_EOF = %#llx\n", (__u64)OBD_OBJECT_EOF);
 
-        u64val = OBD_OBJECT_EOF;
+       u64val = OBD_OBJECT_EOF;
        CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = %#llx\n", u64val);
-        if (u64val != OBD_OBJECT_EOF) {
+       if (u64val != OBD_OBJECT_EOF) {
                CERROR("__u64 %#llx(%d) != 0xffffffffffffffff\n",
-                       u64val, (int)sizeof(u64val));
-                ret = -EINVAL;
-        }
+                      u64val, (int)sizeof(u64val));
+               ret = -EINVAL;
+       }
        len = snprintf(buf, sizeof(buf), "%#llx", u64val);
-        if (len != 18) {
-               CWARN("u64 hex wrong length! strlen(%s)=%d != 18\n", buf, len);
-                ret = -EINVAL;
-        }
+       if (len != 18) {
+               CERROR("u64 hex wrong length, strlen(%s)=%d != 18\n", buf, len);
+               ret = -EINVAL;
+       }
 
-        div64val = OBD_OBJECT_EOF;
+       div64val = OBD_OBJECT_EOF;
        CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = %#llx\n", u64val);
-        if (u64val != OBD_OBJECT_EOF) {
+       if (u64val != OBD_OBJECT_EOF) {
                CERROR("__u64 %#llx(%d) != 0xffffffffffffffff\n",
-                       u64val, (int)sizeof(u64val));
-                ret = -EOVERFLOW;
-        }
-        if (u64val >> 8 != OBD_OBJECT_EOF >> 8) {
+                      u64val, (int)sizeof(u64val));
+               ret = -EOVERFLOW;
+       }
+       if (u64val >> 8 != OBD_OBJECT_EOF >> 8) {
                CERROR("__u64 %#llx(%d) != 0xffffffffffffffff\n",
-                       u64val, (int)sizeof(u64val));
-                return -EOVERFLOW;
-        }
-        if (do_div(div64val, 256) != (u64val & 255)) {
+                      u64val, (int)sizeof(u64val));
+               ret = -EOVERFLOW;
+       }
+       if (do_div(div64val, 256) != (u64val & 255)) {
                CERROR("do_div(%#llx,256) != %llu\n", u64val, u64val & 255);
-                return -EOVERFLOW;
-        }
-        if (u64val >> 8 != div64val) {
+               ret = -EOVERFLOW;
+       }
+       if (u64val >> 8 != div64val) {
                CERROR("do_div(%#llx,256) %llu != %llu\n",
-                       u64val, div64val, u64val >> 8);
-                return -EOVERFLOW;
-        }
+                      u64val, div64val, u64val >> 8);
+               ret = -EOVERFLOW;
+       }
        len = snprintf(buf, sizeof(buf), "%#llx", u64val);
-        if (len != 18) {
-               CWARN("u64 hex wrong length! strlen(%s)=%d != 18\n", buf, len);
-                ret = -EINVAL;
-        }
+       if (len != 18) {
+               CERROR("u64 hex wrong length! strlen(%s)=%d != 18\n", buf, len);
+               ret = -EINVAL;
+       }
        len = snprintf(buf, sizeof(buf), "%llu", u64val);
-        if (len != 20) {
-               CWARN("u64 wrong length! strlen(%s)=%d != 20\n", buf, len);
-                ret = -EINVAL;
-        }
+       if (len != 20) {
+               CERROR("u64 wrong length! strlen(%s)=%d != 20\n", buf, len);
+               ret = -EINVAL;
+       }
        len = snprintf(buf, sizeof(buf), "%lld", u64val);
-        if (len != 2) {
-               CWARN("s64 wrong length! strlen(%s)=%d != 2\n", buf, len);
-                ret = -EINVAL;
-        }
+       if (len != 2) {
+               CERROR("s64 wrong length! strlen(%s)=%d != 2\n", buf, len);
+               ret = -EINVAL;
+       }
        if ((u64val & ~PAGE_MASK) >= PAGE_SIZE) {
-               CWARN("mask failed: u64val %llu >= %llu\n", u64val,
-                     (__u64)PAGE_SIZE);
-                ret = -EINVAL;
-        }
+               CERROR("mask failed: u64val %llu >= %llu\n", u64val,
+                      (__u64)PAGE_SIZE);
+               ret = -EINVAL;
+       }
+       if (ret)
+               RETURN(ret);
 
-        return ret;
+       /* invalid string */
+       if (!test_string_to_size_err("256B34", 256, "B", -EINVAL)) {
+               CERROR("string_helpers: format should be number then units\n");
+               ret = -EINVAL;
+       }
+       if (!test_string_to_size_err("132OpQ", 132, "B", -EINVAL)) {
+               CERROR("string_helpers: invalid units should be rejected\n");
+               ret = -EINVAL;
+       }
+       if (!test_string_to_size_err("1.82B", 1, "B", -EINVAL)) {
+               CERROR("string_helpers: 'B' with '.' should be invalid\n");
+               ret = -EINVAL;
+       }
+       if (test_string_to_size_one("343\n", 343, "B")) {
+               CERROR("string_helpers: should ignore newline\n");
+               ret = -EINVAL;
+       }
+       if (ret)
+               RETURN(ret);
+
+       /* memparse unit handling */
+       ret = 0;
+       ret += test_string_to_size_one("0B", 0, "B");
+       ret += test_string_to_size_one("512B", 512, "B");
+       ret += test_string_to_size_one("1.067kB", 1067, "B");
+       ret += test_string_to_size_one("1.042KiB", 1067, "B");
+       ret += test_string_to_size_one("8", 8388608, "M");
+       ret += test_string_to_size_one("65536", 65536, "B");
+       ret += test_string_to_size_one("128", 131072, "K");
+       ret += test_string_to_size_one("1M", 1048576, "B");
+       ret += test_string_to_size_one("0.5T", 549755813888ULL, "T");
+       ret += test_string_to_size_one("256.5G", 275414777856ULL, "G");
+       if (ret)
+               RETURN(ret);
+
+       /* string helper values */
+       ret += test_string_to_size_one("16", 16777216, "MiB");
+       ret += test_string_to_size_one("8.39MB", 8390000, "MiB");
+       ret += test_string_to_size_one("8.00MiB", 8388608, "MiB");
+       ret += test_string_to_size_one("256GB", 256000000000ULL, "GiB");
+       ret += test_string_to_size_one("238.731GiB", 256335459385ULL, "GiB");
+       if (ret)
+               RETURN(ret);
+
+       /* huge values */
+       ret += test_string_to_size_one("0.4TB", 400000000000ULL, "TiB");
+       ret += test_string_to_size_one("12.5TiB", 13743895347200ULL, "TiB");
+       ret += test_string_to_size_one("2PB", 2000000000000000ULL, "PiB");
+       ret += test_string_to_size_one("16PiB", 18014398509481984ULL, "PiB");
+       if (ret)
+               RETURN(ret);
+
+       /* huge values should overflow */
+       if (!test_string_to_size_err("1000EiB", 0, "EiB", -EOVERFLOW)) {
+               CERROR("string_helpers: failed to detect binary overflow\n");
+               ret = -EINVAL;
+       }
+       if (!test_string_to_size_err("1000EB", 0, "EiB", -EOVERFLOW)) {
+               CERROR("string_helpers: failed to detect decimal overflow\n");
+               ret = -EINVAL;
+       }
+
+       return ret;
 }
 
 static int __init obdclass_init(void)
@@ -632,7 +681,7 @@ static int __init obdclass_init(void)
        libcfs_kkuc_init();
 
        err = obd_init_checks();
-       if (err == -EOVERFLOW)
+       if (err)
                return err;
 
 #ifdef CONFIG_PROC_FS
@@ -665,10 +714,10 @@ static int __init obdclass_init(void)
        /* Default the dirty page cache cap to 1/2 of system memory.
         * For clients with less memory, a larger fraction is needed
         * for other purposes (mostly for BGL). */
-       if (totalram_pages <= 512 << (20 - PAGE_SHIFT))
-               obd_max_dirty_pages = totalram_pages / 4;
+       if (cfs_totalram_pages() <= 512 << (20 - PAGE_SHIFT))
+               obd_max_dirty_pages = cfs_totalram_pages() / 4;
        else
-               obd_max_dirty_pages = totalram_pages / 2;
+               obd_max_dirty_pages = cfs_totalram_pages() / 2;
 
        err = obd_init_caches();
        if (err)
@@ -806,19 +855,20 @@ static void __exit obdclass_exit(void)
        cl_global_fini();
        lu_global_fini();
 
-        obd_cleanup_caches();
+       obd_cleanup_caches();
 
-        class_procfs_clean();
+       class_procfs_clean();
 
-        class_handle_cleanup();
+       class_handle_cleanup();
        class_del_uuid(NULL); /* Delete all UUIDs. */
-        obd_zombie_impexp_stop();
+       obd_zombie_impexp_stop();
 
 #ifdef CONFIG_PROC_FS
        memory_leaked = obd_memory_sum();
        memory_max = obd_memory_max();
 
        lprocfs_free_stats(&obd_memory);
+       /* the below message is checked in test-framework.sh check_mem_leak() */
        CDEBUG((memory_leaked) ? D_ERROR : D_INFO,
               "obd_memory max: %llu, leaked: %llu\n",
               memory_max, memory_leaked);
@@ -827,6 +877,90 @@ static void __exit obdclass_exit(void)
        EXIT;
 }
 
+void obd_heat_clear(struct obd_heat_instance *instance, int count)
+{
+       ENTRY;
+
+       memset(instance, 0, sizeof(*instance) * count);
+       RETURN_EXIT;
+}
+EXPORT_SYMBOL(obd_heat_clear);
+
+/*
+ * The file heat is calculated for every time interval period I. The access
+ * frequency during each period is counted. The file heat is only recalculated
+ * at the end of a time period.  And a percentage of the former file heat is
+ * lost when recalculated. The recursion formula to calculate the heat of the
+ * file f is as follow:
+ *
+ * Hi+1(f) = (1-P)*Hi(f)+ P*Ci
+ *
+ * Where Hi is the heat value in the period between time points i*I and
+ * (i+1)*I; Ci is the access count in the period; the symbol P refers to the
+ * weight of Ci. The larger the value the value of P is, the more influence Ci
+ * has on the file heat.
+ */
+void obd_heat_decay(struct obd_heat_instance *instance,  __u64 time_second,
+                   unsigned int weight, unsigned int period_second)
+{
+       u64 second;
+
+       ENTRY;
+
+       if (instance->ohi_time_second > time_second) {
+               obd_heat_clear(instance, 1);
+               RETURN_EXIT;
+       }
+
+       if (instance->ohi_time_second == 0)
+               RETURN_EXIT;
+
+       for (second = instance->ohi_time_second + period_second;
+            second < time_second;
+            second += period_second) {
+               instance->ohi_heat = instance->ohi_heat *
+                               (256 - weight) / 256 +
+                               instance->ohi_count * weight / 256;
+               instance->ohi_count = 0;
+               instance->ohi_time_second = second;
+       }
+       RETURN_EXIT;
+}
+EXPORT_SYMBOL(obd_heat_decay);
+
+__u64 obd_heat_get(struct obd_heat_instance *instance, unsigned int time_second,
+                  unsigned int weight, unsigned int period_second)
+{
+       ENTRY;
+
+       obd_heat_decay(instance, time_second, weight, period_second);
+
+       if (instance->ohi_count == 0)
+               RETURN(instance->ohi_heat);
+
+       RETURN(instance->ohi_heat * (256 - weight) / 256 +
+              instance->ohi_count * weight / 256);
+}
+EXPORT_SYMBOL(obd_heat_get);
+
+void obd_heat_add(struct obd_heat_instance *instance,
+                 unsigned int time_second,  __u64 count,
+                 unsigned int weight, unsigned int period_second)
+{
+       ENTRY;
+
+       obd_heat_decay(instance, time_second, weight, period_second);
+       if (instance->ohi_time_second == 0) {
+               instance->ohi_time_second = time_second;
+               instance->ohi_heat = 0;
+               instance->ohi_count = count;
+       } else {
+               instance->ohi_count += count;
+       }
+       RETURN_EXIT;
+}
+EXPORT_SYMBOL(obd_heat_add);
+
 MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
 MODULE_DESCRIPTION("Lustre Class Driver");
 MODULE_VERSION(LUSTRE_VERSION_STRING);