int (*launder_page)(struct page *);
        int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long);
        int (*error_remove_page)(struct address_space *, struct page *);
+       int (*swap_activate)(struct file *);
+       int (*swap_deactivate)(struct file *);
 
 locking rules:
        All except set_page_dirty and freepage may block
 launder_page:          yes
 is_partially_uptodate: yes
 error_remove_page:     yes
+swap_activate:         no
+swap_deactivate:       no
 
        ->write_begin(), ->write_end(), ->sync_page() and ->readpage()
 may be called from the request handler (/dev/loop).
 getting mapped back in and redirtied, it needs to be kept locked
 across the entire operation.
 
+       ->swap_activate will be called with a non-zero argument on
+files backing (non block device backed) swapfiles. A return value
+of zero indicates success, in which case this file can be used for
+backing swapspace. The swapspace operations will be proxied to the
+address space operations.
+
+       ->swap_deactivate() will be called in the sys_swapoff()
+path after ->swap_activate() returned success.
+
 ----------------------- file_lock_operations ------------------------------
 prototypes:
        void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
 
        int (*migratepage) (struct page *, struct page *);
        int (*launder_page) (struct page *);
        int (*error_remove_page) (struct mapping *mapping, struct page *page);
+       int (*swap_activate)(struct file *);
+       int (*swap_deactivate)(struct file *);
 };
 
   writepage: called by the VM to write a dirty page to backing store.
        Setting this implies you deal with pages going away under you,
        unless you have them locked or reference counts increased.
 
+  swap_activate: Called when swapon is used on a file to allocate
+       space if necessary and pin the block lookup information in
+       memory. A return value of zero indicates success,
+       in which case this file can be used to back swapspace. The
+       swapspace operations will be proxied to this address space's
+       ->swap_{out,in} methods.
+
+  swap_deactivate: Called during swapoff on files where swap_activate
+       was successful.
+
 
 The File Object
 ===============
 
        int (*is_partially_uptodate) (struct page *, read_descriptor_t *,
                                        unsigned long);
        int (*error_remove_page)(struct address_space *, struct page *);
+
+       /* swapfile support */
+       int (*swap_activate)(struct file *file);
+       int (*swap_deactivate)(struct file *file);
 };
 
 extern const struct address_space_operations empty_aops;
 
        SWP_SOLIDSTATE  = (1 << 4),     /* blkdev seeks are cheap */
        SWP_CONTINUED   = (1 << 5),     /* swap_map has count continuation */
        SWP_BLKDEV      = (1 << 6),     /* its a block device */
+       SWP_FILE        = (1 << 7),     /* set after swap_activate success */
                                        /* add others here before... */
        SWP_SCANNING    = (1 << 8),     /* refcount in scan_swap_map */
 };
 /* linux/mm/page_io.c */
 extern int swap_readpage(struct page *);
 extern int swap_writepage(struct page *page, struct writeback_control *wbc);
+extern int swap_set_page_dirty(struct page *page);
 extern void end_swap_bio_read(struct bio *bio, int err);
 
 /* linux/mm/swap_state.c */
 
 #include <linux/swap.h>
 #include <linux/bio.h>
 #include <linux/swapops.h>
+#include <linux/buffer_head.h>
 #include <linux/writeback.h>
 #include <linux/frontswap.h>
 #include <asm/pgtable.h>
 {
        struct bio *bio;
        int ret = 0, rw = WRITE;
+       struct swap_info_struct *sis = page_swap_info(page);
 
        if (try_to_free_swap(page)) {
                unlock_page(page);
                end_page_writeback(page);
                goto out;
        }
+
+       if (sis->flags & SWP_FILE) {
+               struct kiocb kiocb;
+               struct file *swap_file = sis->swap_file;
+               struct address_space *mapping = swap_file->f_mapping;
+               struct iovec iov = {
+                       .iov_base = page_address(page),
+                       .iov_len  = PAGE_SIZE,
+               };
+
+               init_sync_kiocb(&kiocb, swap_file);
+               kiocb.ki_pos = page_file_offset(page);
+               kiocb.ki_left = PAGE_SIZE;
+               kiocb.ki_nbytes = PAGE_SIZE;
+
+               unlock_page(page);
+               ret = mapping->a_ops->direct_IO(KERNEL_WRITE,
+                                               &kiocb, &iov,
+                                               kiocb.ki_pos, 1);
+               if (ret == PAGE_SIZE) {
+                       count_vm_event(PSWPOUT);
+                       ret = 0;
+               }
+               return ret;
+       }
+
        bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write);
        if (bio == NULL) {
                set_page_dirty(page);
 {
        struct bio *bio;
        int ret = 0;
+       struct swap_info_struct *sis = page_swap_info(page);
 
        VM_BUG_ON(!PageLocked(page));
        VM_BUG_ON(PageUptodate(page));
                unlock_page(page);
                goto out;
        }
+
+       if (sis->flags & SWP_FILE) {
+               struct file *swap_file = sis->swap_file;
+               struct address_space *mapping = swap_file->f_mapping;
+
+               ret = mapping->a_ops->readpage(swap_file, page);
+               if (!ret)
+                       count_vm_event(PSWPIN);
+               return ret;
+       }
+
        bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read);
        if (bio == NULL) {
                unlock_page(page);
 out:
        return ret;
 }
+
+int swap_set_page_dirty(struct page *page)
+{
+       struct swap_info_struct *sis = page_swap_info(page);
+
+       if (sis->flags & SWP_FILE) {
+               struct address_space *mapping = sis->swap_file->f_mapping;
+               return mapping->a_ops->set_page_dirty(page);
+       } else {
+               return __set_page_dirty_no_writeback(page);
+       }
+}
 
  */
 static const struct address_space_operations swap_aops = {
        .writepage      = swap_writepage,
-       .set_page_dirty = __set_page_dirty_no_writeback,
+       .set_page_dirty = swap_set_page_dirty,
        .migratepage    = migrate_page,
 };
 
 
                list_del(&se->list);
                kfree(se);
        }
+
+       if (sis->flags & SWP_FILE) {
+               struct file *swap_file = sis->swap_file;
+               struct address_space *mapping = swap_file->f_mapping;
+
+               sis->flags &= ~SWP_FILE;
+               mapping->a_ops->swap_deactivate(swap_file);
+       }
 }
 
 /*
  */
 static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span)
 {
-       struct inode *inode;
+       struct file *swap_file = sis->swap_file;
+       struct address_space *mapping = swap_file->f_mapping;
+       struct inode *inode = mapping->host;
        unsigned blocks_per_page;
        unsigned long page_no;
        unsigned blkbits;
        int nr_extents = 0;
        int ret;
 
-       inode = sis->swap_file->f_mapping->host;
        if (S_ISBLK(inode->i_mode)) {
                ret = add_swap_extent(sis, 0, sis->max, 0);
                *span = sis->pages;
                goto out;
        }
 
+       if (mapping->a_ops->swap_activate) {
+               ret = mapping->a_ops->swap_activate(swap_file);
+               if (!ret) {
+                       sis->flags |= SWP_FILE;
+                       ret = add_swap_extent(sis, 0, sis->max, 0);
+                       *span = sis->pages;
+               }
+               goto out;
+       }
+
        blkbits = inode->i_blkbits;
        blocks_per_page = PAGE_SIZE >> blkbits;