lkml.org 
[lkml]   [1998]   [May]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subjectupdated patch for 2.1.103 swap code
Hi Stephen,

I've made a small change to the previously posted patch to avoid calling
swap_duplicate() unless there's a danger of blocking. This keeps the
fast path free of unnecessary increments/decrements while ensuring that
a swap entry can always be read in (barring out-of-memory errors.)

I've tested it with multiple cycles of swapoff/swapon with no warning
messages, and all seems to be working well.

Regards,
Bill--- linux-2.1.103/include/linux/swap.h.old Tue May 5 11:22:03 1998
+++ linux-2.1.103/include/linux/swap.h Thu May 21 15:27:57 1998
@@ -68,6 +68,12 @@
extern void swap_after_unlock_page (unsigned long entry);
extern struct page * read_swap_cache_async(unsigned long, int);
#define read_swap_cache(entry) read_swap_cache_async(entry, 1);
+/*
+ * Make these inline later once they are working properly.
+ */
+extern int delete_from_swap_cache(struct page *page);
+extern void remove_from_swap_cache(struct page *page);
+extern void free_page_and_swap_cache(unsigned long addr);

/* linux/mm/swapfile.c */
extern unsigned int nr_swapfiles;
@@ -75,6 +81,8 @@
void si_swapinfo(struct sysinfo *);
unsigned long get_swap_page(void);
extern void FASTCALL(swap_free(unsigned long));
+int sys_swapoff(const char *);
+int sys_swapon(const char *, int);

/*
* vm_ops not present page codes for shared memory.
@@ -137,14 +145,6 @@
}
return retval;
}
-
-/*
- * Make these inline later once they are working properly.
- */
-extern long find_in_swap_cache(struct page *page);
-extern int delete_from_swap_cache(struct page *page);
-extern void remove_from_swap_cache(struct page *page);
-extern void free_page_and_swap_cache(unsigned long addr);

#endif /* __KERNEL__*/

--- linux-2.1.103/mm/swap_state.c.old Sun Mar 22 11:30:40 1998
+++ linux-2.1.103/mm/swap_state.c Wed May 27 13:27:38 1998
@@ -165,23 +165,6 @@
__free_page (page);
}

-
-long find_in_swap_cache(struct page *page)
-{
-#ifdef SWAP_CACHE_INFO
- swap_cache_find_total++;
-#endif
- if (PageSwapCache (page)) {
- long entry = page->offset;
-#ifdef SWAP_CACHE_INFO
- swap_cache_find_success++;
-#endif
- remove_from_swap_cache (page);
- return entry;
- }
- return 0;
-}
-
int delete_from_swap_cache(struct page *page)
{
#ifdef SWAP_CACHE_INFO
@@ -229,7 +212,7 @@
* incremented.
*/

-static struct page * lookup_swap_cache(unsigned long entry)
+static struct page * lookup_swap_cache(unsigned long entry, int wait)
{
struct page *found;

@@ -237,57 +220,73 @@
found = find_page(&swapper_inode, entry);
if (!found)
return 0;
- if (found->inode != &swapper_inode
- || !PageSwapCache(found)) {
- __free_page(found);
- printk ("VM: Found a non-swapper swap page!\n");
- return 0;
- }
+ if (found->inode != &swapper_inode || !PageSwapCache(found))
+ goto out_bad;
if (!PageLocked(found))
return found;
__free_page(found);
+ if (!wait)
+ return 0;
__wait_on_page(found);
}
+
+out_bad:
+ printk ("VM: Found a non-swapper swap page!\n");
+ __free_page(found);
+ return 0;
}

/*
* Locate a page of swap in physical memory, reserving swap cache space
* and reading the disk if it is not already cached. If wait==0, we are
* only doing readahead, so don't worry if the page is already locked.
+ *
+ * The swap map count is duplicated in advance so that the entry can't
+ * be unused if we block for memory. This is necessary to avoid false
+ * failure returns from this routine and other unpleasantries.
*/

