From 5079f36d6485b14a3bf881c6693fa54316cd44ae Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:09:35 -0500 Subject: [PATCH] Import 1.1.33 --- Makefile | 2 +- drivers/block/cdu31a.c | 3 + drivers/block/hd.c | 12 +-- drivers/block/mcd.c | 3 + drivers/block/sbpcd.c | 3 + drivers/char/console.c | 36 +++++++- drivers/char/tty_io.c | 7 +- drivers/scsi/sr.c | 3 + fs/exec.c | 2 + fs/nfs/mmap.c | 106 +++++++++++----------- include/linux/mm.h | 12 ++- include/linux/unistd.h | 1 + ipc/shm.c | 42 +++++---- kernel/exit.c | 2 + kernel/ksyms.c | 5 ++ kernel/sched.c | 5 +- kernel/sys_call.S | 3 +- mm/memory.c | 66 +++++++------- mm/mmap.c | 4 +- mm/swap.c | 196 +++++++++++++++++++++++++++++++++-------- net/inet/arp.c | 10 +-- 21 files changed, 363 insertions(+), 160 deletions(-) diff --git a/Makefile b/Makefile index 59052c4805dc..3a8b3c26c066 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 1 -SUBLEVEL = 32 +SUBLEVEL = 33 all: Version zImage diff --git a/drivers/block/cdu31a.c b/drivers/block/cdu31a.c index 55f248b39249..724d976075d9 100644 --- a/drivers/block/cdu31a.c +++ b/drivers/block/cdu31a.c @@ -1772,6 +1772,9 @@ scd_open(struct inode *inode, int num_spin_ups; + if (filp->f_mode & 2) + return -EACCES; + if (!sony_spun_up) { num_spin_ups = 0; diff --git a/drivers/block/hd.c b/drivers/block/hd.c index 3e93f79e5bb2..d241be7b25fb 100644 --- a/drivers/block/hd.c +++ b/drivers/block/hd.c @@ -218,12 +218,12 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, } static void hd_request (void); -unsigned int identified [MAX_HD] = {0,}; /* 1 = drive ID already displayed */ -unsigned int unmask_intr [MAX_HD] = {0,}; /* 1 = unmask IRQs during I/O */ -unsigned int max_mult [MAX_HD] = {0,}; /* max sectors for MultMode */ -unsigned int mult_req [MAX_HD] = {0,}; /* requested MultMode count */ -unsigned int mult_count [MAX_HD] = {0,}; /* currently enabled MultMode count */ -struct request WCURRENT; +static unsigned int identified [MAX_HD] = {0,}; /* 1 = drive ID already displayed */ +static unsigned int unmask_intr [MAX_HD] = {0,}; /* 1 = unmask IRQs during I/O */ +static unsigned int max_mult [MAX_HD] = {0,}; /* max sectors for MultMode */ +static unsigned int mult_req [MAX_HD] = {0,}; /* requested MultMode count */ +static unsigned int mult_count [MAX_HD] = {0,}; /* currently enabled MultMode count */ +static struct request WCURRENT; static void rawstring (char *prefix, char *s, int n) { diff --git a/drivers/block/mcd.c b/drivers/block/mcd.c index c63e54ac78fd..7b876202f993 100644 --- a/drivers/block/mcd.c +++ b/drivers/block/mcd.c @@ -1029,6 +1029,9 @@ mcd_open(struct inode *ip, struct file *fp) if (mcdPresent == 0) return -ENXIO; /* no hardware */ + + if (fp->f_mode & 2) /* write access? */ + return -EACCES; if (!mcd_open_count && mcd_state == MCD_S_IDLE) { diff --git a/drivers/block/sbpcd.c b/drivers/block/sbpcd.c index e72d9f4bbcab..cbf4ea8af800 100644 --- a/drivers/block/sbpcd.c +++ b/drivers/block/sbpcd.c @@ -3211,6 +3211,9 @@ static int sbpcd_open(struct inode *ip, struct file *fp) if (ndrives==0) return (-ENXIO); /* no hardware */ + if (fp->f_mode & 2) + return -EACCES; + i = MINOR(ip->i_rdev); if ( (i<0) || (i>=NR_SBPCD) ) { diff --git a/drivers/char/console.c b/drivers/char/console.c index 7ffafdc75d7f..aa306a7e81d5 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -35,6 +35,9 @@ * Code to check for different video-cards mostly by Galen Hunt, * * + * Code for xterm like mouse click reporting by Peter Orbaek 20-Jul-94 + * + * */ #define CAN_LOAD_EGA_FONTS /* undefine if the user must not do this */ @@ -80,7 +83,7 @@ static struct termios *console_termios_locked[NR_CONSOLES]; #include /* Routines for selection control. */ -int set_selection(const int arg); +int set_selection(const int arg, struct tty_struct *tty); int paste_selection(struct tty_struct *tty); static void clear_selection(void); static void highlight_pointer(const int currcons, const int where); @@ -153,6 +156,7 @@ static struct { /* misc */ unsigned long vc_ques : 1; unsigned long vc_need_wrap : 1; + unsigned long vc_report_mouse : 1; unsigned long vc_tab_stop[5]; /* Tab stops. 160 columns. */ unsigned char * vc_translate; unsigned char * vc_G0_charset; @@ -196,6 +200,7 @@ static int console_blanked = 0; #define deccm (vc_cons[currcons].vc_deccm) #define decim (vc_cons[currcons].vc_decim) #define need_wrap (vc_cons[currcons].vc_need_wrap) +#define report_mouse (vc_cons[currcons].vc_report_mouse) #define color (vc_cons[currcons].vc_color) #define s_color (vc_cons[currcons].vc_s_color) #define def_color (vc_cons[currcons].vc_def_color) @@ -775,6 +780,16 @@ static void cursor_report(int currcons, struct tty_struct * tty) respond_string(buf, tty); } +static void mouse_report(int currcons, struct tty_struct * tty, + int butt, int mrx, int mry) +{ + char buf[8]; + + sprintf(buf, "\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx), + (char)('!' + mry)); + respond_string(buf, tty); +} + static inline void status_report(int currcons, struct tty_struct * tty) { respond_string("\033[0n", tty); /* Terminal ok */ @@ -832,6 +847,9 @@ static void set_mode(int currcons, int on_off) else clr_kbd(decarm); break; + case 9: + report_mouse = on_off; + break; case 25: /* Cursor on/off */ deccm = on_off; set_cursor(currcons); @@ -1001,6 +1019,7 @@ static void reset_terminal(int currcons, int do_clear) G1_charset = GRAF_TRANS; charset = 0; need_wrap = 0; + report_mouse = 0; disp_ctrl = 0; toggle_meta = 0; @@ -1838,8 +1857,16 @@ static inline short limit(const int v, const int l, const int u) return (v < l) ? l : ((v > u) ? u : v); } +/* invoked via ioctl(TIOCLINUX) */ +int mouse_reporting_p(void) +{ + int currcons = fg_console; + + return ((report_mouse) ? 0 : -EINVAL); +} + /* set the current selection. Invoked by ioctl(). */ -int set_selection(const int arg) +int set_selection(const int arg, struct tty_struct *tty) { unsigned short *args, xs, ys, xe, ye; int currcons = fg_console; @@ -1863,6 +1890,11 @@ int set_selection(const int arg) ps = ys * video_size_row + (xs << 1); pe = ye * video_size_row + (xe << 1); + if (report_mouse && (sel_mode & 16)) { + mouse_report(currcons, tty, sel_mode & 15, xs, ys); + return 0; + } + if (ps > pe) /* make sel_start <= sel_end */ { int tmp = ps; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 29ba87cb4929..82b0d20d0b34 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -66,9 +66,10 @@ #undef TTY_DEBUG_HANGUP #ifdef CONFIG_SELECTION -extern int set_selection(const int arg); +extern int set_selection(const int arg, struct tty_struct *tty); extern int paste_selection(struct tty_struct *tty); extern int sel_loadlut(const int arg); +extern int mouse_reporting_p(void); extern int shift_state; #endif /* CONFIG_SELECTION */ extern int do_screendump(int arg); @@ -1381,7 +1382,7 @@ static int tty_ioctl(struct inode * inode, struct file * file, return do_get_ps_info(arg); #ifdef CONFIG_SELECTION case 2: - return set_selection(arg); + return set_selection(arg, tty); case 3: return paste_selection(tty); case 4: @@ -1392,6 +1393,8 @@ static int tty_ioctl(struct inode * inode, struct file * file, case 6: put_fs_byte(shift_state,arg); return 0; + case 7: + return mouse_reporting_p(); #endif /* CONFIG_SELECTION */ default: return -EINVAL; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 8ff304a86c0e..b49771beea16 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -268,6 +268,9 @@ static int sr_open(struct inode * inode, struct file * filp) if(MINOR(inode->i_rdev) >= NR_SR || !scsi_CDs[MINOR(inode->i_rdev)].device) return -ENXIO; /* No such device */ + if (filp->f_mode & 2) + return -EACCES; + check_disk_change(inode->i_rdev); if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++) diff --git a/fs/exec.c b/fs/exec.c index 4971b5df8046..576e9dd64d36 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -537,6 +537,8 @@ void flush_old_exec(struct linux_binprm * bprm) mpnt1 = mpnt->vm_next; if (mpnt->vm_ops && mpnt->vm_ops->close) mpnt->vm_ops->close(mpnt); + if (mpnt->vm_inode) + iput(mpnt->vm_inode); kfree(mpnt); mpnt = mpnt1; } diff --git a/fs/nfs/mmap.c b/fs/nfs/mmap.c index a13778ed6a2c..a28aebf1b8b2 100644 --- a/fs/nfs/mmap.c +++ b/fs/nfs/mmap.c @@ -23,63 +23,11 @@ #include #include +/* + * Fill in the supplied page for mmap + */ static unsigned long nfs_file_mmap_nopage(struct vm_area_struct * area, - unsigned long address, unsigned long page, int error_code); - -extern void file_mmap_free(struct vm_area_struct * area); -extern int file_mmap_share(struct vm_area_struct * from, struct vm_area_struct * to, - unsigned long address); - -struct vm_operations_struct nfs_file_mmap = { - NULL, /* open */ - file_mmap_free, /* close */ - nfs_file_mmap_nopage, /* nopage */ - NULL, /* wppage */ - file_mmap_share, /* share */ - NULL, /* unmap */ -}; - - -/* This is used for a general mmap of a nfs file */ -int nfs_mmap(struct inode * inode, struct file * file, - unsigned long addr, size_t len, int prot, unsigned long off) -{ - struct vm_area_struct * mpnt; - - if (prot & PAGE_RW) /* only PAGE_COW or read-only supported now */ - return -EINVAL; - if (off & (inode->i_sb->s_blocksize - 1)) - return -EINVAL; - if (!inode->i_sb || !S_ISREG(inode->i_mode)) - return -EACCES; - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - - mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!mpnt) - return -ENOMEM; - - unmap_page_range(addr, len); - mpnt->vm_task = current; - mpnt->vm_start = addr; - mpnt->vm_end = addr + len; - mpnt->vm_page_prot = prot; - mpnt->vm_flags = 0; - mpnt->vm_share = NULL; - mpnt->vm_inode = inode; - inode->i_count++; - mpnt->vm_offset = off; - mpnt->vm_ops = &nfs_file_mmap; - insert_vm_struct(current, mpnt); - merge_segments(current->mm->mmap, NULL, NULL); - return 0; -} - - -static unsigned long nfs_file_mmap_nopage(struct vm_area_struct * area, unsigned long address, - unsigned long page, int error_code) + unsigned long address, unsigned long page, int error_code) { struct inode * inode = area->vm_inode; unsigned int clear; @@ -126,3 +74,49 @@ static unsigned long nfs_file_mmap_nopage(struct vm_area_struct * area, unsigned } return page; } +struct vm_operations_struct nfs_file_mmap = { + NULL, /* open */ + NULL, /* close */ + nfs_file_mmap_nopage, /* nopage */ + NULL, /* wppage */ + NULL, /* share */ + NULL, /* unmap */ +}; + + +/* This is used for a general mmap of a nfs file */ +int nfs_mmap(struct inode * inode, struct file * file, + unsigned long addr, size_t len, int prot, unsigned long off) +{ + struct vm_area_struct * mpnt; + + if (prot & PAGE_RW) /* only PAGE_COW or read-only supported now */ + return -EINVAL; + if (off & (inode->i_sb->s_blocksize - 1)) + return -EINVAL; + if (!inode->i_sb || !S_ISREG(inode->i_mode)) + return -EACCES; + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } + + mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); + if (!mpnt) + return -ENOMEM; + + unmap_page_range(addr, len); + mpnt->vm_task = current; + mpnt->vm_start = addr; + mpnt->vm_end = addr + len; + mpnt->vm_page_prot = prot; + mpnt->vm_flags = 0; + mpnt->vm_share = NULL; + mpnt->vm_inode = inode; + inode->i_count++; + mpnt->vm_offset = off; + mpnt->vm_ops = &nfs_file_mmap; + insert_vm_struct(current, mpnt); + merge_segments(current->mm->mmap, NULL, NULL); + return 0; +} diff --git a/include/linux/mm.h b/include/linux/mm.h index 719481e72fc8..13071c31bc63 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -61,6 +61,8 @@ struct vm_operations_struct { unsigned long page); int (*share)(struct vm_area_struct * from, struct vm_area_struct * to, unsigned long address); int (*unmap)(struct vm_area_struct *area, unsigned long, size_t); + void (*swapout)(struct vm_area_struct *, unsigned long *); + unsigned long (*swapin)(struct vm_area_struct *, unsigned long); }; extern unsigned long __bad_page(void); @@ -167,7 +169,7 @@ extern int vread(char *buf, char *addr, int count); extern void swap_free(unsigned long page_nr); extern unsigned long swap_duplicate(unsigned long page_nr); -extern void swap_in(unsigned long *table_ptr); +extern unsigned long swap_in(unsigned long entry); extern void si_swapinfo(struct sysinfo * val); extern void rw_swap_page(int rw, unsigned long nr, char * buf); @@ -223,4 +225,12 @@ extern unsigned short * mem_map; #define SHM_SWP_TYPE 0x41 extern void shm_no_page (ulong *); +/* swap cache stuff (in swap.c) */ +extern unsigned long * swap_cache; + +extern inline unsigned long in_swap_cache (unsigned long addr) +{ + return swap_cache[addr >> PAGE_SHIFT]; +} + #endif diff --git a/include/linux/unistd.h b/include/linux/unistd.h index 8e1085cd71d9..c17a1aed54db 100644 --- a/include/linux/unistd.h +++ b/include/linux/unistd.h @@ -143,6 +143,7 @@ #define __NR_bdflush 134 #define __NR_sysfs 135 #define __NR_personality 136 +#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ extern int errno; diff --git a/ipc/shm.c b/ipc/shm.c index 2c686d330e93..af2cbbd43835 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -19,6 +19,7 @@ static int findkey (key_t key); static int newseg (key_t key, int shmflg, int size); static int shm_map (struct shm_desc *shmd, int remap); static void killseg (int id); +static unsigned long shm_swap_in(struct vm_area_struct *, unsigned long); static int shm_tot = 0; /* total number of shared memory pages */ static int shm_rss = 0; /* number of shared memory pages that are in memory */ @@ -375,13 +376,24 @@ static int shm_map (struct shm_desc *shmd, int remap) return 0; } +static struct vm_operations_struct shm_vm_ops = { + NULL, /* open */ + NULL, /* close */ + NULL, /* nopage (done with swapin) */ + NULL, /* wppage */ + NULL, /* share */ + NULL, /* unmap */ + NULL, /* swapout (hardcoded right now) */ + shm_swap_in /* swapin */ +}; + /* * This is really minimal support to make the shared mem stuff * ve known by the general VM manager. It should add the vm_ops * field so that 'munmap()' and friends work correctly on shared * memory areas.. */ -static int add_vm_area(unsigned long addr, unsigned long len) +static int add_vm_area(unsigned long addr, unsigned long len, int readonly) { struct vm_area_struct * vma; @@ -392,12 +404,15 @@ static int add_vm_area(unsigned long addr, unsigned long len) vma->vm_task = current; vma->vm_start = addr; vma->vm_end = addr + len; - vma->vm_page_prot = PAGE_SHARED; + if (readonly) + vma->vm_page_prot = PAGE_READONLY; + else + vma->vm_page_prot = PAGE_SHARED; vma->vm_flags = VM_SHM; vma->vm_share = NULL; vma->vm_inode = NULL; vma->vm_offset = 0; - vma->vm_ops = NULL; + vma->vm_ops = &shm_vm_ops; insert_vm_struct(current, vma); merge_segments(current->mm->mmap, NULL, NULL); return 0; @@ -476,7 +491,7 @@ int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) shmd->end = addr + shp->shm_npages * PAGE_SIZE; shmd->task = current; - if ((err = add_vm_area(shmd->start, shmd->end - shmd->start))) { + if ((err = add_vm_area(shmd->start, shmd->end - shmd->start, shmflg & SHM_RDONLY))) { kfree(shmd); return err; } @@ -614,36 +629,34 @@ int shm_fork (struct task_struct *p1, struct task_struct *p2) } /* - * page not present ... go through shm_pages .. called from swap_in() + * page not present ... go through shm_pages */ -void shm_no_page (unsigned long *ptent) +static unsigned long shm_swap_in(struct vm_area_struct * vma, unsigned long code) { unsigned long page; - unsigned long code = *ptent; struct shmid_ds *shp; unsigned int id, idx; id = (code >> SHM_ID_SHIFT) & SHM_ID_MASK; if (id > max_shmid) { printk ("shm_no_page: id=%d too big. proc mem corruptedn", id); - return; + return BAD_PAGE | PAGE_SHARED; } shp = shm_segs[id]; if (shp == IPC_UNUSED || shp == IPC_NOID) { printk ("shm_no_page: id=%d invalid. Race.\n", id); - return; + return BAD_PAGE | PAGE_SHARED; } idx = (code >> SHM_IDX_SHIFT) & SHM_IDX_MASK; if (idx >= shp->shm_npages) { printk ("shm_no_page : too large page index. id=%d\n", id); - return; + return BAD_PAGE | PAGE_SHARED; } if (!(shp->shm_pages[idx] & PAGE_PRESENT)) { if(!(page = get_free_page(GFP_KERNEL))) { oom(current); - *ptent = BAD_PAGE | PAGE_ACCESSED | 7; - return; + return BAD_PAGE | PAGE_SHARED; } if (shp->shm_pages[idx] & PAGE_PRESENT) { free_page (page); @@ -667,10 +680,9 @@ done: current->mm->min_flt++; page = shp->shm_pages[idx]; if (code & SHM_READ_ONLY) /* write-protect */ - page &= ~2; + page &= ~PAGE_RW; mem_map[MAP_NR(page)]++; - *ptent = page; - return; + return page; } /* diff --git a/kernel/exit.c b/kernel/exit.c index e99d9741f9c5..95fa23e68ad9 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -365,6 +365,8 @@ static void exit_mm(void) struct vm_area_struct * next = mpnt->vm_next; if (mpnt->vm_ops && mpnt->vm_ops->close) mpnt->vm_ops->close(mpnt); + if (mpnt->vm_inode) + iput(mpnt->vm_inode); kfree(mpnt); mpnt = next; } diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 583817cdd205..9521ee163219 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -79,6 +79,8 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ X(do_munmap), X(insert_vm_struct), X(zeromap_page_range), + X(unmap_page_range), + X(merge_segments), /* internal kernel memory management */ X(__get_free_pages), @@ -95,6 +97,7 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ X(iput), X(namei), X(lnamei), + X(open_namei), /* device registration */ X(register_chrdev), @@ -133,6 +136,8 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ /* process management */ X(wake_up), X(wake_up_interruptible), + X(sleep_on), + X(interruptible_sleep_on), X(schedule), X(current), X(jiffies), diff --git a/kernel/sched.c b/kernel/sched.c index 5e43d601d676..3f5330cda660 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -412,8 +412,9 @@ int del_timer(struct timer_list * timer) return 1; } } - if (p->next || p->prev) - printk("del_timer() called with timer not initialized\n"); + if (timer->next || timer->prev) + printk("del_timer() called from %08lx with timer not initialized\n", + ((unsigned long *) &timer)[-1]); restore_flags(flags); return 0; #else diff --git a/kernel/sys_call.S b/kernel/sys_call.S index 4e30c79fde6a..bf4b12e407f5 100644 --- a/kernel/sys_call.S +++ b/kernel/sys_call.S @@ -537,5 +537,6 @@ _sys_call_table: .long _sys_bdflush .long _sys_sysfs /* 135 */ .long _sys_personality + .long 0 /* for afs_syscall */ - .space (NR_syscalls-136)*4 + .space (NR_syscalls-137)*4 diff --git a/mm/memory.c b/mm/memory.c index bc3a2b4be3f1..a2e4baef4869 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -724,9 +724,16 @@ static int try_to_share(unsigned long to_address, struct vm_area_struct * to_are from &= PAGE_MASK; from_page = from + PAGE_PTR(from_address); from = *(unsigned long *) from_page; -/* is the page clean and present? */ - if ((from & (PAGE_PRESENT | PAGE_DIRTY)) != PAGE_PRESENT) +/* is the page present? */ + if (!(from & PAGE_PRESENT)) + return 0; +/* if it is private, it must be clean to be shared */ + if ((from_area->vm_page_prot & PAGE_COW) && (from & PAGE_DIRTY)) return 0; +/* the swap caching doesn't really handle shared pages.. */ + if (in_swap_cache(from)) + return 0; +/* is the page reasonable at all? */ if (from >= high_memory) return 0; if (mem_map[MAP_NR(from)] & MAP_PAGE_RESERVED) @@ -856,6 +863,28 @@ static inline unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned return 0; } +static inline void do_swap_page(struct vm_area_struct * vma, + unsigned long address, unsigned long * pge, unsigned long entry) +{ + unsigned long page; + + if (vma->vm_ops && vma->vm_ops->swapin) + page = vma->vm_ops->swapin(vma, entry); + else + page = swap_in(entry); + if (*pge != entry) { + free_page(page); + return; + } + page = page | vma->vm_page_prot; + if (mem_map[MAP_NR(page)] > 1 && (page & PAGE_COW)) + page &= ~PAGE_RW; + ++vma->vm_task->mm->rss; + ++vma->vm_task->mm->maj_flt; + *pge = page; + return; +} + void do_no_page(struct vm_area_struct * vma, unsigned long address, unsigned long error_code) { @@ -870,9 +899,7 @@ void do_no_page(struct vm_area_struct * vma, unsigned long address, if (entry & PAGE_PRESENT) return; if (entry) { - ++vma->vm_task->mm->rss; - ++vma->vm_task->mm->maj_flt; - swap_in((unsigned long *) page); + do_swap_page(vma, address, (unsigned long *) page, entry); return; } address &= PAGE_MASK; @@ -886,6 +913,7 @@ void do_no_page(struct vm_area_struct * vma, unsigned long address, page = get_free_page(GFP_KERNEL); if (share_page(vma, address, error_code, page)) { ++vma->vm_task->mm->min_flt; + ++vma->vm_task->mm->rss; return; } if (!page) { @@ -1224,7 +1252,7 @@ void si_meminfo(struct sysinfo *val) /* This handles a generic mmap of a disk file */ -unsigned long file_mmap_nopage(struct vm_area_struct * area, unsigned long address, +static unsigned long file_mmap_nopage(struct vm_area_struct * area, unsigned long address, unsigned long page, int error_code) { struct inode * inode = area->vm_inode; @@ -1244,33 +1272,11 @@ unsigned long file_mmap_nopage(struct vm_area_struct * area, unsigned long addre return bread_page(page, inode->i_dev, nr, inode->i_sb->s_blocksize, !(error_code & PAGE_RW)); } -void file_mmap_free(struct vm_area_struct * area) -{ - if (area->vm_inode) - iput(area->vm_inode); -#if 0 - if (area->vm_inode) - printk("Free inode %x:%d (%d)\n",area->vm_inode->i_dev, - area->vm_inode->i_ino, area->vm_inode->i_count); -#endif -} - -/* - * Compare the contents of the mmap entries, and decide if we are allowed to - * share the pages - */ -int file_mmap_share(struct vm_area_struct * area1, - struct vm_area_struct * area2, - unsigned long address) -{ - return 1; -} - struct vm_operations_struct file_mmap = { NULL, /* open */ - file_mmap_free, /* close */ + NULL, /* close */ file_mmap_nopage, /* nopage */ NULL, /* wppage */ - file_mmap_share, /* share */ + NULL, /* share */ NULL, /* unmap */ }; diff --git a/mm/mmap.c b/mm/mmap.c index 6ba5f39cf853..9f43fe25206e 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -194,6 +194,8 @@ void unmap_fixup(struct vm_area_struct *area, if (addr == area->vm_start && end == area->vm_end) { if (area->vm_ops && area->vm_ops->close) area->vm_ops->close(area); + if (area->vm_inode) + iput(area->vm_inode); return; } @@ -216,8 +218,8 @@ void unmap_fixup(struct vm_area_struct *area, mpnt->vm_start = end; if (mpnt->vm_inode) mpnt->vm_inode->i_count++; - insert_vm_struct(current, mpnt); area->vm_end = addr; /* Truncate area */ + insert_vm_struct(current, mpnt); } /* construct whatever mapping is needed */ diff --git a/mm/swap.c b/mm/swap.c index 2e6f93d4499d..22576d301bfd 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -8,6 +8,7 @@ * This file should contain most things doing the swapping from/to disk. * Started 18.12.91 */ +#define SWAP_CACHE_INFO #include #include @@ -47,12 +48,114 @@ static struct swap_info_struct { extern int shm_swap (int); -/* - * The following are used to make sure we don't thrash too much... - * NOTE!! NR_LAST_FREE_PAGES must be a power of 2... - */ -#define NR_LAST_FREE_PAGES 32 -static unsigned long last_free_pages[NR_LAST_FREE_PAGES] = {0,}; +unsigned long *swap_cache; +static unsigned long swap_cache_size; + +#ifdef SWAP_CACHE_INFO +static unsigned long add_calls_total = 0; +static unsigned long add_calls_success = 0; +static unsigned long del_calls_total = 0; +static unsigned long del_calls_success = 0; +static unsigned long find_calls_total = 0; +static unsigned long find_calls_success = 0; + +extern inline void show_swap_cache_info (void) +{ + printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n", + add_calls_total, add_calls_success, + del_calls_total, del_calls_success, + find_calls_total, find_calls_success); +} +#endif + +extern inline unsigned long init_swap_cache (unsigned long mem_start, + unsigned long mem_end) +{ + mem_start = (mem_start + 15) & ~15; + swap_cache = (unsigned long *) mem_start; + swap_cache_size = mem_end >> PAGE_SHIFT; + memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long)); +#ifdef SWAP_CACHE_INFO + printk("%ld bytes for swap cache allocated\n", + swap_cache_size * sizeof(unsigned long)); +#endif + + return (unsigned long) (swap_cache + swap_cache_size); +} + +extern inline long find_in_swap_cache (unsigned long addr) +{ + unsigned long entry; + +#ifdef SWAP_CACHE_INFO + find_calls_total++; +#endif + __asm__ __volatile__ ( + "xchgl %0,%1\n" + : "=m" (swap_cache[addr >> PAGE_SHIFT]), + "=r" (entry) + : "0" (swap_cache[addr >> PAGE_SHIFT]), + "1" (0) + ); +#ifdef SWAP_CACHE_INFO + if (entry) + find_calls_success++; +#endif + + return entry; +} + +extern inline int add_to_swap_cache (unsigned long addr, unsigned long entry) +{ + struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)]; + +#ifdef SWAP_CACHE_INFO + add_calls_total++; +#endif + if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { + __asm__ __volatile__ ( + "xchgl %0,%1\n" + : "=m" (swap_cache[addr >> PAGE_SHIFT]), + "=r" (entry) + : "0" (swap_cache[addr >> PAGE_SHIFT]), + "1" (entry) + ); + if (entry) { + printk("swap_cache: replacing non-NULL entry\n"); + } +#ifdef SWAP_CACHE_INFO + add_calls_success++; +#endif + return 1; + } + return 0; +} + + +extern inline int delete_from_swap_cache(unsigned long addr) +{ + unsigned long entry; + +#ifdef SWAP_CACHE_INFO + del_calls_total++; +#endif + __asm__ __volatile__ ( + "xchgl %0,%1\n" + : "=m" (swap_cache[addr >> PAGE_SHIFT]), + "=r" (entry) + : "0" (swap_cache[addr >> PAGE_SHIFT]), + "1" (0) + ); + if (entry) { +#ifdef SWAP_CACHE_INFO + del_calls_success++; +#endif + swap_free(entry); + return 1; + } + return 0; +} + void rw_swap_page(int rw, unsigned long entry, char * buf) { @@ -142,7 +245,7 @@ unsigned long swap_duplicate(unsigned long entry) } p = type + swap_info; if (offset >= p->max) { - printk("swap_free: weirdness\n"); + printk("swap_duplicate: weirdness\n"); return 0; } if (!p->swap_map[offset]) { @@ -193,42 +296,24 @@ void swap_free(unsigned long entry) wake_up(&lock_queue); } -void swap_in(unsigned long *table_ptr) +unsigned long swap_in(unsigned long entry) { - unsigned long entry; unsigned long page; - entry = *table_ptr; - if (PAGE_PRESENT & entry) { - printk("trying to swap in present page\n"); - return; - } - if (!entry) { - printk("No swap page in swap_in\n"); - return; - } - if (SWP_TYPE(entry) == SHM_SWP_TYPE) { - shm_no_page ((unsigned long *) table_ptr); - return; - } if (!(page = get_free_page(GFP_KERNEL))) { oom(current); - page = BAD_PAGE; - } else - read_swap_page(entry, (char *) page); - if (*table_ptr != entry) { - free_page(page); - return; + return BAD_PAGE; } - *table_ptr = page | (PAGE_DIRTY | PAGE_PRIVATE); - swap_free(entry); + read_swap_page(entry, (char *) page); + if (add_to_swap_cache(page, entry)) + return page | PAGE_PRIVATE; + swap_free(entry); + return page | PAGE_DIRTY | PAGE_PRIVATE; } static inline int try_to_swap_out(unsigned long * table_ptr) { - int i; - unsigned long page; - unsigned long entry; + unsigned long page, entry; page = *table_ptr; if (!(PAGE_PRESENT & page)) @@ -237,13 +322,14 @@ static inline int try_to_swap_out(unsigned long * table_ptr) return 0; if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED) return 0; + + if ((PAGE_DIRTY & page) && delete_from_swap_cache(page)) { + return 0; + } if (PAGE_ACCESSED & page) { *table_ptr &= ~PAGE_ACCESSED; return 0; } - for (i = 0; i < NR_LAST_FREE_PAGES; i++) - if (last_free_pages[i] == (page & PAGE_MASK)) - return 0; if (PAGE_DIRTY & page) { page &= PAGE_MASK; if (mem_map[MAP_NR(page)] != 1) @@ -256,6 +342,26 @@ static inline int try_to_swap_out(unsigned long * table_ptr) free_page(page); return 1; } + + if ((entry = find_in_swap_cache(page))) { + *table_ptr |= PAGE_DIRTY; + if (mem_map[MAP_NR(page)] != 1) { + return 0; + } + if (!entry) { + if (!(entry = get_swap_page())) { + return 0; + } + *table_ptr = entry; + invalidate(); + write_swap_page(entry, (char *) (page & PAGE_MASK)); + } else { + *table_ptr = entry; + invalidate(); + } + free_page(page & PAGE_MASK); + return 1; + } page &= PAGE_MASK; *table_ptr = 0; invalidate(); @@ -531,8 +637,10 @@ void free_pages(unsigned long addr, unsigned long order) if (!(*map & MAP_PAGE_RESERVED)) { save_flags(flag); cli(); - if (!--*map) + if (!--*map) { free_pages_ok(addr, order); + delete_from_swap_cache(addr); + } restore_flags(flag); if(*map == 1) { int j; @@ -639,6 +747,9 @@ void show_free_areas(void) } restore_flags(flags); printk("= %lukB)\n", total); +#ifdef SWAP_CACHE_INFO + show_swap_cache_info(); +#endif } /* @@ -654,6 +765,7 @@ static int try_to_unuse(unsigned int type) struct task_struct *p; nr = 0; + /* * When we have to sleep, we restart the whole algorithm from the same * task we stopped in. That at least rids us of all races. @@ -677,8 +789,15 @@ repeat: page = *ppage; if (!page) continue; - if (page & PAGE_PRESENT) + if (page & PAGE_PRESENT) { + if (!(page = in_swap_cache(page))) + continue; + if (SWP_TYPE(page) != type) + continue; + *ppage |= PAGE_DIRTY; + delete_from_swap_cache(*ppage); continue; + } if (SWP_TYPE(page) != type) continue; if (!tmp) { @@ -898,6 +1017,7 @@ unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem) unsigned long mask = PAGE_MASK; int i; + start_mem = init_swap_cache(start_mem, end_mem); mem_map = (unsigned short *) start_mem; p = mem_map + MAP_NR(end_mem); start_mem = (unsigned long) p; diff --git a/net/inet/arp.c b/net/inet/arp.c index 5c61c5636b9e..61e989e495e6 100644 --- a/net/inet/arp.c +++ b/net/inet/arp.c @@ -746,13 +746,13 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) entry->hlen = hlen; entry->htype = htype; entry->flags = ATF_COM; + entry->timer.next = entry->timer.prev = NULL; memcpy(entry->ha, sha, hlen); entry->last_used = jiffies; entry->dev = skb->dev; skb_queue_head_init(&entry->skb); entry->next = arp_tables[hash]; arp_tables[hash] = entry; - entry->timer.next = entry->timer.prev = NULL; sti(); } @@ -837,14 +837,14 @@ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev, entry->htype = dev->type; entry->flags = 0; memset(entry->ha, 0, dev->addr_len); - entry->last_used = jiffies; - entry->next = arp_tables[hash]; entry->dev = dev; - arp_tables[hash] = entry; + entry->last_used = jiffies; entry->timer.next = entry->timer.prev = NULL; entry->timer.function = arp_expire_request; entry->timer.data = (unsigned long)entry; entry->timer.expires = ARP_RES_TIME; + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; add_timer(&entry->timer); entry->retries = ARP_MAX_TRIES; skb_queue_head_init(&entry->skb); @@ -1048,8 +1048,8 @@ static int arp_req_set(struct arpreq *req) entry->ip = ip; entry->hlen = hlen; entry->htype = htype; - entry->next = arp_tables[hash]; entry->timer.next = entry->timer.prev = NULL; + entry->next = arp_tables[hash]; arp_tables[hash] = entry; skb_queue_head_init(&entry->skb); } -- 2.39.5