diff options
-rw-r--r-- | include/linux/swap.h | 13 | ||||
-rw-r--r-- | mm/swapfile.c | 55 |
2 files changed, 59 insertions, 9 deletions
diff --git a/include/linux/swap.h b/include/linux/swap.h index 85d74373002c..d95cde5e257d 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
@@ -20,10 +20,13 @@ struct bio; | |||
20 | #define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */ | 20 | #define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */ |
21 | #define SWAP_FLAG_PRIO_MASK 0x7fff | 21 | #define SWAP_FLAG_PRIO_MASK 0x7fff |
22 | #define SWAP_FLAG_PRIO_SHIFT 0 | 22 | #define SWAP_FLAG_PRIO_SHIFT 0 |
23 | #define SWAP_FLAG_DISCARD 0x10000 /* discard swap cluster after use */ | 23 | #define SWAP_FLAG_DISCARD 0x10000 /* enable discard for swap */ |
24 | #define SWAP_FLAG_DISCARD_ONCE 0x20000 /* discard swap area at swapon-time */ | ||
25 | #define SWAP_FLAG_DISCARD_PAGES 0x40000 /* discard page-clusters after use */ | ||
24 | 26 | ||
25 | #define SWAP_FLAGS_VALID (SWAP_FLAG_PRIO_MASK | SWAP_FLAG_PREFER | \ | 27 | #define SWAP_FLAGS_VALID (SWAP_FLAG_PRIO_MASK | SWAP_FLAG_PREFER | \ |
26 | SWAP_FLAG_DISCARD) | 28 | SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | \ |
29 | SWAP_FLAG_DISCARD_PAGES) | ||
27 | 30 | ||
28 | static inline int current_is_kswapd(void) | 31 | static inline int current_is_kswapd(void) |
29 | { | 32 | { |
@@ -147,14 +150,16 @@ struct swap_extent { | |||
147 | enum { | 150 | enum { |
148 | SWP_USED = (1 << 0), /* is slot in swap_info[] used? */ | 151 | SWP_USED = (1 << 0), /* is slot in swap_info[] used? */ |
149 | SWP_WRITEOK = (1 << 1), /* ok to write to this swap? */ | 152 | SWP_WRITEOK = (1 << 1), /* ok to write to this swap? */ |
150 | SWP_DISCARDABLE = (1 << 2), /* swapon+blkdev support discard */ | 153 | SWP_DISCARDABLE = (1 << 2), /* blkdev support discard */ |
151 | SWP_DISCARDING = (1 << 3), /* now discarding a free cluster */ | 154 | SWP_DISCARDING = (1 << 3), /* now discarding a free cluster */ |
152 | SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */ | 155 | SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */ |
153 | SWP_CONTINUED = (1 << 5), /* swap_map has count continuation */ | 156 | SWP_CONTINUED = (1 << 5), /* swap_map has count continuation */ |
154 | SWP_BLKDEV = (1 << 6), /* its a block device */ | 157 | SWP_BLKDEV = (1 << 6), /* its a block device */ |
155 | SWP_FILE = (1 << 7), /* set after swap_activate success */ | 158 | SWP_FILE = (1 << 7), /* set after swap_activate success */ |
159 | SWP_AREA_DISCARD = (1 << 8), /* single-time swap area discards */ | ||
160 | SWP_PAGE_DISCARD = (1 << 9), /* freed swap page-cluster discards */ | ||
156 | /* add others here before... */ | 161 | /* add others here before... */ |
157 | SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ | 162 | SWP_SCANNING = (1 << 10), /* refcount in scan_swap_map */ |
158 | }; | 163 | }; |
159 | 164 | ||
160 | #define SWAP_CLUSTER_MAX 32UL | 165 | #define SWAP_CLUSTER_MAX 32UL |
diff --git a/mm/swapfile.c b/mm/swapfile.c index 746af55b8455..36af6eeaa67e 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c | |||
@@ -212,7 +212,7 @@ static unsigned long scan_swap_map(struct swap_info_struct *si, | |||
212 | si->cluster_nr = SWAPFILE_CLUSTER - 1; | 212 | si->cluster_nr = SWAPFILE_CLUSTER - 1; |
213 | goto checks; | 213 | goto checks; |
214 | } | 214 | } |
215 | if (si->flags & SWP_DISCARDABLE) { | 215 | if (si->flags & SWP_PAGE_DISCARD) { |
216 | /* | 216 | /* |
217 | * Start range check on racing allocations, in case | 217 | * Start range check on racing allocations, in case |
218 | * they overlap the cluster we eventually decide on | 218 | * they overlap the cluster we eventually decide on |
@@ -322,7 +322,7 @@ checks: | |||
322 | 322 | ||
323 | if (si->lowest_alloc) { | 323 | if (si->lowest_alloc) { |
324 | /* | 324 | /* |
325 | * Only set when SWP_DISCARDABLE, and there's a scan | 325 | * Only set when SWP_PAGE_DISCARD, and there's a scan |
326 | * for a free cluster in progress or just completed. | 326 | * for a free cluster in progress or just completed. |
327 | */ | 327 | */ |
328 | if (found_free_cluster) { | 328 | if (found_free_cluster) { |
@@ -2016,6 +2016,20 @@ static int setup_swap_map_and_extents(struct swap_info_struct *p, | |||
2016 | return nr_extents; | 2016 | return nr_extents; |
2017 | } | 2017 | } |
2018 | 2018 | ||
2019 | /* | ||
2020 | * Helper to sys_swapon determining if a given swap | ||
2021 | * backing device queue supports DISCARD operations. | ||
2022 | */ | ||
2023 | static bool swap_discardable(struct swap_info_struct *si) | ||
2024 | { | ||
2025 | struct request_queue *q = bdev_get_queue(si->bdev); | ||
2026 | |||
2027 | if (!q || !blk_queue_discard(q)) | ||
2028 | return false; | ||
2029 | |||
2030 | return true; | ||
2031 | } | ||
2032 | |||
2019 | SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) | 2033 | SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) |
2020 | { | 2034 | { |
2021 | struct swap_info_struct *p; | 2035 | struct swap_info_struct *p; |
@@ -2123,8 +2137,37 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) | |||
2123 | p->flags |= SWP_SOLIDSTATE; | 2137 | p->flags |= SWP_SOLIDSTATE; |
2124 | p->cluster_next = 1 + (prandom_u32() % p->highest_bit); | 2138 | p->cluster_next = 1 + (prandom_u32() % p->highest_bit); |
2125 | } | 2139 | } |
2126 | if ((swap_flags & SWAP_FLAG_DISCARD) && discard_swap(p) == 0) | 2140 | |
2127 | p->flags |= SWP_DISCARDABLE; | 2141 | if ((swap_flags & SWAP_FLAG_DISCARD) && swap_discardable(p)) { |
2142 | /* | ||
2143 | * When discard is enabled for swap with no particular | ||
2144 | * policy flagged, we set all swap discard flags here in | ||
2145 | * order to sustain backward compatibility with older | ||
2146 | * swapon(8) releases. | ||
2147 | */ | ||
2148 | p->flags |= (SWP_DISCARDABLE | SWP_AREA_DISCARD | | ||
2149 | SWP_PAGE_DISCARD); | ||
2150 | |||
2151 | /* | ||
2152 | * By flagging sys_swapon, a sysadmin can tell us to | ||
2153 | * either do single-time area discards only, or to just | ||
2154 | * perform discards for released swap page-clusters. | ||
2155 | * Now it's time to adjust the p->flags accordingly. | ||
2156 | */ | ||
2157 | if (swap_flags & SWAP_FLAG_DISCARD_ONCE) | ||
2158 | p->flags &= ~SWP_PAGE_DISCARD; | ||
2159 | else if (swap_flags & SWAP_FLAG_DISCARD_PAGES) | ||
2160 | p->flags &= ~SWP_AREA_DISCARD; | ||
2161 | |||
2162 | /* issue a swapon-time discard if it's still required */ | ||
2163 | if (p->flags & SWP_AREA_DISCARD) { | ||
2164 | int err = discard_swap(p); | ||
2165 | if (unlikely(err)) | ||
2166 | printk(KERN_ERR | ||
2167 | "swapon: discard_swap(%p): %d\n", | ||
2168 | p, err); | ||
2169 | } | ||
2170 | } | ||
2128 | } | 2171 | } |
2129 | 2172 | ||
2130 | mutex_lock(&swapon_mutex); | 2173 | mutex_lock(&swapon_mutex); |
@@ -2135,11 +2178,13 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) | |||
2135 | enable_swap_info(p, prio, swap_map, frontswap_map); | 2178 | enable_swap_info(p, prio, swap_map, frontswap_map); |
2136 | 2179 | ||
2137 | printk(KERN_INFO "Adding %uk swap on %s. " | 2180 | printk(KERN_INFO "Adding %uk swap on %s. " |
2138 | "Priority:%d extents:%d across:%lluk %s%s%s\n", | 2181 | "Priority:%d extents:%d across:%lluk %s%s%s%s%s\n", |
2139 | p->pages<<(PAGE_SHIFT-10), name->name, p->prio, | 2182 | p->pages<<(PAGE_SHIFT-10), name->name, p->prio, |
2140 | nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10), | 2183 | nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10), |
2141 | (p->flags & SWP_SOLIDSTATE) ? "SS" : "", | 2184 | (p->flags & SWP_SOLIDSTATE) ? "SS" : "", |
2142 | (p->flags & SWP_DISCARDABLE) ? "D" : "", | 2185 | (p->flags & SWP_DISCARDABLE) ? "D" : "", |
2186 | (p->flags & SWP_AREA_DISCARD) ? "s" : "", | ||
2187 | (p->flags & SWP_PAGE_DISCARD) ? "c" : "", | ||
2143 | (frontswap_map) ? "FS" : ""); | 2188 | (frontswap_map) ? "FS" : ""); |
2144 | 2189 | ||
2145 | mutex_unlock(&swapon_mutex); | 2190 | mutex_unlock(&swapon_mutex); |