Merge branch 'for-3.11-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[sfrench/cifs-2.6.git] / mm / swapfile.c
index 746af55b8455ce0e9280b81c78e0ee5f2316f5d4..36af6eeaa67eaa0532442706e668e9a268596c90 100644 (file)
@@ -212,7 +212,7 @@ static unsigned long scan_swap_map(struct swap_info_struct *si,
                        si->cluster_nr = SWAPFILE_CLUSTER - 1;
                        goto checks;
                }
-               if (si->flags & SWP_DISCARDABLE) {
+               if (si->flags & SWP_PAGE_DISCARD) {
                        /*
                         * Start range check on racing allocations, in case
                         * they overlap the cluster we eventually decide on
@@ -322,7 +322,7 @@ checks:
 
        if (si->lowest_alloc) {
                /*
-                * Only set when SWP_DISCARDABLE, and there's a scan
+                * Only set when SWP_PAGE_DISCARD, and there's a scan
                 * for a free cluster in progress or just completed.
                 */
                if (found_free_cluster) {
@@ -2016,6 +2016,20 @@ static int setup_swap_map_and_extents(struct swap_info_struct *p,
        return nr_extents;
 }
 
+/*
+ * Helper to sys_swapon determining if a given swap
+ * backing device queue supports DISCARD operations.
+ */
+static bool swap_discardable(struct swap_info_struct *si)
+{
+       struct request_queue *q = bdev_get_queue(si->bdev);
+
+       if (!q || !blk_queue_discard(q))
+               return false;
+
+       return true;
+}
+
 SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 {
        struct swap_info_struct *p;
@@ -2123,8 +2137,37 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
                        p->flags |= SWP_SOLIDSTATE;
                        p->cluster_next = 1 + (prandom_u32() % p->highest_bit);
                }
-               if ((swap_flags & SWAP_FLAG_DISCARD) && discard_swap(p) == 0)
-                       p->flags |= SWP_DISCARDABLE;
+
+               if ((swap_flags & SWAP_FLAG_DISCARD) && swap_discardable(p)) {
+                       /*
+                        * When discard is enabled for swap with no particular
+                        * policy flagged, we set all swap discard flags here in
+                        * order to sustain backward compatibility with older
+                        * swapon(8) releases.
+                        */
+                       p->flags |= (SWP_DISCARDABLE | SWP_AREA_DISCARD |
+                                    SWP_PAGE_DISCARD);
+
+                       /*
+                        * By flagging sys_swapon, a sysadmin can tell us to
+                        * either do single-time area discards only, or to just
+                        * perform discards for released swap page-clusters.
+                        * Now it's time to adjust the p->flags accordingly.
+                        */
+                       if (swap_flags & SWAP_FLAG_DISCARD_ONCE)
+                               p->flags &= ~SWP_PAGE_DISCARD;
+                       else if (swap_flags & SWAP_FLAG_DISCARD_PAGES)
+                               p->flags &= ~SWP_AREA_DISCARD;
+
+                       /* issue a swapon-time discard if it's still required */
+                       if (p->flags & SWP_AREA_DISCARD) {
+                               int err = discard_swap(p);
+                               if (unlikely(err))
+                                       printk(KERN_ERR
+                                              "swapon: discard_swap(%p): %d\n",
+                                               p, err);
+                       }
+               }
        }
 
        mutex_lock(&swapon_mutex);
@@ -2135,11 +2178,13 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
        enable_swap_info(p, prio, swap_map, frontswap_map);
 
        printk(KERN_INFO "Adding %uk swap on %s.  "
-                       "Priority:%d extents:%d across:%lluk %s%s%s\n",
+                       "Priority:%d extents:%d across:%lluk %s%s%s%s%s\n",
                p->pages<<(PAGE_SHIFT-10), name->name, p->prio,
                nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10),
                (p->flags & SWP_SOLIDSTATE) ? "SS" : "",
                (p->flags & SWP_DISCARDABLE) ? "D" : "",
+               (p->flags & SWP_AREA_DISCARD) ? "s" : "",
+               (p->flags & SWP_PAGE_DISCARD) ? "c" : "",
                (frontswap_map) ? "FS" : "");
 
        mutex_unlock(&swapon_mutex);