virtiofs: keep a list of free dax memory ranges
authorVivek Goyal <vgoyal@redhat.com>
Wed, 19 Aug 2020 22:19:48 +0000 (18:19 -0400)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 10 Sep 2020 09:39:22 +0000 (11:39 +0200)
Divide the dax memory range into fixed size ranges (2MB for now) and put
them in a list. This will track free ranges. Once an inode requires a
free range, we will take one from here and put it in interval-tree
of ranges assigned to inode.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Peng Tao <tao.peng@linux.alibaba.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/dax.c

index 9660d01..0311060 100644 (file)
 #include "fuse_i.h"
 
 #include <linux/dax.h>
+#include <linux/pfn_t.h>
+
+/* Default memory range size, 2MB */
+#define FUSE_DAX_SHIFT 21
+#define FUSE_DAX_SZ    (1 << FUSE_DAX_SHIFT)
+#define FUSE_DAX_PAGES (FUSE_DAX_SZ / PAGE_SIZE)
+
+/** Translation information for file offsets to DAX window offsets */
+struct fuse_dax_mapping {
+       /* Will connect in fcd->free_ranges to keep track of free memory */
+       struct list_head list;
+
+       /** Position in DAX window */
+       u64 window_offset;
+
+       /** Length of mapping, in bytes */
+       loff_t length;
+};
 
 struct fuse_conn_dax {
        /* DAX device */
        struct dax_device *dev;
+
+       /* DAX Window Free Ranges */
+       long nr_free_ranges;
+       struct list_head free_ranges;
 };
 
+static void fuse_free_dax_mem_ranges(struct list_head *mem_list)
+{
+       struct fuse_dax_mapping *range, *temp;
+
+       /* Free All allocated elements */
+       list_for_each_entry_safe(range, temp, mem_list, list) {
+               list_del(&range->list);
+               kfree(range);
+       }
+}
+
 void fuse_dax_conn_free(struct fuse_conn *fc)
 {
-       kfree(fc->dax);
+       if (fc->dax) {
+               fuse_free_dax_mem_ranges(&fc->dax->free_ranges);
+               kfree(fc->dax);
+       }
+}
+
+static int fuse_dax_mem_range_init(struct fuse_conn_dax *fcd)
+{
+       long nr_pages, nr_ranges;
+       void *kaddr;
+       pfn_t pfn;
+       struct fuse_dax_mapping *range;
+       int ret, id;
+       size_t dax_size = -1;
+       unsigned long i;
+
+       INIT_LIST_HEAD(&fcd->free_ranges);
+       id = dax_read_lock();
+       nr_pages = dax_direct_access(fcd->dev, 0, PHYS_PFN(dax_size), &kaddr,
+                                    &pfn);
+       dax_read_unlock(id);
+       if (nr_pages < 0) {
+               pr_debug("dax_direct_access() returned %ld\n", nr_pages);
+               return nr_pages;
+       }
+
+       nr_ranges = nr_pages/FUSE_DAX_PAGES;
+       pr_debug("%s: dax mapped %ld pages. nr_ranges=%ld\n",
+               __func__, nr_pages, nr_ranges);
+
+       for (i = 0; i < nr_ranges; i++) {
+               range = kzalloc(sizeof(struct fuse_dax_mapping), GFP_KERNEL);
+               ret = -ENOMEM;
+               if (!range)
+                       goto out_err;
+
+               /* TODO: This offset only works if virtio-fs driver is not
+                * having some memory hidden at the beginning. This needs
+                * better handling
+                */
+               range->window_offset = i * FUSE_DAX_SZ;
+               range->length = FUSE_DAX_SZ;
+               list_add_tail(&range->list, &fcd->free_ranges);
+       }
+
+       fcd->nr_free_ranges = nr_ranges;
+       return 0;
+out_err:
+       /* Free All allocated elements */
+       fuse_free_dax_mem_ranges(&fcd->free_ranges);
+       return ret;
 }
 
 int fuse_dax_conn_alloc(struct fuse_conn *fc, struct dax_device *dax_dev)
 {
        struct fuse_conn_dax *fcd;
+       int err;
 
        if (!dax_dev)
                return 0;
@@ -30,6 +114,11 @@ int fuse_dax_conn_alloc(struct fuse_conn *fc, struct dax_device *dax_dev)
                return -ENOMEM;
 
        fcd->dev = dax_dev;
+       err = fuse_dax_mem_range_init(fcd);
+       if (err) {
+               kfree(fcd);
+               return err;
+       }
 
        fc->dax = fcd;
        return 0;