@@ -49,6 +49,8 @@
#include "print-tree.h"
#include "volumes.h"
#include "locking.h"
+#include "hotdata_map.h"
+#include "hotdata_hash.h"
/* Mask out flags that are inappropriate for the given type of inode. */
static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
@@ -1869,7 +1871,7 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
return 0;
}
-long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg)
+static long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg)
{
struct btrfs_ioctl_space_args space_args;
struct btrfs_ioctl_space_info space;
@@ -1974,6 +1976,142 @@ long btrfs_ioctl_trans_end(struct file *file)
return 0;
}
+/*
+ * Retrieve information about access frequency for the given file. Return it in
+ * a userspace-friendly struct for btrfsctl (or another tool) to parse.
+ *
+ * The temperature that is returned can be "live" -- that is, recalculated when
+ * the ioctl is called -- or it can be returned from the hashtable, reflecting
+ * the (possibly old) value that the system will use when considering files
+ * for migration. This behavior is determined by heat_info->live.
+ */
+static long btrfs_ioctl_heat_info(struct file *file, void __user *argp)
+{
+ struct inode *mnt_inode = fdentry(file)->d_inode;
+ struct inode *file_inode;
+ struct file *file_filp;
+ struct btrfs_root *root = BTRFS_I(mnt_inode)->root;
+ struct btrfs_ioctl_heat_info *heat_info;
+ struct hot_inode_tree *hitree;
+ struct hot_inode_item *he;
+ int ret;
+
+ heat_info = kmalloc(sizeof(struct btrfs_ioctl_heat_info),
+ GFP_KERNEL | GFP_NOFS);
+
+ if (copy_from_user((void *) heat_info,
+ argp,
+ sizeof(struct btrfs_ioctl_heat_info)) != 0) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ file_filp = filp_open(heat_info->filename, O_RDONLY, 0);
+
+ if (IS_ERR(file_filp)) {
+ ret = (long) file_filp;
+ goto err;
+ }
+
+ file_inode = file_filp->f_dentry->d_inode;
+
+ hitree = &root->hot_inode_tree;
+ read_lock(&hitree->lock);
+ he = lookup_hot_inode_item(hitree, file_inode->i_ino);
+ read_unlock(&hitree->lock);
+
+ if (!he || IS_ERR(he)) {
+ /* we don't have any info on this file yet */
+ ret = -ENODATA;
+ goto err;
+ }
+
+ spin_lock(&he->lock);
+
+ heat_info->avg_delta_reads =
+ (__u64) he->freq_data.avg_delta_reads;
+ heat_info->avg_delta_writes =
+ (__u64) he->freq_data.avg_delta_writes;
+ heat_info->last_read_time =
+ (__u64) timespec_to_ns(&he->freq_data.last_read_time);
+ heat_info->last_write_time =
+ (__u64) timespec_to_ns(&he->freq_data.last_write_time);
+ heat_info->num_reads =
+ (__u32) he->freq_data.nr_reads;
+ heat_info->num_writes =
+ (__u32) he->freq_data.nr_writes;
+
+ if (heat_info->live > 0) {
+ /* got a request for live temperature,
+ * call btrfs_get_temp to recalculate */
+ heat_info->temperature = btrfs_get_temp(&he->freq_data);
+ } else {
+ /* not live temperature, get it from the hashlist */
+ read_lock(&he->heat_node->hlist->rwlock);
+ heat_info->temperature = he->heat_node->hlist->temperature;
+ read_unlock(&he->heat_node->hlist->rwlock);
+ }
+
+ spin_unlock(&he->lock);
+ free_hot_inode_item(he);
+
+ if (copy_to_user(argp, (void *) heat_info,
+ sizeof(struct btrfs_ioctl_heat_info))) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ kfree(heat_info);
+ return 0;
+
+err:
+ kfree(heat_info);
+ return ret;
+}
+
+static long btrfs_ioctl_heat_opts(struct file *file, void __user *argp, int set)
+{
+ struct inode *inode = fdentry(file)->d_inode;
+ int arg, ret = 0;
+
+ if (!set) {
+ arg = ((BTRFS_I(inode)->flags & BTRFS_INODE_NO_HOTDATA_TRACK)
+ ? 0 : 1) +
+ ((BTRFS_I(inode)->flags & BTRFS_INODE_NO_HOTDATA_MOVE)
+ ? 0 : 1);
+
+ if (copy_to_user(argp, (void *) &arg, sizeof(int)) != 0)
+ ret = -EFAULT;
+ } else if (copy_from_user((void *) &arg, argp, sizeof(int)) != 0)
+ ret = -EFAULT;
+ else
+ switch (arg) {
+ case 0: /* track nothing, move nothing */
+ /* set both flags */
+ BTRFS_I(inode)->flags |=
+ BTRFS_INODE_NO_HOTDATA_TRACK |
+ BTRFS_INODE_NO_HOTDATA_MOVE;
+ break;
+ case 1: /* do tracking, don't move anything */
+ /* clear NO_HOTDATA_TRACK, set NO_HOTDATA_MOVE */
+ BTRFS_I(inode)->flags &=
+ ~BTRFS_INODE_NO_HOTDATA_TRACK;
+ BTRFS_I(inode)->flags |=
+ BTRFS_INODE_NO_HOTDATA_MOVE;
+ break;
+ case 2: /* track and move */
+ /* clear both flags */
+ BTRFS_I(inode)->flags &=
+ ~(BTRFS_INODE_NO_HOTDATA_TRACK |
+ BTRFS_INODE_NO_HOTDATA_MOVE);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
long btrfs_ioctl(struct file *file, unsigned int
cmd, unsigned long arg)
{
@@ -2021,6 +2159,12 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_ino_lookup(file, argp);
case BTRFS_IOC_SPACE_INFO:
return btrfs_ioctl_space_info(root, argp);
+ case BTRFS_IOC_GET_HEAT_INFO:
+ return btrfs_ioctl_heat_info(file, argp);
+ case BTRFS_IOC_GET_HEAT_OPTS:
+ return btrfs_ioctl_heat_opts(file, argp, 0);
+ case BTRFS_IOC_SET_HEAT_OPTS:
+ return btrfs_ioctl_heat_opts(file, argp, 1);
case BTRFS_IOC_SYNC:
btrfs_sync_fs(file->f_dentry->d_sb, 1);
return 0;
@@ -138,6 +138,18 @@ struct btrfs_ioctl_space_args {
struct btrfs_ioctl_space_info spaces[0];
};
+struct btrfs_ioctl_heat_info {
+ __u64 avg_delta_reads;
+ __u64 avg_delta_writes;
+ __u64 last_read_time;
+ __u64 last_write_time;
+ __u32 num_reads;
+ __u32 num_writes;
+ char filename[BTRFS_PATH_NAME_MAX + 1];
+ int temperature;
+ __u8 live;
+};
+
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
struct btrfs_ioctl_vol_args)
#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@@ -178,4 +190,13 @@ struct btrfs_ioctl_space_args {
#define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, u64)
#define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \
struct btrfs_ioctl_space_args)
+
+/*
+ * Hot data tracking ioctls:
+ */
+#define BTRFS_IOC_GET_HEAT_INFO _IOWR(BTRFS_IOCTL_MAGIC, 21, \
+ struct btrfs_ioctl_heat_info)
+#define BTRFS_IOC_SET_HEAT_OPTS _IOW(BTRFS_IOCTL_MAGIC, 22, int)
+#define BTRFS_IOC_GET_HEAT_OPTS _IOR(BTRFS_IOCTL_MAGIC, 23, int)
+
#endif