Whamcloud - gitweb
LU-9091 obdclass: allow bare KMGTPE param suffix
[fs/lustre-release.git] / lustre / obdclass / class_obd.c
index 56be7fd..8543769 100644 (file)
@@ -23,7 +23,7 @@
  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright (c) 2011, 2015, Intel Corporation.
+ * Copyright (c) 2011, 2017, Intel Corporation.
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
@@ -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>
 
@@ -69,6 +67,8 @@ unsigned int obd_dump_on_timeout;
 EXPORT_SYMBOL(obd_dump_on_timeout);
 unsigned int obd_dump_on_eviction;
 EXPORT_SYMBOL(obd_dump_on_eviction);
+unsigned int obd_lbug_on_eviction;
+EXPORT_SYMBOL(obd_lbug_on_eviction);
 unsigned long obd_max_dirty_pages;
 EXPORT_SYMBOL(obd_max_dirty_pages);
 atomic_long_t obd_dirty_pages;
@@ -96,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);
@@ -133,29 +130,173 @@ out:
         RETURN(rc);
 }
 
-int class_handle_ioctl(unsigned int cmd, unsigned long arg)
+#define OBD_MAX_IOCTL_BUFFER   8192
+
+static int obd_ioctl_is_invalid(struct obd_ioctl_data *data)
 {
-        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;
+       if (data->ioc_len > BIT(30)) {
+               CERROR("OBD ioctl: ioc_len larger than 1<<30\n");
+               return 1;
+       }
 
-        /* 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;
-        }
+       if (data->ioc_inllen1 > BIT(30)) {
+               CERROR("OBD ioctl: ioc_inllen1 larger than 1<<30\n");
+               return 1;
+       }
+
+       if (data->ioc_inllen2 > BIT(30)) {
+               CERROR("OBD ioctl: ioc_inllen2 larger than 1<<30\n");
+               return 1;
+       }
+
+       if (data->ioc_inllen3 > BIT(30)) {
+               CERROR("OBD ioctl: ioc_inllen3 larger than 1<<30\n");
+               return 1;
+       }
+
+       if (data->ioc_inllen4 > BIT(30)) {
+               CERROR("OBD ioctl: ioc_inllen4 larger than 1<<30\n");
+               return 1;
+       }
+
+       if (data->ioc_inlbuf1 && data->ioc_inllen1 == 0) {
+               CERROR("OBD ioctl: inlbuf1 pointer but 0 length\n");
+               return 1;
+       }
+
+       if (data->ioc_inlbuf2 && data->ioc_inllen2 == 0) {
+               CERROR("OBD ioctl: inlbuf2 pointer but 0 length\n");
+               return 1;
+       }
+
+       if (data->ioc_inlbuf3 && data->ioc_inllen3 == 0) {
+               CERROR("OBD ioctl: inlbuf3 pointer but 0 length\n");
+               return 1;
+       }
+
+       if (data->ioc_inlbuf4 && data->ioc_inllen4 == 0) {
+               CERROR("OBD ioctl: inlbuf4 pointer but 0 length\n");
+               return 1;
+       }
+
+       if (data->ioc_pbuf1 && data->ioc_plen1 == 0) {
+               CERROR("OBD ioctl: pbuf1 pointer but 0 length\n");
+               return 1;
+       }
+
+       if (data->ioc_pbuf2 && data->ioc_plen2 == 0) {
+               CERROR("OBD ioctl: pbuf2 pointer but 0 length\n");
+               return 1;
+       }
+
+       if (!data->ioc_pbuf1 && data->ioc_plen1 != 0) {
+               CERROR("OBD ioctl: plen1 set but NULL pointer\n");
+               return 1;
+       }
+
+       if (!data->ioc_pbuf2 && data->ioc_plen2 != 0) {
+               CERROR("OBD ioctl: plen2 set but NULL pointer\n");
+               return 1;
+       }
+
+       if (obd_ioctl_packlen(data) > data->ioc_len) {
+               CERROR("OBD ioctl: packlen exceeds ioc_len (%d > %d)\n",
+                      obd_ioctl_packlen(data), data->ioc_len);
+               return 1;
+       }
+
+       return 0;
+}
 
-        CDEBUG(D_IOCTL, "cmd = %x\n", cmd);
+/* buffer MUST be at least the size of obd_ioctl_hdr */
+int obd_ioctl_getdata(char **buf, int *len, void __user *arg)
+{
+       struct obd_ioctl_hdr hdr;
+       struct obd_ioctl_data *data;
+       int offset = 0;
+
+       ENTRY;
+       if (copy_from_user(&hdr, arg, sizeof(hdr)))
+               RETURN(-EFAULT);
+
+       if (hdr.ioc_version != OBD_IOCTL_VERSION) {
+               CERROR("Version mismatch kernel (%x) vs application (%x)\n",
+                      OBD_IOCTL_VERSION, hdr.ioc_version);
+               RETURN(-EINVAL);
+       }
+
+       if (hdr.ioc_len > OBD_MAX_IOCTL_BUFFER) {
+               CERROR("User buffer len %d exceeds %d max buffer\n",
+                      hdr.ioc_len, OBD_MAX_IOCTL_BUFFER);
+               RETURN(-EINVAL);
+       }
+
+       if (hdr.ioc_len < sizeof(struct obd_ioctl_data)) {
+               CERROR("User buffer too small for ioctl (%d)\n", hdr.ioc_len);
+               RETURN(-EINVAL);
+       }
+
+       /* When there are lots of processes calling vmalloc on multi-core
+        * system, the high lock contention will hurt performance badly,
+        * obdfilter-survey is an example, which relies on ioctl. So we'd
+        * better avoid vmalloc on ioctl path. LU-66
+        */
+       OBD_ALLOC_LARGE(*buf, hdr.ioc_len);
+       if (!*buf) {
+               CERROR("Cannot allocate control buffer of len %d\n",
+                      hdr.ioc_len);
+               RETURN(-EINVAL);
+       }
+       *len = hdr.ioc_len;
+       data = (struct obd_ioctl_data *)*buf;
+
+       if (copy_from_user(*buf, arg, hdr.ioc_len)) {
+               OBD_FREE_LARGE(*buf, hdr.ioc_len);
+               RETURN(-EFAULT);
+       }
+
+       if (obd_ioctl_is_invalid(data)) {
+               CERROR("ioctl not correctly formatted\n");
+               OBD_FREE_LARGE(*buf, hdr.ioc_len);
+               RETURN(-EINVAL);
+       }
+
+       if (data->ioc_inllen1) {
+               data->ioc_inlbuf1 = &data->ioc_bulk[0];
+               offset += cfs_size_round(data->ioc_inllen1);
+       }
+
+       if (data->ioc_inllen2) {
+               data->ioc_inlbuf2 = &data->ioc_bulk[0] + offset;
+               offset += cfs_size_round(data->ioc_inllen2);
+       }
+
+       if (data->ioc_inllen3) {
+               data->ioc_inlbuf3 = &data->ioc_bulk[0] + offset;
+               offset += cfs_size_round(data->ioc_inllen3);
+       }
+
+       if (data->ioc_inllen4)
+               data->ioc_inlbuf4 = &data->ioc_bulk[0] + offset;
+
+       RETURN(0);
+}
+EXPORT_SYMBOL(obd_ioctl_getdata);
+
+int class_handle_ioctl(unsigned int cmd, unsigned long arg)
+{
+       char *buf = NULL;
+       struct obd_ioctl_data *data;
+       struct obd_device *obd = NULL;
+       int err = 0, len = 0;
+
+       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: {
@@ -274,14 +415,17 @@ int class_handle_ioctl(unsigned int cmd, unsigned long arg)
                 if (!obd)
                         GOTO(out, err = -ENOENT);
 
-                if (obd->obd_stopping)
-                        status = "ST";
-                else if (obd->obd_set_up)
-                        status = "UP";
-                else if (obd->obd_attached)
-                        status = "AT";
-                else
-                        status = "--";
+               if (obd->obd_stopping)
+                       status = "ST";
+               else if (obd->obd_inactive)
+                       status = "IN";
+               else if (obd->obd_set_up)
+                       status = "UP";
+               else if (obd->obd_attached)
+                       status = "AT";
+               else
+                       status = "--";
+
                 str = (char *)data->ioc_bulk;
                 snprintf(str, len - sizeof(*data), "%3d %s %s %s %s %d",
                          (int)index, status, obd->obd_type->typ_name,
@@ -348,70 +492,184 @@ out:
        RETURN(err);
 } /* class_handle_ioctl */
 
-static int obd_init_checks(void)
+/* to control /dev/obd */
+static long obd_class_ioctl(struct file *filp, unsigned int cmd,
+                           unsigned long arg)
+{
+       int err = 0;
+
+       ENTRY;
+       /* 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 */
+               RETURN(err = -ENOTTY);
+
+       err = class_handle_ioctl(cmd, (unsigned long)arg);
+
+       RETURN(err);
+}
+
+/* declare character device */
+static struct file_operations obd_psdev_fops = {
+       .owner          = THIS_MODULE,
+       .unlocked_ioctl = obd_class_ioctl,      /* unlocked_ioctl */
+};
+
+/* modules setup */
+struct miscdevice obd_psdev = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = OBD_DEV_NAME,
+       .fops   = &obd_psdev_fops,
+};
+
+#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)
@@ -423,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
@@ -456,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)
@@ -529,7 +787,6 @@ cleanup_lu_global:
        lu_global_fini();
 
 cleanup_class_procfs:
-       obd_sysctl_clean();
        class_procfs_clean();
 
 cleanup_caches:
@@ -598,20 +855,20 @@ static void __exit obdclass_exit(void)
        cl_global_fini();
        lu_global_fini();
 
-        obd_cleanup_caches();
-        obd_sysctl_clean();
+       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);
@@ -620,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);