struct page * read_swap_cache_async(unsigned long entry, int wait)
{
- struct page *found_page, *new_page = 0;
- unsigned long new_page_addr = 0;
+ struct page *found_page, *new_page;
+ unsigned long new_page_addr;

#ifdef DEBUG_SWAP
printk("DebugVM: read_swap_cache_async entry %08lx%s\n",
entry, wait ? ", wait" : "");
#endif
-repeat:
- found_page = lookup_swap_cache(entry);
- if (found_page) {
- if (new_page)
- __free_page(new_page);
- return found_page;
- }
+ /*
+ * Look for the page, but don't wait.
+ */
+ found_page = lookup_swap_cache(entry, 0);
+ if (found_page)
+ goto out;
+ /*
+ * The entry is not present, or was locked.
+ * Lock down the swap map entry in case we
+ * block while allocating the page.
+ */
+ swap_duplicate(entry); /* Account for the swap cache */

- /* The entry is not present. Lock down a new page, add it to
- * the swap cache and read its contents. */
- if (!new_page) {
- new_page_addr = __get_free_page(GFP_KERNEL);
- if (!new_page_addr)
- return 0; /* Out of memory */
- new_page = mem_map + MAP_NR(new_page_addr);
- goto repeat; /* We might have stalled */
- }
+ new_page_addr = __get_free_page(GFP_KERNEL);
+ if (!new_page_addr)
+ goto out_free_entry; /* Out of memory */
+ new_page = mem_map + MAP_NR(new_page_addr);
+
+ /*
+ * Check the swap cache again ... we might have stalled.
+ * The entry is locked, so it's safe to wait this time.
+ */
+ found_page = lookup_swap_cache(entry, 1);
+ if (found_page)
+ goto out_free_page;

- if (!add_to_swap_cache(new_page, entry)) {
- free_page(new_page_addr);
- return 0;
- }
- swap_duplicate(entry); /* Account for the swap cache */
+ /*
+ * Add it to the swap cache and read its contents.
+ */
+ if (!add_to_swap_cache(new_page, entry))
+ goto out_free_page;
+
set_bit(PG_locked, &new_page->flags);
rw_swap_page(READ, entry, (char *) new_page_addr, wait);
#ifdef DEBUG_SWAP
@@ -296,5 +295,13 @@
entry, (char *) page_address(new_page));
#endif
return new_page;
+
+out_free_page:
+ __free_page(new_page);
+out_free_entry:
+ /* We didn't use the entry ... */
+ swap_free(entry);
+out:
+ return found_page;
}

