]> git.baikalelectronics.ru Git - kernel.git/commitdiff
smb3: enable swap on SMB3 mounts
authorSteve French <stfrench@microsoft.com>
Fri, 10 Apr 2020 02:42:18 +0000 (21:42 -0500)
committerSteve French <stfrench@microsoft.com>
Fri, 10 Apr 2020 18:32:32 +0000 (13:32 -0500)
Add experimental support for allowing a swap file to be on an SMB3
mount.  There are use cases where swapping over a secure network
filesystem is preferable. In some cases there are no local
block devices large enough, and network block devices can be
hard to setup and secure.  And in some cases there are no
local block devices at all (e.g. with the recent addition of
remote boot over SMB3 mounts).

There are various enhancements that can be added later e.g.:
- doing a mandatory byte range lock over the swapfile (until
the Linux VFS is modified to notify the file system that an open
is for a swapfile, when the file can be opened "DENY_ALL" to prevent
others from opening it).
- pinning more buffers in the underlying transport to minimize memory
allocations in the TCP stack under the fs
- documenting how to create ACLs (on the server) to secure the
swapfile (or adding additional tools to cifs-utils to make it easier)

Signed-off-by: Steve French <stfrench@microsoft.com>
Acked-by: Pavel Shilovsky <pshilov@microsoft.com>
Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/file.c
fs/cifs/inode.c

index 94e3ed4850b5a9e0e5e990aa595a34db9ab87a88..c31f362fa098be64e5370ea61ea3f7cd17636ae2 100644 (file)
@@ -1208,6 +1208,10 @@ static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off,
 {
        unsigned int xid = get_xid();
        ssize_t rc;
+       struct cifsFileInfo *cfile = dst_file->private_data;
+
+       if (cfile->swapfile)
+               return -EOPNOTSUPP;
 
        rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff,
                                        len, flags);
index 7448e7202e7a4e9e39db70216eefcf6f6bf30e3c..05dd3dea684b478048deca2132be96b64af69ac4 100644 (file)
@@ -1313,6 +1313,7 @@ struct cifsFileInfo {
        struct tcon_link *tlink;
        unsigned int f_flags;
        bool invalidHandle:1;   /* file closed via session abend */
+       bool swapfile:1;
        bool oplock_break_cancelled:1;
        unsigned int oplock_epoch; /* epoch from the lease break */
        __u32 oplock_level; /* oplock/lease level from the lease break */
index 5920820bfbd0770be0bc18223ba9c31367f0a2da..0b1528edebcf710bcce1e4fda34384fb95046692 100644 (file)
@@ -4808,6 +4808,60 @@ cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter)
         return -EINVAL;
 }
 
+static int cifs_swap_activate(struct swap_info_struct *sis,
+                             struct file *swap_file, sector_t *span)
+{
+       struct cifsFileInfo *cfile = swap_file->private_data;
+       struct inode *inode = swap_file->f_mapping->host;
+       unsigned long blocks;
+       long long isize;
+
+       cifs_dbg(FYI, "swap activate\n");
+
+       spin_lock(&inode->i_lock);
+       blocks = inode->i_blocks;
+       isize = inode->i_size;
+       spin_unlock(&inode->i_lock);
+       if (blocks*512 < isize) {
+               pr_warn("swap activate: swapfile has holes\n");
+               return -EINVAL;
+       }
+       *span = sis->pages;
+
+       printk_once(KERN_WARNING "Swap support over SMB3 is experimental\n");
+
+       /*
+        * TODO: consider adding ACL (or documenting how) to prevent other
+        * users (on this or other systems) from reading it
+        */
+
+
+       /* TODO: add sk_set_memalloc(inet) or similar */
+
+       if (cfile)
+               cfile->swapfile = true;
+       /*
+        * TODO: Since file already open, we can't open with DENY_ALL here
+        * but we could add call to grab a byte range lock to prevent others
+        * from reading or writing the file
+        */
+
+       return 0;
+}
+
+static void cifs_swap_deactivate(struct file *file)
+{
+       struct cifsFileInfo *cfile = file->private_data;
+
+       cifs_dbg(FYI, "swap deactivate\n");
+
+       /* TODO: undo sk_set_memalloc(inet) will eventually be needed */
+
+       if (cfile)
+               cfile->swapfile = false;
+
+       /* do we need to unpin (or unlock) the file */
+}
 
 const struct address_space_operations cifs_addr_ops = {
        .readpage = cifs_readpage,
@@ -4821,6 +4875,13 @@ const struct address_space_operations cifs_addr_ops = {
        .direct_IO = cifs_direct_io,
        .invalidatepage = cifs_invalidate_page,
        .launder_page = cifs_launder_page,
+       /*
+        * TODO: investigate and if useful we could add an cifs_migratePage
+        * helper (under an CONFIG_MIGRATION) in the future, and also
+        * investigate and add an is_dirty_writeback helper if needed
+        */
+       .swap_activate = cifs_swap_activate,
+       .swap_deactivate = cifs_swap_deactivate,
 };
 
 /*
index 8d01ec2dca6696833232c3c9dd85f50e2973ec82..8fbbdcdad8ffa89fda849927cec56fdb7666fda6 100644 (file)
@@ -2026,6 +2026,10 @@ cifs_revalidate_mapping(struct inode *inode)
        int rc;
        unsigned long *flags = &CIFS_I(inode)->flags;
 
+       /* swapfiles are not supposed to be shared */
+       if (IS_SWAPFILE(inode))
+               return 0;
+
        rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable,
                                     TASK_KILLABLE);
        if (rc)