void swap_duplicate(unsigned long entry) { struct swap_info_struct * p; unsigned long offset, type; if (!entry) return; offset = SWP_OFFSET(entry); type = SWP_TYPE(entry); if (type & SHM_SWP_TYPE) return; if (type >= nr_swapfiles) { printk("Trying to duplicate nonexistent swap-page\n"); return; } p = type + swap_info; if (offset >= p->max) { printk("swap_duplicate: weirdness\n"); return; } if (!p->swap_map[offset]) { printk("swap_duplicate: trying to duplicate unused page\n"); return; } p->swap_map[offset]++; return; }
/* * page not present ... go through shm_pages */ static unsigned long shm_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share) { pte_t pte; struct shmid_kernel *shp; unsigned int id, idx; id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK; idx = (address - shmd->vm_start + shmd->vm_offset) >> PAGE_SHIFT; #ifdef DEBUG_SHM if (id > max_shmid) { printk ("shm_nopage: id=%d too big. proc mem corrupted\n", id); return 0; } #endif shp = shm_segs[id]; #ifdef DEBUG_SHM if (shp == IPC_UNUSED || shp == IPC_NOID) { printk ("shm_nopage: id=%d invalid. Race.\n", id); return 0; } #endif /* This can occur on a remap */ if (idx >= shp->shm_npages) { return 0; } pte = __pte(shp->shm_pages[idx]); if (!pte_present(pte)) { unsigned long page = get_free_page(GFP_USER); if (!page) return -1; pte = __pte(shp->shm_pages[idx]); if (pte_present(pte)) { free_page (page); /* doesn't sleep */ goto done; } if (!pte_none(pte)) { rw_swap_page_nocache(READ, pte_val(pte), (char *)page); pte = __pte(shp->shm_pages[idx]); if (pte_present(pte)) { free_page (page); /* doesn't sleep */ goto done; } swap_free(pte_val(pte)); shm_swp--; } shm_rss++; pte = pte_mkdirty(mk_pte(page, PAGE_SHARED)); shp->shm_pages[idx] = pte_val(pte); } else --current->maj_flt; /* was incremented in do_no_page */ done: /* pte_val(pte) == shp->shm_pages[idx] */ current->min_flt++; atomic_inc(&mem_map[MAP_NR(pte_page(pte))].count); return pte_page(pte); }
/* * Caller has made sure that the swapdevice corresponding to entry * is still around or has not been recycled. */ void swap_free(swp_entry_t entry) { struct swap_info_struct * p; p = swap_info_get(entry); if (p) { swap_entry_free(p, SWP_OFFSET(entry)); swap_info_put(p); } }
/* * Verify that a swap entry is valid and increment its swap map count. * * Note: if swap_map[] reaches SWAP_MAP_MAX the entries are treated as * "permanent", but will be reclaimed by the next swapoff. */ int swap_duplicate(unsigned long entry) { struct swap_info_struct * p; unsigned long offset, type; int result = 0; if (!entry) goto out; type = SWP_TYPE(entry); if (type & SHM_SWP_TYPE) goto out; if (type >= nr_swapfiles) goto bad_file; p = type + swap_info; offset = SWP_OFFSET(entry); if (offset >= p->max) goto bad_offset; if (!p->swap_map[offset]) goto bad_unused; /* * Entry is valid, so increment the map count. */ if (p->swap_map[offset] < SWAP_MAP_MAX) p->swap_map[offset]++; else { static int overflow = 0; if (overflow++ < 5) printk(KERN_WARNING "swap_duplicate: entry %08lx map count=%d\n", entry, p->swap_map[offset]); p->swap_map[offset] = SWAP_MAP_MAX; } result = 1; #ifdef DEBUG_SWAP printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n", entry, p->swap_map[offset]); #endif out: return result; bad_file: printk(KERN_ERR "swap_duplicate: entry %08lx, nonexistent swap file\n", entry); goto out; bad_offset: printk(KERN_ERR "swap_duplicate: entry %08lx, offset exceeds max\n", entry); goto out; bad_unused: printk(KERN_ERR "swap_duplicate at %8p: entry %08lx, unused page\n", __builtin_return_address(0), entry); goto out; }
/* * remove the attach descriptor shmd. * free memory for segment if it is marked destroyed. * The descriptor has already been removed from the current->mm->mmap list * and will later be kfree()d. */ static void shm_close (struct vm_area_struct *shmd) { struct shmid_kernel *shp; int id; /* remove from the list of attaches of the shm segment */ id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK; shp = shm_segs[id]; remove_attach(shp,shmd); /* remove from shp->attaches */ shp->u.shm_lpid = current->pid; shp->u.shm_dtime = CURRENT_TIME; if (--shp->u.shm_nattch <= 0 && shp->u.shm_perm.mode & SHM_DEST) killseg (id); }
int swap_count(unsigned long entry) { struct swap_info_struct * p; unsigned long offset, type; int retval = 0; if (!entry) goto bad_entry; type = SWP_TYPE(entry); if (type & SHM_SWP_TYPE) goto out; if (type >= nr_swapfiles) goto bad_file; p = type + swap_info; offset = SWP_OFFSET(entry); if (offset >= p->max) goto bad_offset; if (!p->swap_map[offset]) goto bad_unused; retval = p->swap_map[offset]; #ifdef DEBUG_SWAP printk("DebugVM: swap_count(entry %08lx, count %d)\n", entry, retval); #endif out: return retval; bad_entry: printk(KERN_ERR "swap_count: null entry!\n"); goto out; bad_file: printk(KERN_ERR "swap_count: entry %08lx, nonexistent swap file!\n", entry); goto out; bad_offset: printk(KERN_ERR "swap_count: entry %08lx, offset exceeds max!\n", entry); goto out; bad_unused: printk(KERN_ERR "swap_count at %8p: entry %08lx, unused page!\n", __builtin_return_address(0), entry); goto out; }
/* This is called by fork, once for every shm attach. */ static void shm_open (struct vm_area_struct *shmd) { unsigned int id; struct shmid_kernel *shp; id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK; shp = shm_segs[id]; if (shp == IPC_UNUSED) { printk("shm_open: unused id=%d PANIC\n", id); return; } if (!++shp->u.shm_nattch) { shp->u.shm_nattch--; return; /* XXX: should be able to report failure */ } insert_attach(shp,shmd); /* insert shmd into shp->attaches */ shp->u.shm_atime = CURRENT_TIME; shp->u.shm_lpid = current->pid; }
/* This is run when asynchronous page I/O has completed. */ void swap_after_unlock_page (unsigned long entry) { unsigned long type, offset; struct swap_info_struct * p; type = SWP_TYPE(entry); if (type >= nr_swapfiles) { printk("swap_after_unlock_page: bad swap-device\n"); return; } p = &swap_info[type]; offset = SWP_OFFSET(entry); if (offset >= p->max) { printk("swap_after_unlock_page: weirdness\n"); return; } if (!clear_bit(offset,p->swap_lockmap)) printk("swap_after_unlock_page: lock already cleared\n"); wake_up(&lock_queue); }
/* * Check if we're the only user of a swap page, * when the page is locked. */ static int exclusive_swap_page(struct page *page) { int retval = 0; struct swap_info_struct * p; swp_entry_t entry; entry.val = page->index; p = swap_info_get(entry); if (p) { /* Is the only swap cache user the cache itself? */ if (p->swap_map[SWP_OFFSET(entry)] == 1) { /* Recheck the page count with the pagecache lock held.. */ spin_lock(&pagecache_lock); if (page_count(page) - !!page->buffers == 2) retval = 1; spin_unlock(&pagecache_lock); } swap_info_put(p); } return retval; }
/* * Work out if there are any other processes sharing this * swap cache page. Free it if you can. Return success. */ int remove_exclusive_swap_page(struct page *page) { int retval; struct swap_info_struct * p; swp_entry_t entry; if (!PageLocked(page)) BUG(); if (!PageSwapCache(page)) return 0; if (page_count(page) - !!page->buffers != 2) /* 2: us + cache */ return 0; entry.val = page->index; p = swap_info_get(entry); if (!p) return 0; /* Is the only swap cache user the cache itself? */ retval = 0; if (p->swap_map[SWP_OFFSET(entry)] == 1) { /* Recheck the page count with the pagecache lock held.. */ spin_lock(&pagecache_lock); if (page_count(page) - !!page->buffers == 2) { __delete_from_swap_cache(page); SetPageDirty(page); retval = 1; } spin_unlock(&pagecache_lock); } swap_info_put(p); if (retval) { block_flushpage(page, 0); swap_free(entry); page_cache_release(page); } return retval; }
/* * Free the swap entry like above, but also try to * free the page cache entry if it is the last user. */ void free_swap_and_cache(swp_entry_t entry) { struct swap_info_struct * p; struct page *page = NULL; p = swap_info_get(entry); if (p) { if (swap_entry_free(p, SWP_OFFSET(entry)) == 1) page = find_trylock_page(&swapper_space, entry.val); swap_info_put(p); } if (page) { page_cache_get(page); /* Only cache user (+us), or swap space full? Free it! */ if (page_count(page) - !!page->buffers == 2 || vm_swap_full()) { delete_from_swap_cache(page); SetPageDirty(page); } UnlockPage(page); page_cache_release(page); } }
static struct swap_info_struct * swap_info_get(swp_entry_t entry) { struct swap_info_struct * p; unsigned long offset, type; if (!entry.val) goto out; type = SWP_TYPE(entry); if (type >= nr_swapfiles) goto bad_nofile; p = & swap_info[type]; if (!(p->flags & SWP_USED)) goto bad_device; offset = SWP_OFFSET(entry); if (offset >= p->max) goto bad_offset; if (!p->swap_map[offset]) goto bad_free; swap_list_lock(); if (p->prio > swap_info[swap_list.next].prio) swap_list.next = type; swap_device_lock(p); return p; bad_free: printk(KERN_ERR "swap_free: %s%08lx\n", Unused_offset, entry.val); goto out; bad_offset: printk(KERN_ERR "swap_free: %s%08lx\n", Bad_offset, entry.val); goto out; bad_device: printk(KERN_ERR "swap_free: %s%08lx\n", Unused_file, entry.val); goto out; bad_nofile: printk(KERN_ERR "swap_free: %s%08lx\n", Bad_file, entry.val); out: return NULL; }
void swap_free(unsigned long entry) { struct swap_info_struct * p; unsigned long offset, type; if (!entry) return; type = SWP_TYPE(entry); if (type & SHM_SWP_TYPE) return; if (type >= nr_swapfiles) { printk("Trying to free nonexistent swap-page\n"); return; } p = & swap_info[type]; offset = SWP_OFFSET(entry); if (offset >= p->max) { printk("swap_free: weirdness\n"); return; } if (!(p->flags & SWP_USED)) { printk("Trying to free swap from unused swap-device\n"); return; } if (offset < p->lowest_bit) p->lowest_bit = offset; if (offset > p->highest_bit) p->highest_bit = offset; if (!p->swap_map[offset]) printk("swap_free: swap-space map null (entry %08lx)\n",entry); else if (p->swap_map[offset] == SWAP_MAP_RESERVED) printk("swap_free: swap-space reserved (entry %08lx)\n",entry); else if (!--p->swap_map[offset]) nr_swap_pages++; if (p->prio > swap_info[swap_list.next].prio) { swap_list.next = swap_list.head; } }
void osfmach3_insert_vm_struct( struct mm_struct *mm, struct vm_area_struct *vmp) { memory_object_t mem_obj; vm_offset_t mem_obj_offset; kern_return_t kr; unsigned short vm_flags; boolean_t is_shared; vm_prot_t cur_prot, max_prot; vm_address_t user_addr, wanted_addr; vm_size_t size; unsigned int id; struct shmid_ds *shp; struct osfmach3_mach_task_struct *mach_task; extern struct shmid_ds *shm_segs[SHMMNI]; if (vmp->vm_flags & VM_REMAPPING) { /* don't mess with Mach VM: it's only Linux remappings */ return; } #ifdef VMA_DEBUG if (vma_debug) { printk("VMA:osfmach3_insert_vm_struct: mm=0x%p, vmp=0x%p\n", mm, vmp); } #endif /* VMA_DEBUG */ mach_task = mm->mm_mach_task; if (vmp->vm_inode == NULL) { if (vmp->vm_pte != 0) { /* shared memory */ id = SWP_OFFSET(vmp->vm_pte) & SHM_ID_MASK; shp = shm_segs[id]; if (shp != IPC_UNUSED) { mem_obj = (mach_port_t) shp->shm_pages; mem_obj_offset = 0; } else { mem_obj = MEMORY_OBJECT_NULL; mem_obj_offset = 0; } } else { mem_obj = MEMORY_OBJECT_NULL; mem_obj_offset = 0; } } else if (S_ISREG(vmp->vm_inode->i_mode)) { mem_obj = inode_pager_setup(vmp->vm_inode); if (mem_obj == MEMORY_OBJECT_NULL) { panic("osfmach3_insert_vm_struct: can't setup pager"); } mem_obj_offset = (vm_offset_t) vmp->vm_offset; } else if (vmp->vm_inode->i_mem_object != NULL) { /* special file, but with a pager already setup */ mem_obj = vmp->vm_inode->i_mem_object->imo_mem_obj; mem_obj_offset = (vm_offset_t) vmp->vm_offset; } else { panic("osfmach3_insert_vm_struct: non-regular file"); } vm_flags = vmp->vm_flags; cur_prot = VM_PROT_NONE; if (vm_flags & VM_READ) cur_prot |= VM_PROT_READ; if (vm_flags & VM_WRITE) cur_prot |= VM_PROT_WRITE; if (vm_flags & VM_EXEC) cur_prot |= VM_PROT_EXECUTE; max_prot = VM_PROT_ALL; is_shared = (vmp->vm_flags & VM_SHARED) != 0; user_addr = vmp->vm_start; wanted_addr = user_addr; size = vmp->vm_end - vmp->vm_start; #ifdef VMA_DEBUG if (vma_debug) { printk("VMA: vm_map(task=0x%x, user_addr=0x%x, size=0x%x, " "mem_obj=0x%x, offset=0x%x, %sCOPY, cur_prot=0x%x, " "max_prot=0x%x, %s)\n", mach_task->mach_task_port, user_addr, size, mem_obj, mem_obj_offset, is_shared ? "!" : "", cur_prot, max_prot, is_shared ? "INHERIT_SHARE" : "INHERIT_COPY"); } #endif /* VMA_DEBUG */ server_thread_blocking(FALSE); kr = vm_map(mach_task->mach_task_port, &user_addr, size, 0, /* no mask */ FALSE, /* not anywhere */ mem_obj, mem_obj_offset, !is_shared, cur_prot, max_prot, is_shared ? VM_INHERIT_SHARE : VM_INHERIT_COPY); server_thread_unblocking(FALSE); if (kr != KERN_SUCCESS) { printk("Failure: vm_map(task=0x%x, user_addr=0x%x, size=0x%x, " "mem_obj=0x%x, offset=0x%x, %sCOPY, cur_prot=0x%x, " "max_prot=0x%x, %s)\n", mach_task->mach_task_port, user_addr, size, mem_obj, mem_obj_offset, is_shared ? "!" : "", cur_prot, max_prot, is_shared ? "INHERIT_SHARE" : "INHERIT_COPY"); MACH3_DEBUG(1, kr, ("osfmach3_insert_vm_struct: vm_map")); printk("osfmach3_insert_vm_struct: can't map\n"); } if (user_addr != wanted_addr) { printk("vm_map: mapped at 0x%x instead of 0x%x\n", user_addr, wanted_addr); printk("osfmach3_insert_vm_struct: mapping at wrong address\n"); } if (vmp->vm_flags & VM_LOCKED) { extern mach_port_t privileged_host_port; server_thread_blocking(FALSE); kr = vm_wire(privileged_host_port, mach_task->mach_task_port, user_addr, size, cur_prot); server_thread_unblocking(FALSE); if (kr != KERN_SUCCESS) { MACH3_DEBUG(2, kr, ("osfmach3_insert_vm_struct: " "vm_wire(task=0x%x, addr=0x%x, size=0x%x, " "prot=0x%x)", mach_task->mach_task_port, user_addr, size, cur_prot)); printk("osfmach3_insert_vm_struct: vm_wire failed\n"); } } #if 0 if (vmp->vm_inode != NULL) { /* * If mem_obj was already cached in the kernel, we got an * extra reference on its i_mem_object structure (inode_pager). * If it was the first time we mapped the inode, the memory * object has just been initialized by the kernel and we * got a reference in memory_object_init(). In both cases, * we have to release a reference. */ ASSERT(mem_obj != MEMORY_OBJECT_NULL); ASSERT(vmp->vm_inode->i_mem_object); ASSERT(vmp->vm_inode->i_mem_object->imo_mem_obj_control); inode_pager_release(vmp->vm_inode); } #endif }
/* * Reads or writes a swap page. * wait=1: start I/O and wait for completion. wait=0: start asynchronous I/O. * * Important prevention of race condition: The first thing we do is set a lock * on this swap page, which lasts until I/O completes. This way a * write_swap_page(entry) immediately followed by a read_swap_page(entry) * on the same entry will first complete the write_swap_page(). Fortunately, * not more than one write_swap_page() request can be pending per entry. So * all races the caller must catch are: multiple read_swap_page() requests * on the same entry. */ void rw_swap_page(int rw, unsigned long entry, char * buf, int wait) { unsigned long type, offset; struct swap_info_struct * p; struct page *page; type = SWP_TYPE(entry); if (type >= nr_swapfiles) { printk("Internal error: bad swap-device\n"); return; } p = &swap_info[type]; offset = SWP_OFFSET(entry); if (offset >= p->max) { printk("rw_swap_page: weirdness\n"); return; } if (p->swap_map && !p->swap_map[offset]) { printk("Hmm.. Trying to use unallocated swap (%08lx)\n", entry); return; } if (!(p->flags & SWP_USED)) { printk("Trying to swap to unused swap-device\n"); return; } /* Make sure we are the only process doing I/O with this swap page. */ while (set_bit(offset,p->swap_lockmap)) { run_task_queue(&tq_disk); sleep_on(&lock_queue); } if (rw == READ) kstat.pswpin++; else kstat.pswpout++; page = mem_map + MAP_NR(buf); atomic_inc(&page->count); wait_on_page(page); if (p->swap_device) { if (!wait) { set_bit(PG_free_after, &page->flags); set_bit(PG_decr_after, &page->flags); set_bit(PG_swap_unlock_after, &page->flags); page->swap_unlock_entry = entry; atomic_inc(&nr_async_pages); } ll_rw_page(rw,p->swap_device,offset,buf); /* * NOTE! We don't decrement the page count if we * don't wait - that will happen asynchronously * when the IO completes. */ if (!wait) return; wait_on_page(page); } else if (p->swap_file) { struct inode *swapf = p->swap_file; unsigned int zones[PAGE_SIZE/512]; int i; if (swapf->i_op->bmap == NULL && swapf->i_op->smap != NULL){ /* With MsDOS, we use msdos_smap which return a sector number (not a cluster or block number). It is a patch to enable the UMSDOS project. Other people are working on better solution. It sounds like ll_rw_swap_file defined it operation size (sector size) based on PAGE_SIZE and the number of block to read. So using bmap or smap should work even if smap will require more blocks. */ int j; unsigned int block = offset << 3; for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){ if (!(zones[i] = swapf->i_op->smap(swapf,block++))) { printk("rw_swap_page: bad swap file\n"); return; } } }else{ int j; unsigned int block = offset << (PAGE_SHIFT - swapf->i_sb->s_blocksize_bits); for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize) if (!(zones[i] = bmap(swapf,block++))) { printk("rw_swap_page: bad swap file\n"); } } ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf); } else printk("rw_swap_page: no swap file or device\n"); atomic_dec(&page->count); if (offset && !clear_bit(offset,p->swap_lockmap)) printk("rw_swap_page: lock already cleared\n"); wake_up(&lock_queue); }
static void rw_swap_page_base(int rw, unsigned long entry, struct page *page, int wait) { unsigned long type, offset; struct swap_info_struct * p; int zones[PAGE_SIZE/512]; int zones_used; kdev_t dev = 0; int block_size; #ifdef DEBUG_SWAP printk ("DebugVM: %s_swap_page entry %08lx, page %p (count %d), %s\n", (rw == READ) ? "read" : "write", entry, (char *) page_address(page), atomic_read(&page->count), wait ? "wait" : "nowait"); #endif type = SWP_TYPE(entry); if (type >= nr_swapfiles) { printk("Internal error: bad swap-device\n"); return; } /* Don't allow too many pending pages in flight.. */ if (atomic_read(&nr_async_pages) > pager_daemon.swap_cluster) wait = 1; p = &swap_info[type]; offset = SWP_OFFSET(entry); if (offset >= p->max) { printk("rw_swap_page: weirdness\n"); return; } if (p->swap_map && !p->swap_map[offset]) { printk(KERN_ERR "rw_swap_page: " "Trying to %s unallocated swap (%08lx)\n", (rw == READ) ? "read" : "write", entry); return; } if (!(p->flags & SWP_USED)) { printk(KERN_ERR "rw_swap_page: " "Trying to swap to unused swap-device\n"); return; } if (!PageLocked(page)) { printk(KERN_ERR "VM: swap page is unlocked\n"); return; } if (PageSwapCache(page)) { /* Make sure we are the only process doing I/O with this swap page. */ if (test_and_set_bit(offset, p->swap_lockmap)) { struct wait_queue __wait; __wait.task = current; add_wait_queue(&lock_queue, &__wait); for (;;) { current->state = TASK_UNINTERRUPTIBLE; mb(); if (!test_and_set_bit(offset, p->swap_lockmap)) break; run_task_queue(&tq_disk); schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&lock_queue, &__wait); } /* * Make sure that we have a swap cache association for this * page. We need this to find which swap page to unlock once * the swap IO has completed to the physical page. If the page * is not already in the cache, just overload the offset entry * as if it were: we are not allowed to manipulate the inode * hashing for locked pages. */ if (page->offset != entry) { printk ("swap entry mismatch"); return; } } if (rw == READ) { clear_bit(PG_uptodate, &page->flags); kstat.pswpin++; } else kstat.pswpout++; atomic_inc(&page->count); if (p->swap_device) { zones[0] = offset; zones_used = 1; dev = p->swap_device; block_size = PAGE_SIZE; } else if (p->swap_file) { struct inode *swapf = p->swap_file->d_inode; int i; if (swapf->i_op->bmap == NULL && swapf->i_op->smap != NULL){ /* With MS-DOS, we use msdos_smap which returns a sector number (not a cluster or block number). It is a patch to enable the UMSDOS project. Other people are working on better solution. It sounds like ll_rw_swap_file defined its operation size (sector size) based on PAGE_SIZE and the number of blocks to read. So using bmap or smap should work even if smap will require more blocks. */ int j; unsigned int block = offset << 3; for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){ if (!(zones[i] = swapf->i_op->smap(swapf,block++))) { printk("rw_swap_page: bad swap file\n"); return; } } block_size = 512; }else{ int j; unsigned int block = offset << (PAGE_SHIFT - swapf->i_sb->s_blocksize_bits); block_size = swapf->i_sb->s_blocksize; for (i=0, j=0; j< PAGE_SIZE ; i++, j += block_size) if (!(zones[i] = bmap(swapf,block++))) { printk("rw_swap_page: bad swap file\n"); return; } zones_used = i; dev = swapf->i_dev; } } else { printk(KERN_ERR "rw_swap_page: no swap file or device\n"); /* Do some cleaning up so if this ever happens we can hopefully * trigger controlled shutdown. */ if (PageSwapCache(page)) { if (!test_and_clear_bit(offset,p->swap_lockmap)) printk("swap_after_unlock_page: lock already cleared\n"); wake_up(&lock_queue); } atomic_dec(&page->count); return; } if (!wait) { set_bit(PG_decr_after, &page->flags); atomic_inc(&nr_async_pages); } if (PageSwapCache(page)) { /* only lock/unlock swap cache pages! */ set_bit(PG_swap_unlock_after, &page->flags); } set_bit(PG_free_after, &page->flags); /* block_size == PAGE_SIZE/zones_used */ brw_page(rw, page, dev, zones, block_size, 0); /* Note! For consistency we do all of the logic, * decrementing the page count, and unlocking the page in the * swap lock map - in the IO completion handler. */ if (!wait) return; wait_on_page(page); /* This shouldn't happen, but check to be sure. */ if (atomic_read(&page->count) == 0) printk(KERN_ERR "rw_swap_page: page unused while waiting!\n"); #ifdef DEBUG_SWAP printk ("DebugVM: %s_swap_page finished on page %p (count %d)\n", (rw == READ) ? "read" : "write", (char *) page_adddress(page), atomic_read(&page->count)); #endif }