--- linux-2.1.103/mm/swapfile.c.old Sun May 17 12:19:41 1998
+++ linux-2.1.103/mm/swapfile.c Thu May 21 15:27:58 1998
@@ -302,23 +302,21 @@
{
struct swap_info_struct * si = &swap_info[type];
struct task_struct *p;
- unsigned long page = 0;
struct page *page_map;
+ unsigned long page;
unsigned long entry;
- int i;
+ int i, repeat, tries = 5, error = 0;

- while (1) {
+repeat:
+ repeat = 0;
+ for (i = 1; i < si->max ; i++) {
+ int count = si->swap_map[i];
/*
- * Find a swap page in use and read it in.
+ * Look for a swap page that's still in use.
*/
- for (i = 1 , entry = 0; i < si->max ; i++) {
- if (si->swap_map[i] > 0 && si->swap_map[i] != 0x80) {
- entry = SWP_ENTRY(type, i);
- break;
- }
- }
- if (!entry)
- break;
+ if (count == 0 || count == 0x80)
+ continue;
+ entry = SWP_ENTRY(type, i);

/* Get a page for the entry, using the existing swap
cache page if there is one. Otherwise, get a clean
@@ -335,16 +333,33 @@
page we've been using. */
if (PageSwapCache(page_map))
delete_from_swap_cache(page_map);
- free_page(page);
+ __free_page(page_map);
if (si->swap_map[i] != 0) {
- if (si->swap_map[i] != 127)
- printk("try_to_unuse: entry %08lx "
- "not in use\n", entry);
- si->swap_map[i] = 0;
- nr_swap_pages++;
+ repeat = 1;
+ /*
+ * On the last pass, clear any overflowed counts,
+ * but return EBUSY if swap is still in use.
+ */
+ if (!tries) {
+ if (si->swap_map[i] == 127) {
+ printk(KERN_WARNING
+ "VM: cleared overflow for "
+ "entry=%08lx\n", entry);
+ si->swap_map[i] = 0;
+ nr_swap_pages++;
+ } else {
+ printk(KERN_ERR
+ "try_to_unuse: entry=%08lx, "
+ "count=%d??\n",
+ entry, si->swap_map[i]);
+ error = -EBUSY;
+ }
+ }
}
}
- return 0;
+ if (repeat && tries--)
+ goto repeat;
+ return error;
}

asmlinkage int sys_swapoff(const char * specialfile)
@@ -380,10 +395,9 @@
prev = type;
}
err = -EINVAL;
- if (type < 0){
- dput(dentry);
- goto out;
- }
+ if (type < 0)
+ goto out_dput;
+
if (prev < 0) {
swap_list.head = p->next;
} else {
@@ -396,7 +410,6 @@
p->flags = SWP_USED;
err = try_to_unuse(type);
if (err) {
- dput(dentry);
/* re-insert swap space back into swap_list */
for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next)
if (p->prio >= swap_info[i].prio)
@@ -407,7 +420,7 @@
else
swap_info[prev].next = p - swap_info;
p->flags = SWP_WRITEOK;
- goto out;
+ goto out_dput;
}
if(p->swap_device){
memset(&filp, 0, sizeof(filp));
@@ -422,9 +435,9 @@
}
dput(dentry);

- nr_swap_pages -= p->pages;
- dput(p->swap_file);
+ dentry = p->swap_file;
p->swap_file = NULL;
+ nr_swap_pages -= p->pages;
p->swap_device = 0;
vfree(p->swap_map);
p->swap_map = NULL;
@@ -432,6 +445,9 @@
p->swap_lockmap = NULL;
p->flags = 0;
err = 0;
+
+out_dput:
+ dput(dentry);
out:
unlock_kernel();
return err;
--- linux-2.1.103/mm/page_io.c.old Tue May 5 11:22:03 1998
+++ linux-2.1.103/mm/page_io.c Thu May 21 15:27:58 1998
@@ -74,18 +74,18 @@
return;
}
if (p->swap_map && !p->swap_map[offset]) {
- printk("Hmm.. Trying to %s unallocated swap (%08lx)\n",
+ printk("rw_swap_page: Trying to %s unallocated swap (%08lx)\n",
(rw == READ) ? "read" : "write",
entry);
return;
}
if (!(p->flags & SWP_USED)) {
- printk("Trying to swap to unused swap-device\n");
+ printk("rw_swap_page: Trying to swap to unused swap-device\n");
return;
}

if (!PageLocked(page)) {
- printk("VM: swap page is unlocked\n");
+ printk("rw_swap_page: swap page is unlocked\n");
return;
}

@@ -115,7 +115,7 @@
return;
}
if (page->offset != entry) {
- printk ("swap entry mismatch");
+ printk ("VM: swap entry mismatch\n");
return;
}

@@ -181,7 +181,12 @@
} else
printk("rw_swap_page: no swap file or device\n");

- atomic_dec(&page->count);
+#if 1 /* see how often this actually happens ... */
+ if (atomic_read(&page->count) == 1)
+ printk("rw_swap_page: page unused while waiting!\n");
+#endif
+ __free_page(page);
+
if (offset && !test_and_clear_bit(offset,p->swap_lockmap))
printk("rw_swap_page: lock already cleared\n");
wake_up(&lock_queue);

\
 
 \ /
  Last update: 2005-03-22 13:42    [from the cache]
©2003-2011 Jasper Spaans