Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
/*
 * 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);
}
Ejemplo n.º 3
0
/*
 * 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);
	}
}
Ejemplo n.º 4
0
/*
 * 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;
}
Ejemplo n.º 5
0
/*
 * 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);
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
/* 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;
}
Ejemplo n.º 8
0
/* 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);
}
Ejemplo n.º 9
0
/*
 * 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;
}
Ejemplo n.º 10
0
/*
 * 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;
}
Ejemplo n.º 11
0
/*
 * 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);
	}
}
Ejemplo n.º 12
0
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;
}	
Ejemplo n.º 13
0
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;
	}
}
Ejemplo n.º 14
0
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
}
Ejemplo n.º 15
0
/*
 * 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);
}
Ejemplo n.º 16
0
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
}