From 13c9f31aba86054cbe508c28a9001dd516f04910 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 19:25:59 -0700 Subject: [Power Mgmt] Share variables between pmdisk and swsusp. - In pmdisk, change pm_pagedir_nosave back to pagedir_nosave, and pmdisk_pages back to nr_copy_pages. - Mark them, and other page count/pagedir variables extern. - Make sure they're not static in swsusp. - Remove mention from include/linux/suspend.h, since no one else needs them. --- include/linux/suspend.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index d0955f06c9b4..2daac52c53a3 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -45,9 +45,6 @@ extern void drain_local_pages(void); /* kernel/power/swsusp.c */ extern int software_suspend(void); -extern unsigned int nr_copy_pages __nosavedata; -extern suspend_pagedir_t *pagedir_nosave __nosavedata; - #else /* CONFIG_SOFTWARE_SUSPEND */ static inline int software_suspend(void) { -- cgit v1.2.3 From 44ea4dc07e7a1281293386e755f9a96d523e84b3 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 21:11:04 -0700 Subject: [Power Mgmt] Consolidate pmdisk and swsusp low-level handling. - Split do_magic into swsusp_arch_suspend() and swsusp_arch_resume(). - Clean up based on pmdisk implementation - Only save registers we need to. - Use rep;movsl for copying, rather than doing each byte. - Create swsusp_suspend and swsusp_resume wrappers for calling the assmebly routines that: - Call {save,restore}_processor_state() in each. - Disable/enable interrupts in each. - Call swsusp_{suspend,restore} in software_{suspend,resume} - Kill all the do_magic_* functions. - Remove prototypes from linux/suspend.h - Remove similar pmdisk functions. - Update calls in kernel/power/disk.c to use swsusp versions. --- arch/i386/power/Makefile | 1 - arch/i386/power/swsusp.S | 76 +++++++++---------------------- include/linux/suspend.h | 6 --- kernel/power/disk.c | 8 ++-- kernel/power/pmdisk.c | 89 ------------------------------------ kernel/power/swsusp.c | 115 +++++++++++++++++++---------------------------- 6 files changed, 70 insertions(+), 225 deletions(-) (limited to 'include/linux') diff --git a/arch/i386/power/Makefile b/arch/i386/power/Makefile index 2e1c9ab34d4c..8cfa4e8a719d 100644 --- a/arch/i386/power/Makefile +++ b/arch/i386/power/Makefile @@ -1,3 +1,2 @@ obj-$(CONFIG_PM) += cpu.o -obj-$(CONFIG_PM_DISK) += pmdisk.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o diff --git a/arch/i386/power/swsusp.S b/arch/i386/power/swsusp.S index 8e4a7bacaadf..221ac27a204f 100644 --- a/arch/i386/power/swsusp.S +++ b/arch/i386/power/swsusp.S @@ -15,83 +15,47 @@ .text -ENTRY(do_magic) - pushl %ebx - cmpl $0,8(%esp) - jne resume - call do_magic_suspend_1 - call save_processor_state +ENTRY(swsusp_arch_suspend) movl %esp, saved_context_esp - movl %eax, saved_context_eax movl %ebx, saved_context_ebx - movl %ecx, saved_context_ecx - movl %edx, saved_context_edx movl %ebp, saved_context_ebp movl %esi, saved_context_esi movl %edi, saved_context_edi pushfl ; popl saved_context_eflags - call do_magic_suspend_2 - popl %ebx + call swsusp_save ret -resume: +ENTRY(swsusp_arch_resume) movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx movl %ecx,%cr3 - call do_magic_resume_1 - movl $0,loop - cmpl $0,nr_copy_pages - je copy_done -copy_loop: - movl $0,loop2 + movl pagedir_nosave,%ebx + xorl %eax, %eax + xorl %edx, %edx .p2align 4,,7 -copy_one_page: - movl pagedir_nosave,%ecx - movl loop,%eax - movl loop2,%edx - sall $4,%eax - movl 4(%ecx,%eax),%ebx - movl (%ecx,%eax),%eax - movb (%edx,%eax),%al - movb %al,(%edx,%ebx) - movl loop2,%eax - leal 1(%eax),%edx - movl %edx,loop2 - movl %edx,%eax - cmpl $4095,%eax - jbe copy_one_page - movl loop,%eax - leal 1(%eax),%edx - movl %edx,loop - movl %edx,%eax - cmpl nr_copy_pages,%eax - jb copy_loop +copy_loop: + movl 4(%ebx,%edx),%edi + movl (%ebx,%edx),%esi + + movl $1024, %ecx + rep + movsl -copy_done: - movl $__USER_DS,%eax + incl %eax + addl $16, %edx + cmpl nr_copy_pages,%eax + jb copy_loop + .p2align 4,,7 - movw %ax, %ds - movw %ax, %es movl saved_context_esp, %esp movl saved_context_ebp, %ebp - movl saved_context_eax, %eax movl saved_context_ebx, %ebx - movl saved_context_ecx, %ecx - movl saved_context_edx, %edx movl saved_context_esi, %esi movl saved_context_edi, %edi - call restore_processor_state + pushl saved_context_eflags ; popfl - call do_magic_resume_2 - popl %ebx + call swsusp_restore ret - - .section .data.nosave -loop: - .quad 0 -loop2: - .quad 0 - .previous diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 2daac52c53a3..496d17f40291 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -75,12 +75,6 @@ static inline void disable_nonboot_cpus(void) {} static inline void enable_nonboot_cpus(void) {} #endif -asmlinkage void do_magic(int is_resume); -asmlinkage void do_magic_resume_1(void); -asmlinkage void do_magic_resume_2(void); -asmlinkage void do_magic_suspend_1(void); -asmlinkage void do_magic_suspend_2(void); - void save_processor_state(void); void restore_processor_state(void); struct saved_context; diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 6abcf99b7ada..f3a4a4b1bb56 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -23,10 +23,10 @@ extern u32 pm_disk_mode; extern struct pm_ops * pm_ops; -extern int pmdisk_save(void); +extern int swsusp_suspend(void); extern int pmdisk_write(void); extern int pmdisk_read(void); -extern int pmdisk_restore(void); +extern int swsusp_resume(void); extern int pmdisk_free(void); @@ -161,7 +161,7 @@ int pm_suspend_disk(void) pr_debug("PM: snapshotting memory.\n"); in_suspend = 1; - if ((error = pmdisk_save())) + if ((error = swsusp_save())) goto Done; if (in_suspend) { @@ -227,7 +227,7 @@ static int pm_resume(void) mdelay(1000); pr_debug("PM: Restoring saved image.\n"); - pmdisk_restore(); + swsusp_resume(); pr_debug("PM: Restore failed, recovering.n"); finish(); Free: diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index f10253a3840a..bbe93fdc1cdf 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -249,28 +249,7 @@ static int write_suspend_image(void) extern void free_suspend_pagedir(unsigned long); -extern int suspend_prepare_image(void); -/** - * pmdisk_suspend - Atomically snapshot the system. - * - * This must be called with interrupts disabled, to prevent the - * system changing at all from underneath us. - * - * To do this, we count the number of pages in the system that we - * need to save; make sure we have enough memory and swap to clone - * the pages and save them in swap, allocate the space to hold them, - * and then snapshot them all. - */ - -int pmdisk_suspend(void) -{ - int error = 0; - - if ((error = swsusp_swap_check())) - return error; - return suspend_prepare_image(); -} /** @@ -297,36 +276,6 @@ static int suspend_save_image(void) return error; } -/* - * Magic happens here - */ - -int pmdisk_resume(void) -{ - BUG_ON (nr_copy_pages_check != nr_copy_pages); - BUG_ON (pagedir_order_check != pagedir_order); - - /* Even mappings of "global" things (vmalloc) need to be fixed */ - __flush_tlb_global(); - return 0; -} - -/* pmdisk_arch_suspend() is implemented in arch/?/power/pmdisk.S, - and basically does: - - if (!resume) { - save_processor_state(); - SAVE_REGISTERS - return pmdisk_suspend(); - } - GO_TO_SWAPPER_PAGE_TABLES - COPY_PAGES_BACK - RESTORE_REGISTERS - restore_processor_state(); - return pmdisk_resume(); - - */ - /* More restore stuff */ @@ -563,28 +512,6 @@ static int __init read_suspend_image(void) goto Done; } -/** - * pmdisk_save - Snapshot memory - */ - -int pmdisk_save(void) -{ - int error; - -#if defined (CONFIG_HIGHMEM) || defined (CONFIG_DISCONTIGMEM) - pr_debug("pmdisk: not supported with high- or discontig-mem.\n"); - return -EPERM; -#endif - if ((error = arch_prepare_suspend())) - return error; - local_irq_disable(); - save_processor_state(); - error = pmdisk_arch_suspend(0); - restore_processor_state(); - local_irq_enable(); - return error; -} - /** * pmdisk_write - Write saved memory image to swap. @@ -632,22 +559,6 @@ int __init pmdisk_read(void) } -/** - * pmdisk_restore - Replace running kernel with saved image. - */ - -int __init pmdisk_restore(void) -{ - int error; - local_irq_disable(); - save_processor_state(); - error = pmdisk_arch_suspend(1); - restore_processor_state(); - local_irq_enable(); - return error; -} - - /** * pmdisk_free - Free memory allocated to hold snapshot. */ diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 91d8390dc2f5..7de6b26f4efe 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -893,80 +893,58 @@ static void suspend_finish(void) #endif } -/* - * Magic happens here - */ -asmlinkage void do_magic_resume_1(void) -{ - barrier(); - mb(); - spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ +extern asmlinkage int swsusp_arch_suspend(void); +extern asmlinkage int swsusp_arch_resume(void); - device_power_down(3); - PRINTK( "Waiting for DMAs to settle down...\n"); - mdelay(1000); /* We do not want some readahead with DMA to corrupt our memory, right? - Do it with disabled interrupts for best effect. That way, if some - driver scheduled DMA, we have good chance for DMA to finish ;-). */ -} -asmlinkage void do_magic_resume_2(void) +asmlinkage int swsusp_save(void) { - BUG_ON (nr_copy_pages_check != nr_copy_pages); - BUG_ON (pagedir_order_check != pagedir_order); + int error = 0; - __flush_tlb_global(); /* Even mappings of "global" things (vmalloc) need to be fixed */ - device_power_up(); - spin_unlock_irq(&suspend_pagedir_lock); + if ((error = swsusp_swap_check())) + return error; + return suspend_prepare_image(); } -/* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does: - - if (!resume) { - do_magic_suspend_1(); - save_processor_state(); - SAVE_REGISTERS - do_magic_suspend_2(); - return; - } - GO_TO_SWAPPER_PAGE_TABLES - do_magic_resume_1(); - COPY_PAGES_BACK - RESTORE_REGISTERS +int swsusp_suspend(void) +{ + int error; + if ((error = arch_prepare_suspend())) + return error; + local_irq_disable(); + save_processor_state(); + error = swsusp_arch_suspend(); restore_processor_state(); - do_magic_resume_2(); + local_irq_enable(); + return error; +} - */ -asmlinkage void do_magic_suspend_1(void) +asmlinkage int swsusp_restore(void) { - mb(); - barrier(); - BUG_ON(in_atomic()); - spin_lock_irq(&suspend_pagedir_lock); + BUG_ON (nr_copy_pages_check != nr_copy_pages); + BUG_ON (pagedir_order_check != pagedir_order); + + /* Even mappings of "global" things (vmalloc) need to be fixed */ + __flush_tlb_global(); + return 0; } -asmlinkage void do_magic_suspend_2(void) +int swsusp_resume(void) { - int is_problem; - swsusp_swap_check(); - device_power_down(3); - is_problem = suspend_prepare_image(); - device_power_up(); - spin_unlock_irq(&suspend_pagedir_lock); - if (!is_problem) { - kernel_fpu_end(); /* save_processor_state() does kernel_fpu_begin, and we need to revert it in order to pass in_atomic() checks */ - BUG_ON(in_atomic()); - suspend_save_image(); - suspend_power_down(); /* FIXME: if suspend_power_down is commented out, console is lost after few suspends ?! */ - } + int error; + local_irq_disable(); + save_processor_state(); + error = swsusp_arch_resume(); + restore_processor_state(); + local_irq_enable(); + return error; +} - printk(KERN_EMERG "%sSuspend failed, trying to recover...\n", name_suspend); - MDELAY(1000); /* So user can wait and report us messages if armageddon comes :-) */ - barrier(); - mb(); -} + +static int in_suspend __nosavedata = 0; /* * This is main interface to the outside world. It needs to be @@ -998,16 +976,15 @@ int software_suspend(void) /* Save state of all device drivers, and stop them. */ printk("Suspending devices... "); if ((res = device_suspend(3))==0) { - /* If stopping device drivers worked, we proceed basically into - * suspend_save_image. - * - * do_magic(0) returns after system is resumed. - * - * do_magic() copies all "used" memory to "free" memory, then - * unsuspends all device drivers, and writes memory to disk - * using normal kernel mechanism. - */ - do_magic(0); + in_suspend = 1; + + res = swsusp_save(); + + if (!res && in_suspend) { + suspend_save_image(); + suspend_power_down(); + } + in_suspend = 0; suspend_finish(); } thaw_processes(); @@ -1352,7 +1329,7 @@ static int __init software_resume(void) /* FIXME: Should we stop processes here, just to be safer? */ disable_nonboot_cpus(); device_suspend(3); - do_magic(1); + swsusp_resume(); panic("This never returns"); read_failure: -- cgit v1.2.3 From 62ca4334fa3b23492b79ab2887b62ac4bb4c2ed2 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 16 Jul 2004 22:23:27 -0700 Subject: [Power Mgmt] Merge swsusp and pmdisk info headers. - Move definition of struct pmdsik_info to power.h and rename to struct swsusp_info. - Kill struct suspend_header. - Move helpers from pmdisk into swsusp: init_header(), dump_info(), write_header(), sanity_check(), check_header(). - Fix up calls in pmdisk to call the right ones. - Clean up swsusp code to use helpers; delete duplicates. --- include/linux/suspend.h | 10 --- kernel/power/pmdisk.c | 131 ++++-------------------------------- kernel/power/power.h | 16 ++++- kernel/power/swsusp.c | 175 +++++++++++++++++++++++++++++------------------- 4 files changed, 135 insertions(+), 197 deletions(-) (limited to 'include/linux') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 496d17f40291..6b63016b55a9 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -23,16 +23,6 @@ typedef struct pbe { #define SWAP_FILENAME_MAXLENGTH 32 -struct suspend_header { - u32 version_code; - unsigned long num_physpages; - char machine[8]; - char version[20]; - int num_cpus; - int page_size; - suspend_pagedir_t *suspend_pagedir; - unsigned int num_pbes; -}; #define SUSPEND_PD_PAGES(x) (((x)*sizeof(struct pbe))/PAGE_SIZE+1) diff --git a/kernel/power/pmdisk.c b/kernel/power/pmdisk.c index ef0d74f351b4..53b47002f5e3 100644 --- a/kernel/power/pmdisk.c +++ b/kernel/power/pmdisk.c @@ -28,7 +28,6 @@ #include #include #include -#include #include @@ -61,17 +60,7 @@ extern suspend_pagedir_t *pagedir_nosave; extern suspend_pagedir_t *pagedir_save; extern int pagedir_order; - -struct pmdisk_info { - struct new_utsname uts; - u32 version_code; - unsigned long num_physpages; - int cpus; - unsigned long image_pages; - unsigned long pagedir_pages; - swp_entry_t pagedir[768]; -} __attribute__((aligned(PAGE_SIZE))) pmdisk_info; - +extern struct swsusp_info swsusp_info; #define PMDISK_SIG "pmdisk-swap1" @@ -139,11 +128,11 @@ extern void swsusp_data_free(void); static void free_pagedir_entries(void) { - int num = pmdisk_info.pagedir_pages; + int num = swsusp_info.pagedir_pages; int i; for (i = 0; i < num; i++) - swap_free(pmdisk_info.pagedir[i]); + swap_free(swsusp_info.pagedir[i]); } @@ -159,64 +148,15 @@ static int write_pagedir(void) int n = SUSPEND_PD_PAGES(nr_copy_pages); int i; - pmdisk_info.pagedir_pages = n; + swsusp_info.pagedir_pages = n; printk( "Writing pagedir (%d pages)\n", n); for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) - error = swsusp_write_page(addr,&pmdisk_info.pagedir[i]); + error = swsusp_write_page(addr,&swsusp_info.pagedir[i]); return error; } - -#ifdef DEBUG -static void dump_pmdisk_info(void) -{ - printk(" pmdisk: Version: %u\n",pmdisk_info.version_code); - printk(" pmdisk: Num Pages: %ld\n",pmdisk_info.num_physpages); - printk(" pmdisk: UTS Sys: %s\n",pmdisk_info.uts.sysname); - printk(" pmdisk: UTS Node: %s\n",pmdisk_info.uts.nodename); - printk(" pmdisk: UTS Release: %s\n",pmdisk_info.uts.release); - printk(" pmdisk: UTS Version: %s\n",pmdisk_info.uts.version); - printk(" pmdisk: UTS Machine: %s\n",pmdisk_info.uts.machine); - printk(" pmdisk: UTS Domain: %s\n",pmdisk_info.uts.domainname); - printk(" pmdisk: CPUs: %d\n",pmdisk_info.cpus); - printk(" pmdisk: Image: %ld Pages\n",pmdisk_info.image_pages); - printk(" pmdisk: Pagedir: %ld Pages\n",pmdisk_info.pagedir_pages); -} -#else -static void dump_pmdisk_info(void) -{ - -} -#endif - -static void init_header(void) -{ - memset(&pmdisk_info,0,sizeof(pmdisk_info)); - pmdisk_info.version_code = LINUX_VERSION_CODE; - pmdisk_info.num_physpages = num_physpages; - memcpy(&pmdisk_info.uts,&system_utsname,sizeof(system_utsname)); - - pmdisk_info.cpus = num_online_cpus(); - pmdisk_info.image_pages = nr_copy_pages; - dump_pmdisk_info(); -} - -/** - * write_header - Fill and write the suspend header. - * @entry: Location of the last swap entry used. - * - * Allocate a page, fill header, write header. - * - * @entry is the location of the last pagedir entry written on - * entrance. On exit, it contains the location of the header. - */ - -static int write_header(swp_entry_t * entry) -{ - return swsusp_write_page((unsigned long)&pmdisk_info,entry); -} - - +extern void swsusp_init_header(void); +extern int swsusp_write_header(swp_entry_t*); /** * write_suspend_image - Write entire image and metadata. @@ -228,13 +168,14 @@ static int write_suspend_image(void) int error; swp_entry_t prev = { 0 }; + swsusp_init_header(); if ((error = swsusp_data_write())) goto FreeData; if ((error = write_pagedir())) goto FreePagedir; - if ((error = write_header(&prev))) + if ((error = swsusp_write_header(&prev))) goto FreePagedir; error = mark_swapfiles(prev); @@ -310,57 +251,10 @@ static int __init check_sig(void) } -/* - * Sanity check if this image makes sense with this kernel/swap context - * I really don't think that it's foolproof but more than nothing.. - */ - -static const char * __init sanity_check(void) -{ - dump_pmdisk_info(); - if(pmdisk_info.version_code != LINUX_VERSION_CODE) - return "kernel version"; - if(pmdisk_info.num_physpages != num_physpages) - return "memory size"; - if (strcmp(pmdisk_info.uts.sysname,system_utsname.sysname)) - return "system type"; - if (strcmp(pmdisk_info.uts.release,system_utsname.release)) - return "kernel release"; - if (strcmp(pmdisk_info.uts.version,system_utsname.version)) - return "version"; - if (strcmp(pmdisk_info.uts.machine,system_utsname.machine)) - return "machine"; - if(pmdisk_info.cpus != num_online_cpus()) - return "number of cpus"; - return NULL; -} - - -static int __init check_header(void) -{ - const char * reason = NULL; - int error; - - init_header(); - - if ((error = bio_read_page(swp_offset(pmdisk_header.pmdisk_info), - &pmdisk_info))) - return error; - - /* Is this same machine? */ - if ((reason = sanity_check())) { - printk(KERN_ERR "pmdisk: Resume mismatch: %s\n",reason); - return -EPERM; - } - nr_copy_pages = pmdisk_info.image_pages; - return error; -} - - static int __init read_pagedir(void) { unsigned long addr; - int i, n = pmdisk_info.pagedir_pages; + int i, n = swsusp_info.pagedir_pages; int error = 0; pagedir_order = get_bitmask_order(n); @@ -373,7 +267,7 @@ static int __init read_pagedir(void) pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n); for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) { - unsigned long offset = swp_offset(pmdisk_info.pagedir[i]); + unsigned long offset = swp_offset(swsusp_info.pagedir[i]); if (offset) error = bio_read_page(offset, (void *)addr); else @@ -388,12 +282,13 @@ static int __init read_pagedir(void) static int __init read_suspend_image(void) { extern int swsusp_data_read(void); + extern int swsusp_check_header(swp_entry_t); int error = 0; if ((error = check_sig())) return error; - if ((error = check_header())) + if ((error = swsusp_check_header(pmdisk_header.pmdisk_info))) return error; if ((error = read_pagedir())) return error; diff --git a/kernel/power/power.h b/kernel/power/power.h index 92424ccc2076..69c693cea1b5 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -1,4 +1,5 @@ - +#include +#include /* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but we probably do not take enough locks for switching consoles, etc, @@ -9,6 +10,19 @@ #endif +struct swsusp_info { + struct new_utsname uts; + u32 version_code; + unsigned long num_physpages; + int cpus; + unsigned long image_pages; + unsigned long pagedir_pages; + suspend_pagedir_t * suspend_pagedir; + swp_entry_t pagedir[768]; +} __attribute__((aligned(PAGE_SIZE))); + + + #ifdef CONFIG_PM_DISK extern int pm_suspend_disk(void); diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 21cb666a7a81..9cb01d8cc92d 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -111,6 +111,8 @@ suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; suspend_pagedir_t *pagedir_save; int pagedir_order __nosavedata = 0; +struct swsusp_info swsusp_info; + struct link { char dummy[PAGE_SIZE - sizeof(swp_entry_t)]; swp_entry_t next; @@ -119,7 +121,6 @@ struct link { union diskpage { union swap_header swh; struct link link; - struct suspend_header sh; }; /* @@ -155,27 +156,6 @@ static const char name_resume[] = "Resume Machine: "; * Saving part... */ -static __inline__ int fill_suspend_header(struct suspend_header *sh) -{ - memset((char *)sh, 0, sizeof(*sh)); - - sh->version_code = LINUX_VERSION_CODE; - sh->num_physpages = num_physpages; - strncpy(sh->machine, system_utsname.machine, 8); - strncpy(sh->version, system_utsname.version, 20); - /* FIXME: Is this bogus? --RR */ - sh->num_cpus = num_online_cpus(); - sh->page_size = PAGE_SIZE; - sh->suspend_pagedir = pagedir_nosave; - BUG_ON (pagedir_save != pagedir_nosave); - sh->num_pbes = nr_copy_pages; - /* TODO: needed? mounted fs' last mounted date comparison - * [so they haven't been mounted since last suspend. - * Maybe it isn't.] [we'd need to do this for _all_ fs-es] - */ - return 0; -} - /* We memorize in swapfile_used what swap devices are used for suspension */ #define SWAPFILE_UNUSED 0 #define SWAPFILE_SUSPEND 1 /* This is the suspending device */ @@ -375,7 +355,55 @@ int swsusp_data_write(void) return error; } +#ifdef DEBUG +static void dump_info(void) +{ + printk(" swsusp: Version: %u\n",swsusp_info.version_code); + printk(" swsusp: Num Pages: %ld\n",swsusp_info.num_physpages); + printk(" swsusp: UTS Sys: %s\n",swsusp_info.uts.sysname); + printk(" swsusp: UTS Node: %s\n",swsusp_info.uts.nodename); + printk(" swsusp: UTS Release: %s\n",swsusp_info.uts.release); + printk(" swsusp: UTS Version: %s\n",swsusp_info.uts.version); + printk(" swsusp: UTS Machine: %s\n",swsusp_info.uts.machine); + printk(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname); + printk(" swsusp: CPUs: %d\n",swsusp_info.cpus); + printk(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages); + printk(" swsusp: Pagedir: %ld Pages\n",swsusp_info.pagedir_pages); +} +#else +static void dump_info(void) +{ + +} +#endif + +void swsusp_init_header(void) +{ + memset(&swsusp_info,0,sizeof(swsusp_info)); + swsusp_info.version_code = LINUX_VERSION_CODE; + swsusp_info.num_physpages = num_physpages; + memcpy(&swsusp_info.uts,&system_utsname,sizeof(system_utsname)); + + swsusp_info.suspend_pagedir = pagedir_nosave; + swsusp_info.cpus = num_online_cpus(); + swsusp_info.image_pages = nr_copy_pages; + dump_info(); +} + +/** + * write_header - Fill and write the suspend header. + * @entry: Location of the last swap entry used. + * + * Allocate a page, fill header, write header. + * + * @entry is the location of the last pagedir entry written on + * entrance. On exit, it contains the location of the header. + */ +int swsusp_write_header(swp_entry_t * entry) +{ + return swsusp_write_page((unsigned long)&swsusp_info,entry); +} /** * write_suspend_image - Write entire image to disk. @@ -411,17 +439,12 @@ static int write_suspend_image(void) error = swsusp_write_page(addr,&entry); } printk("H"); - BUG_ON (sizeof(struct suspend_header) > PAGE_SIZE-sizeof(swp_entry_t)); BUG_ON (sizeof(union diskpage) != PAGE_SIZE); BUG_ON (sizeof(struct link) != PAGE_SIZE); - cur = (void *) buffer; - if (fill_suspend_header(&cur->sh)) - BUG(); /* Not a BUG_ON(): we want fill_suspend_header to be called, always */ - - cur->link.next = entry; - if ((error = swsusp_write_page((unsigned long)cur,&entry))) - return error; + swsusp_init_header(); + swsusp_info.pagedir[0] = entry; + error = swsusp_write_header(&entry); printk( "S" ); mark_swapfiles(entry, MARK_SWAP_SUSPEND); printk( "|\n" ); @@ -734,7 +757,7 @@ static int alloc_image_pages(void) static int enough_free_mem(void) { if(nr_free_pages() < (nr_copy_pages + PAGES_FOR_IO)) { - pr_debug("pmdisk: Not enough free pages: Have %d\n", + pr_debug("swsusp: Not enough free pages: Have %d\n", nr_free_pages()); return 0; } @@ -758,7 +781,7 @@ static int enough_swap(void) si_swapinfo(&i); if (i.freeswap < (nr_copy_pages + PAGES_FOR_IO)) { - pr_debug("pmdisk: Not enough swap. Need %ld\n",i.freeswap); + pr_debug("swsusp: Not enough swap. Need %ld\n",i.freeswap); return 0; } return 1; @@ -1087,35 +1110,6 @@ static int __init swsusp_pagedir_relocate(void) return check_pagedir(); } -/* - * Sanity check if this image makes sense with this kernel/swap context - * I really don't think that it's foolproof but more than nothing.. - */ - -static int sanity_check_failed(char *reason) -{ - printk(KERN_ERR "%s%s\n", name_resume, reason); - return -EPERM; -} - -static int sanity_check(struct suspend_header *sh) -{ - if (sh->version_code != LINUX_VERSION_CODE) - return sanity_check_failed("Incorrect kernel version"); - if (sh->num_physpages != num_physpages) - return sanity_check_failed("Incorrect memory size"); - if (strncmp(sh->machine, system_utsname.machine, 8)) - return sanity_check_failed("Incorrect machine type"); - if (strncmp(sh->version, system_utsname.version, 20)) - return sanity_check_failed("Incorrect version"); - if (sh->num_cpus != num_online_cpus()) - return sanity_check_failed("Incorrect number of cpus"); - if (sh->page_size != PAGE_SIZE) - return sanity_check_failed("Incorrect PAGE_SIZE"); - return 0; -} - - /** * Using bio to read from swap. * This code requires a bit more work than just using buffer heads @@ -1172,7 +1166,7 @@ static int submit(int rw, pgoff_t page_off, void * page) bio->bi_end_io = end_io; if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) { - printk("pmdisk: ERROR: adding page to bio at %ld\n",page_off); + printk("swsusp: ERROR: adding page to bio at %ld\n",page_off); error = -EFAULT; goto Done; } @@ -1197,6 +1191,50 @@ int bio_write_page(pgoff_t page_off, void * page) return submit(WRITE,page_off,page); } +/* + * Sanity check if this image makes sense with this kernel/swap context + * I really don't think that it's foolproof but more than nothing.. + */ + +static const char * __init sanity_check(void) +{ + dump_info(); + if(swsusp_info.version_code != LINUX_VERSION_CODE) + return "kernel version"; + if(swsusp_info.num_physpages != num_physpages) + return "memory size"; + if (strcmp(swsusp_info.uts.sysname,system_utsname.sysname)) + return "system type"; + if (strcmp(swsusp_info.uts.release,system_utsname.release)) + return "kernel release"; + if (strcmp(swsusp_info.uts.version,system_utsname.version)) + return "version"; + if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) + return "machine"; + if(swsusp_info.cpus != num_online_cpus()) + return "number of cpus"; + return NULL; +} + + +int __init swsusp_check_header(swp_entry_t loc) +{ + const char * reason = NULL; + int error; + + if ((error = bio_read_page(swp_offset(loc), &swsusp_info))) + return error; + + /* Is this same machine? */ + if ((reason = sanity_check())) { + printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason); + return -EPERM; + } + nr_copy_pages = swsusp_info.image_pages; + return error; +} + + /** * swsusp_read_data - Read image pages from swap. @@ -1276,13 +1314,14 @@ static int __init __read_suspend_image(int noresume) printk( "%sSignature found, resuming\n", name_resume ); MDELAY(1000); - if (bio_read_page(next.val, cur)) return -EIO; - if (sanity_check(&cur->sh)) /* Is this same machine? */ - return -EPERM; - PREPARENEXT; + if ((error = swsusp_check_header(next))) + return error; + + next = swsusp_info.pagedir[0]; + next.val = swp_offset(next); - pagedir_save = cur->sh.suspend_pagedir; - nr_copy_pages = cur->sh.num_pbes; + pagedir_save = swsusp_info.suspend_pagedir; + nr_copy_pages = swsusp_info.image_pages; nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages); pagedir_order = get_bitmask_order(nr_pgdir_pages); -- cgit v1.2.3 From e2676af65065dbf62e1a8e59016ed8536d0fee4a Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Sat, 17 Jul 2004 00:09:36 -0700 Subject: [Power Mgmt] Merge swsusp entry points with the PM core. - Add {enable,disable}_nonboot_cpus() to prepare() and finish() in kernel/power/disk.c - Move swsusp __setup options there. Remove resume_status variable in favor of simpler 'noresume' variable, and check it in pm_resume(). - Remove software_resume() from swsusp; rename pm_resume() to software_resume(). The latter is considerably cleaner, and leverages the core code. - Move software_suspend() to kernel/power/main.c and shrink it down a wrapper that takes pm_sem and calls pm_suspend_disk(), which does the same as the old software_suspend() did. This keeps the same entry points (via ACPI sleep and the reboot() syscall), but actually allows those to use the low-level power states of the system rather than always shutting off the system. - Remove now-unused functions from swsusp. --- include/linux/suspend.h | 1 - kernel/power/disk.c | 49 ++++++++---- kernel/power/main.c | 15 ++++ kernel/power/swsusp.c | 200 +----------------------------------------------- 4 files changed, 52 insertions(+), 213 deletions(-) (limited to 'include/linux') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 6b63016b55a9..932dc2ca4152 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -41,7 +41,6 @@ static inline int software_suspend(void) printk("Warning: fake suspend called\n"); return -EPERM; } -#define software_resume() do { } while(0) #endif /* CONFIG_SOFTWARE_SUSPEND */ diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 6d4119cf40cc..ab49261f078f 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -30,6 +30,9 @@ extern int swsusp_resume(void); extern int swsusp_free(void); +static int noresume = 0; +char resume_file[256] = CONFIG_PM_STD_PARTITION; + /** * power_down - Shut machine down for hibernate. * @mode: Suspend-to-disk mode @@ -99,6 +102,7 @@ static void finish(void) { device_resume(); platform_finish(); + enable_nonboot_cpus(); thaw_processes(); pm_restore_console(); } @@ -126,6 +130,7 @@ static int prepare(void) /* Free memory before shutting down devices. */ free_some_memory(); + disable_nonboot_cpus(); if ((error = device_suspend(PM_SUSPEND_DISK))) goto Finish; @@ -133,6 +138,7 @@ static int prepare(void) Finish: platform_finish(); Thaw: + enable_nonboot_cpus(); thaw_processes(); pm_restore_console(); return error; @@ -188,7 +194,7 @@ int pm_suspend_disk(void) /** - * pm_resume - Resume from a saved image. + * software_resume - Resume from a saved image. * * Called as a late_initcall (so all devices are discovered and * initialized), we call pmdisk to see if we have a saved image or not. @@ -199,10 +205,18 @@ int pm_suspend_disk(void) * */ -static int pm_resume(void) +static int software_resume(void) { int error; + if (noresume) { + /** + * FIXME: If noresume is specified, we need to find the partition + * and reset it back to normal swap space. + */ + return 0; + } + pr_debug("PM: Reading pmdisk image.\n"); if ((error = swsusp_read())) @@ -216,16 +230,6 @@ static int pm_resume(void) barrier(); mb(); - /* FIXME: The following (comment and mdelay()) are from swsusp. - * Are they really necessary? - * - * We do not want some readahead with DMA to corrupt our memory, right? - * Do it with disabled interrupts for best effect. That way, if some - * driver scheduled DMA, we have good chance for DMA to finish ;-). - */ - pr_debug("PM: Waiting for DMAs to settle down.\n"); - mdelay(1000); - pr_debug("PM: Restoring saved image.\n"); swsusp_resume(); pr_debug("PM: Restore failed, recovering.n"); @@ -237,7 +241,7 @@ static int pm_resume(void) return 0; } -late_initcall(pm_resume); +late_initcall(software_resume); static char * pm_disk_modes[] = { @@ -336,3 +340,22 @@ static int __init pm_disk_init(void) } core_initcall(pm_disk_init); + + +static int __init resume_setup(char *str) +{ + if (noresume) + return 1; + + strncpy( resume_file, str, 255 ); + return 1; +} + +static int __init noresume_setup(char *str) +{ + noresume = 1; + return 1; +} + +__setup("noresume", noresume_setup); +__setup("resume=", resume_setup); diff --git a/kernel/power/main.c b/kernel/power/main.c index d582906fecc6..3e37ade640f6 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -169,6 +169,21 @@ static int enter_state(u32 state) return error; } +/* + * This is main interface to the outside world. It needs to be + * called from process context. + */ +int software_suspend(void) +{ + int error; + + if (down_trylock(&pm_sem)) + return -EBUSY; + error = pm_suspend_disk(); + up(&pm_sem); + return error; +} + /** * pm_suspend - Externally visible function for suspending system. diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 74805928f5d8..e478c7e37bc6 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -71,11 +71,6 @@ #include "power.h" -unsigned char software_suspend_enabled = 0; - -#define NORESUME 1 -#define RESUME_SPECIFIED 2 - /* References to section boundaries */ extern char __nosave_begin, __nosave_end; @@ -85,8 +80,7 @@ extern int is_head_of_free_region(struct page *); int pagedir_order_check; int nr_copy_pages_check; -static int resume_status; -static char resume_file[256] = CONFIG_PM_STD_PARTITION; +extern char resume_file[]; static dev_t resume_device; /* Local variables that should not be affected by save */ unsigned int nr_copy_pages __nosavedata = 0; @@ -664,30 +658,6 @@ void swsusp_free(void) free_pages(p, pagedir_order); } -static int prepare_suspend_processes(void) -{ - sys_sync(); /* Syncing needs pdflushd, so do it before stopping processes */ - if (freeze_processes()) { - printk( KERN_ERR "Suspend failed: Not all processes stopped!\n" ); - thaw_processes(); - return 1; - } - return 0; -} - -/* - * Try to free as much memory as possible, but do not OOM-kill anyone - * - * Notice: all userland should be stopped at this point, or livelock is possible. - */ -static void free_some_memory(void) -{ - printk("Freeing memory: "); - while (shrink_all_memory(10000)) - printk("."); - printk("|\n"); -} - static void calc_order(void) { @@ -878,48 +848,6 @@ int swsusp_write(void) } -static void suspend_power_down(void) -{ - extern int C_A_D; - C_A_D = 0; - printk(KERN_EMERG "%s%s Trying to power down.\n", name_suspend, TEST_SWSUSP ? "Disable TEST_SWSUSP. NOT ": ""); -#ifdef CONFIG_VT - PRINTK(KERN_EMERG "shift_state: %04x\n", shift_state); - mdelay(1000); - if (TEST_SWSUSP ^ (!!(shift_state & (1 << KG_CTRL)))) - machine_restart(NULL); - else -#endif - { - device_suspend(3); - device_shutdown(); - machine_power_off(); - } - - printk(KERN_EMERG "%sProbably not capable for powerdown. System halted.\n", name_suspend); - machine_halt(); - while (1); - /* NOTREACHED */ -} - - -static void suspend_finish(void) -{ - free_pages((unsigned long) pagedir_nosave, pagedir_order); - -#ifdef CONFIG_HIGHMEM - printk( "Restoring highmem\n" ); - restore_highmem(); -#endif - device_resume(); - -#ifdef SUSPEND_CONSOLE - acquire_console_sem(); - update_screen(fg_console); - release_console_sem(); -#endif -} - extern asmlinkage int swsusp_arch_suspend(void); extern asmlinkage int swsusp_arch_resume(void); @@ -971,59 +899,6 @@ int swsusp_resume(void) -static int in_suspend __nosavedata = 0; - -/* - * This is main interface to the outside world. It needs to be - * called from process context. - */ -int software_suspend(void) -{ - int res; - if (!software_suspend_enabled) - return -EAGAIN; - - software_suspend_enabled = 0; - might_sleep(); - - if (arch_prepare_suspend()) { - printk("%sArchitecture failed to prepare\n", name_suspend); - return -EPERM; - } - if (pm_prepare_console()) - printk( "%sCan't allocate a console... proceeding\n", name_suspend); - if (!prepare_suspend_processes()) { - - /* At this point, all user processes and "dangerous" - kernel threads are stopped. Free some memory, as we - need half of memory free. */ - - free_some_memory(); - disable_nonboot_cpus(); - /* Save state of all device drivers, and stop them. */ - printk("Suspending devices... "); - if ((res = device_suspend(3))==0) { - in_suspend = 1; - - res = swsusp_save(); - - if (!res && in_suspend) { - swsusp_write(); - suspend_power_down(); - } - in_suspend = 0; - suspend_finish(); - } - thaw_processes(); - enable_nonboot_cpus(); - } else - res = -EBUSY; - software_suspend_enabled = 1; - MDELAY(1000); - pm_restore_console(); - return res; -} - /* More restore stuff */ #define does_collide(addr) does_collide_order(pagedir_nosave, addr, 0) @@ -1370,76 +1245,3 @@ int __init swsusp_read(void) pr_debug("pmdisk: Error %d resuming\n", error); return error; } - -/** - * software_resume - Resume from a saved image. - * - * Called as a late_initcall (so all devices are discovered and - * initialized), we call swsusp to see if we have a saved image or not. - * If so, we quiesce devices, then restore the saved image. We will - * return above (in pm_suspend_disk() ) if everything goes well. - * Otherwise, we fail gracefully and return to the normally - * scheduled program. - * - */ -static int __init software_resume(void) -{ - if (num_online_cpus() > 1) { - printk(KERN_WARNING "Software Suspend has malfunctioning SMP support. Disabled :(\n"); - return -EINVAL; - } - /* We enable the possibility of machine suspend */ - software_suspend_enabled = 1; - if (!resume_status) - return 0; - - printk( "%s", name_resume ); - if (resume_status == NORESUME) { - /* - * FIXME: If noresume is specified, we need to handle by finding - * the right partition and resettting the signature. - */ - return 0; - } - MDELAY(1000); - - if (pm_prepare_console()) - printk("swsusp: Can't allocate a console... proceeding\n"); - - if (swsusp_read()) - goto read_failure; - /* FIXME: Should we stop processes here, just to be safer? */ - disable_nonboot_cpus(); - device_suspend(3); - swsusp_resume(); - panic("This never returns"); - -read_failure: - pm_restore_console(); - return 0; -} - -late_initcall(software_resume); - -static int __init resume_setup(char *str) -{ - if (resume_status == NORESUME) - return 1; - - strncpy( resume_file, str, 255 ); - resume_status = RESUME_SPECIFIED; - - return 1; -} - -static int __init noresume_setup(char *str) -{ - resume_status = NORESUME; - return 1; -} - -__setup("noresume", noresume_setup); -__setup("resume=", resume_setup); - -EXPORT_SYMBOL(software_suspend); -EXPORT_SYMBOL(software_suspend_enabled); -- cgit v1.2.3 From f7fec5c0f340890755352b1003e1d14ae8c4c21a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 19 Sep 2004 09:26:01 -0300 Subject: [NET] Calculate ipv6_pinfo offset from struct proto->slab_obj_size With a new rule for the struct sock hierarchy descendants layout, that states that the struct ipv6_pinfo member should be last one in the struct layout (see tcp6_sock, udp6_sock, sctp6_sock, etc), we can calculate the ipv6_pinfo member offset by using struct proto->slab_obj_size. So ditch struct ipv6_sk_offset and struct proto->af_specific. Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: David S. Miller --- include/linux/ipv6.h | 15 +++++++++------ include/net/sock.h | 1 - net/ipv6/af_inet6.c | 4 ++-- net/ipv6/raw.c | 5 ----- net/ipv6/tcp_ipv6.c | 5 ----- net/ipv6/udp.c | 5 ----- net/sctp/socket.c | 5 ----- 7 files changed, 11 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 55aba976aba6..d7d1673880d5 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -182,8 +182,7 @@ enum { as offsets from skb->nh. */ -struct inet6_skb_parm -{ +struct inet6_skb_parm { int iif; __u16 ra; __u16 hop; @@ -194,6 +193,14 @@ struct inet6_skb_parm #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) +/** + * struct ipv6_pinfo - ipv6 private area + * + * In the struct sock hierarchy (tcp6_sock, upd6_sock, etc) + * this _must_ be the last member, so that inet6_sk_generic + * is able to calculate its offset from the base struct sock + * by using the struct proto->slab_obj_size member. -acme + */ struct ipv6_pinfo { struct in6_addr saddr; struct in6_addr rcv_saddr; @@ -282,10 +289,6 @@ static inline struct raw6_opt * raw6_sk(const struct sock *__sk) return &((struct raw6_sock *)__sk)->raw6; } -struct ipv6_sk_offset { - int offset; -}; - #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #define __ipv6_only_sock(sk) (inet6_sk(sk)->ipv6only) #define ipv6_only_sock(sk) ((sk)->sk_family == PF_INET6 && __ipv6_only_sock(sk)) diff --git a/include/net/sock.h b/include/net/sock.h index e9a5eb5d1840..e47031cf0a51 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -557,7 +557,6 @@ struct proto { kmem_cache_t *slab; int slab_obj_size; - void *af_specific; char name[32]; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 09b5c0071a85..bd437d9300a5 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -107,9 +107,9 @@ static void inet6_sock_destruct(struct sock *sk) static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) { - const struct ipv6_sk_offset *offset = sk->sk_prot->af_specific; + const int offset = sk->sk_prot->slab_obj_size - sizeof(struct ipv6_pinfo); - return (struct ipv6_pinfo *)(((u8 *)sk) + offset->offset); + return (struct ipv6_pinfo *)(((u8 *)sk) + offset); } static int inet6_create(struct socket *sock, int protocol) diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 6821f31ee06d..c73d720114fa 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -973,10 +973,6 @@ static int rawv6_init_sk(struct sock *sk) return(0); } -struct ipv6_sk_offset raw_sock_offset = { - .offset = offsetof(struct raw6_sock, inet6), -}; - struct proto rawv6_prot = { .name = "RAW", .close = rawv6_close, @@ -994,7 +990,6 @@ struct proto rawv6_prot = { .hash = raw_v6_hash, .unhash = raw_v6_unhash, .slab_obj_size = sizeof(struct raw6_sock), - .af_specific = &raw_sock_offset, }; #ifdef CONFIG_PROC_FS diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1410650e38b8..d5484b15e5c0 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -2120,10 +2120,6 @@ void tcp6_proc_exit(void) } #endif -struct ipv6_sk_offset tcp_sock_offset = { - .offset = offsetof(struct tcp6_sock, inet6), -}; - struct proto tcpv6_prot = { .name = "TCPv6", .close = tcp_close, @@ -2151,7 +2147,6 @@ struct proto tcpv6_prot = { .sysctl_rmem = sysctl_tcp_rmem, .max_header = MAX_TCP_HEADER, .slab_obj_size = sizeof(struct tcp6_sock), - .af_specific = &tcp_sock_offset, }; static struct inet6_protocol tcpv6_protocol = { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index a1fa658fcf2e..ff00dc49cdd8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1031,10 +1031,6 @@ void udp6_proc_exit(void) { /* ------------------------------------------------------------------------ */ -struct ipv6_sk_offset udp_sock_offset = { - .offset = offsetof(struct udp6_sock, inet6), -}; - struct proto udpv6_prot = { .name = "UDP", .close = udpv6_close, @@ -1051,7 +1047,6 @@ struct proto udpv6_prot = { .unhash = udp_v6_unhash, .get_port = udp_v6_get_port, .slab_obj_size = sizeof(struct udp6_sock), - .af_specific = &udp_sock_offset, }; extern struct proto_ops inet6_dgram_ops; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 89ea13bf5105..0d1bc4507d6b 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4626,10 +4626,6 @@ struct proto sctp_prot = { }; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -struct ipv6_sk_offset sctp_sock_offset = { - .offset = offsetof(struct sctp6_sock, inet6), -}; - struct proto sctpv6_prot = { .name = "SCTPv6", .close = sctp_close, @@ -4650,6 +4646,5 @@ struct proto sctpv6_prot = { .unhash = sctp_unhash, .get_port = sctp_get_port, .slab_obj_size = sizeof(struct sctp6_sock), - .af_specific = &sctp_sock_offset, }; #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ -- cgit v1.2.3 From d5df4e65d9b5a9a46ff174ca9e87d5332bf65afb Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Fri, 24 Sep 2004 04:51:10 -0700 Subject: [power mgmt] Make system state enums match device state values. Changes the PM_SUSPEND_MEM (and PM_SUSPEND_DISK) enum values so that they make sense as PCI device power states. (a) Fixes bugs whereby PCI drivers are being given bogus values. This should resolve OSDL bugid 2886 without changing the PCI API (its PM calls still act as on 2.4 kernels). (b) Doesn't change the awkward assumption in the 2.6 PMcore that the /sys/bus/*/devices/power/state, /proc/acpi/sleep, dev->power.power_state, and dev->detach_state files share the same numeric codes ... even for busses very unlike PCI, or systems with several "on" policies as well as STD and STR, or with device-PM transtions with no system-wide equivalent. Really we need to move away from "u32" codes that are easily confused with each other, towards typed values (probably struct pointers), but that's a long-term change and we need the PCI issue fixed sooner. Signed-off-by: David Brownell --- include/linux/pm.h | 11 ++++++----- kernel/power/main.c | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pm.h b/include/linux/pm.h index d54bc441daff..7bfd2d43963e 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -194,11 +194,12 @@ extern void (*pm_idle)(void); extern void (*pm_power_off)(void); enum { - PM_SUSPEND_ON, - PM_SUSPEND_STANDBY, - PM_SUSPEND_MEM, - PM_SUSPEND_DISK, - PM_SUSPEND_MAX, + PM_SUSPEND_ON = 0, + PM_SUSPEND_STANDBY = 1, + /* NOTE: PM_SUSPEND_MEM == PCI_D3hot */ + PM_SUSPEND_MEM = 3, + PM_SUSPEND_DISK = 4, + PM_SUSPEND_MAX = 5, }; enum { diff --git a/kernel/power/main.c b/kernel/power/main.c index 6b5c59cc26ba..2f08a432f0d5 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -230,8 +230,8 @@ static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n p = memchr(buf, '\n', n); len = p ? p - buf : n; - for (s = &pm_states[state]; *s; s++, state++) { - if (!strncmp(buf, *s, len)) + for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { + if (*s && !strncmp(buf, *s, len)) break; } if (*s) -- cgit v1.2.3 From 9192a60bd5cadea1425bd408bab053a8556ff928 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 26 Sep 2004 17:23:19 -0700 Subject: [PATCH] Add wait_event_timeout() There appears to be one case missing from the wait_event() family - the uninterruptible timeout wait. The following patch adds this. This wait is particularly useful when (eg) you wish to pass work off to an interrupt handler to perform, but also want to know if the hardware has decided to go gaga under you. You don't want to sit around waiting for something that'll never happen - you want to go and wack the gremlin which caused the failure and retry. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/wait.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'include/linux') diff --git a/include/linux/wait.h b/include/linux/wait.h index 4417f800a639..21cd4df67b24 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -156,6 +156,29 @@ do { \ __wait_event(wq, condition); \ } while (0) +#define __wait_event_timeout(wq, condition, ret) \ +do { \ + DEFINE_WAIT(__wait); \ + \ + for (;;) { \ + prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ + if (condition) \ + break; \ + ret = schedule_timeout(ret); \ + if (!ret) \ + break; \ + } \ + finish_wait(&wq, &__wait); \ +} while (0) + +#define wait_event_timeout(wq, condition, timeout) \ +({ \ + long __ret = timeout; \ + if (!(condition)) \ + __wait_event_timeout(wq, condition, __ret); \ + __ret; \ +}) + #define __wait_event_interruptible(wq, condition, ret) \ do { \ DEFINE_WAIT(__wait); \ -- cgit v1.2.3 From 297c2ba23e5146b5380be0f2153c30e9a8783f61 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 27 Sep 2004 03:13:21 +0200 Subject: [NETFILTER]: Fill hole in netfilter skb fields on 64bit Signed-off-by: Patrick McHardy --- include/linux/skbuff.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 66a56c3d2436..b6c6ef59ab16 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -250,8 +250,8 @@ struct sk_buff { #ifdef CONFIG_NETFILTER unsigned long nfmark; __u32 nfcache; - struct nf_conntrack *nfct; __u32 nfctinfo; + struct nf_conntrack *nfct; #ifdef CONFIG_NETFILTER_DEBUG unsigned int nf_debug; #endif -- cgit v1.2.3 From c6932918fdd92db49fb875eca51db0a3860928f8 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sun, 26 Sep 2004 21:58:11 -0700 Subject: [TCP]: Choose congestion algorithm at initialization. The choice of congestion algorithm needs to be made when connection is setup to avoid problems when the sysctl values change later and the necessary data hasn't been collected. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/tcp.h | 10 ++++++++-- include/net/tcp.h | 28 +++++++++++++++------------- net/ipv4/tcp_input.c | 21 ++++++++++++--------- net/ipv4/tcp_minisocks.c | 3 ++- net/ipv4/tcp_output.c | 4 ++-- 5 files changed, 39 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index c03df4894379..f374df7a823f 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -205,6 +205,13 @@ typedef struct tcp_pcount { __u32 val; } tcp_pcount_t; +enum tcp_congestion_algo { + TCP_RENO=0, + TCP_VEGAS, + TCP_WESTWOOD, + TCP_BIC, +}; + struct tcp_opt { int tcp_header_len; /* Bytes of tcp header to send */ @@ -265,7 +272,7 @@ struct tcp_opt { __u8 frto_counter; /* Number of new acks after RTO */ __u32 frto_highmark; /* snd_nxt when RTO occurred */ - __u8 unused_pad; + __u8 adv_cong; /* Using Vegas, Westwood, or BIC */ __u8 defer_accept; /* User waits for some data after accept() */ /* one byte hole, try to pack */ @@ -412,7 +419,6 @@ struct tcp_opt { __u32 beg_snd_nxt; /* right edge during last RTT */ __u32 beg_snd_una; /* left edge during last RTT */ __u32 beg_snd_cwnd; /* saves the size of the cwnd */ - __u8 do_vegas; /* do vegas for this connection */ __u8 doing_vegas_now;/* if true, do vegas for this RTT */ __u16 cntRTT; /* # of RTTs measured within last RTT */ __u32 minRTT; /* min of RTTs measured within last RTT (in usec) */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 63ed0f745a8d..eb0f1970a40f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1271,6 +1271,13 @@ static __inline__ unsigned int tcp_packets_in_flight(struct tcp_opt *tp) tcp_get_pcount(&tp->retrans_out)); } +/* + * Which congestion algorithim is in use on the connection. + */ +#define tcp_is_vegas(__tp) ((__tp)->adv_cong == TCP_VEGAS) +#define tcp_is_westwood(__tp) ((__tp)->adv_cong == TCP_WESTWOOD) +#define tcp_is_bic(__tp) ((__tp)->adv_cong == TCP_BIC) + /* Recalculate snd_ssthresh, we want to set it to: * * Reno: @@ -1283,7 +1290,7 @@ static __inline__ unsigned int tcp_packets_in_flight(struct tcp_opt *tp) */ static inline __u32 tcp_recalc_ssthresh(struct tcp_opt *tp) { - if (sysctl_tcp_bic) { + if (tcp_is_bic(tp)) { if (sysctl_tcp_bic_fast_convergence && tp->snd_cwnd < tp->bictcp.last_max_cwnd) tp->bictcp.last_max_cwnd @@ -1302,11 +1309,6 @@ static inline __u32 tcp_recalc_ssthresh(struct tcp_opt *tp) /* Stop taking Vegas samples for now. */ #define tcp_vegas_disable(__tp) ((__tp)->vegas.doing_vegas_now = 0) - -/* Is this TCP connection using Vegas (regardless of whether it is taking - * Vegas measurements at the current time)? - */ -#define tcp_is_vegas(__tp) ((__tp)->vegas.do_vegas) static inline void tcp_vegas_enable(struct tcp_opt *tp) { @@ -1340,7 +1342,7 @@ static inline void tcp_vegas_enable(struct tcp_opt *tp) /* Should we be taking Vegas samples right now? */ #define tcp_vegas_enabled(__tp) ((__tp)->vegas.doing_vegas_now) -extern void tcp_vegas_init(struct tcp_opt *tp); +extern void tcp_ca_init(struct tcp_opt *tp); static inline void tcp_set_ca_state(struct tcp_opt *tp, u8 ca_state) { @@ -2024,7 +2026,7 @@ extern void tcp_proc_unregister(struct tcp_seq_afinfo *afinfo); static inline void tcp_westwood_update_rtt(struct tcp_opt *tp, __u32 rtt_seq) { - if (sysctl_tcp_westwood) + if (tcp_is_westwood(tp)) tp->westwood.rtt = rtt_seq; } @@ -2033,13 +2035,13 @@ void __tcp_westwood_slow_bw(struct sock *, struct sk_buff *); static inline void tcp_westwood_fast_bw(struct sock *sk, struct sk_buff *skb) { - if (sysctl_tcp_westwood) + if (tcp_is_westwood(tcp_sk(sk))) __tcp_westwood_fast_bw(sk, skb); } static inline void tcp_westwood_slow_bw(struct sock *sk, struct sk_buff *skb) { - if (sysctl_tcp_westwood) + if (tcp_is_westwood(tcp_sk(sk))) __tcp_westwood_slow_bw(sk, skb); } @@ -2052,14 +2054,14 @@ static inline __u32 __tcp_westwood_bw_rttmin(const struct tcp_opt *tp) static inline __u32 tcp_westwood_bw_rttmin(const struct tcp_opt *tp) { - return sysctl_tcp_westwood ? __tcp_westwood_bw_rttmin(tp) : 0; + return tcp_is_westwood(tp) ? __tcp_westwood_bw_rttmin(tp) : 0; } static inline int tcp_westwood_ssthresh(struct tcp_opt *tp) { __u32 ssthresh = 0; - if (sysctl_tcp_westwood) { + if (tcp_is_westwood(tp)) { ssthresh = __tcp_westwood_bw_rttmin(tp); if (ssthresh) tp->snd_ssthresh = ssthresh; @@ -2072,7 +2074,7 @@ static inline int tcp_westwood_cwnd(struct tcp_opt *tp) { __u32 cwnd = 0; - if (sysctl_tcp_westwood) { + if (tcp_is_westwood(tp)) { cwnd = __tcp_westwood_bw_rttmin(tp); if (cwnd) tp->snd_cwnd = cwnd; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 1c53442639ae..8a2968840be4 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -555,17 +555,20 @@ static void tcp_event_data_recv(struct sock *sk, struct tcp_opt *tp, struct sk_b tcp_grow_window(sk, tp, skb); } -/* Set up a new TCP connection, depending on whether it should be - * using Vegas or not. - */ -void tcp_vegas_init(struct tcp_opt *tp) +/* When starting a new connection, pin down the current choice of + * congestion algorithm. + */ +void tcp_ca_init(struct tcp_opt *tp) { - if (sysctl_tcp_vegas_cong_avoid) { - tp->vegas.do_vegas = 1; + if (sysctl_tcp_westwood) + tp->adv_cong = TCP_WESTWOOD; + else if (sysctl_tcp_bic) + tp->adv_cong = TCP_BIC; + else if (sysctl_tcp_vegas_cong_avoid) { + tp->adv_cong = TCP_VEGAS; tp->vegas.baseRTT = 0x7fffffff; tcp_vegas_enable(tp); - } else - tcp_vegas_disable(tp); + } } /* Do RTT sampling needed for Vegas. @@ -2039,7 +2042,7 @@ tcp_ack_update_rtt(struct tcp_opt *tp, int flag, s32 seq_rtt) static inline __u32 bictcp_cwnd(struct tcp_opt *tp) { /* orignal Reno behaviour */ - if (!sysctl_tcp_bic) + if (!tcp_is_bic(tp)) return tp->snd_cwnd; if (tp->bictcp.last_cwnd == tp->snd_cwnd && diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index c526f9dd97a7..2bdc1975c319 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -841,7 +841,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, if (newtp->ecn_flags&TCP_ECN_OK) newsk->sk_no_largesend = 1; - tcp_vegas_init(newtp); + tcp_ca_init(newtp); + TCP_INC_STATS_BH(TCP_MIB_PASSIVEOPENS); } return newsk; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index e37d16e019e0..cb3d52a67c26 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1359,7 +1359,7 @@ static inline void tcp_connect_init(struct sock *sk) tp->window_clamp = dst_metric(dst, RTAX_WINDOW); tp->advmss = dst_metric(dst, RTAX_ADVMSS); tcp_initialize_rcv_mss(sk); - tcp_vegas_init(tp); + tcp_ca_init(tp); tcp_select_initial_window(tcp_full_space(sk), tp->advmss - (tp->ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0), @@ -1411,7 +1411,7 @@ int tcp_connect(struct sock *sk) TCP_SKB_CB(buff)->end_seq = tp->write_seq; tp->snd_nxt = tp->write_seq; tp->pushed_seq = tp->write_seq; - tcp_vegas_init(tp); + tcp_ca_init(tp); /* Send it off. */ TCP_SKB_CB(buff)->when = tcp_time_stamp; -- cgit v1.2.3 From e76e875474132757526782cf4b17a19fde457e81 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sun, 26 Sep 2004 23:59:14 -0700 Subject: [TIME]: Put jiffies_to_usecs in time.h Move local version put in tcp_diag.c into time.h where it belongs. Also, make it smarted about HZ values math. Based upon suggestions from Joe Perches Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/time.h | 12 ++++++++++++ net/ipv4/tcp_diag.c | 6 ------ 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/time.h b/include/linux/time.h index de41e12bbbff..8bccdf73a97f 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -194,6 +194,18 @@ static inline unsigned int jiffies_to_msecs(const unsigned long j) return (j * 1000) / HZ; #endif } + +static inline unsigned int jiffies_to_usecs(const unsigned long j) +{ +#if HZ <= 1000 && !(1000 % HZ) + return (1000000 / HZ) * j; +#elif HZ > 1000 && !(HZ % 1000) + return (j*1000 + (HZ - 1000))/(HZ / 1000); +#else + return (j * 1000000) / HZ; +#endif +} + static inline unsigned long msecs_to_jiffies(const unsigned int m) { #if HZ <= 1000 && !(1000 % HZ) diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 4f7cde805c1c..4b2fe8a86e8d 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -41,12 +41,6 @@ static struct sock *tcpnl; rta->rta_len = rtalen; \ RTA_DATA(rta); }) -static inline unsigned int jiffies_to_usecs(const unsigned long j) -{ - return 1000*jiffies_to_msecs(j); -} - - /* Return information about state of tcp endpoint in API format. */ void tcp_get_info(struct sock *sk, struct tcp_info *info) { -- cgit v1.2.3 From ac0c3bb0558d36c5ac900930bdc70bcaf0f06cf4 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Wed, 29 Sep 2004 10:05:50 +1000 Subject: [XFS] Update XFS quota header - add macros, use standard gpl template. Signed-off-by: Nathan Scott --- include/linux/dqblk_xfs.h | 63 +++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dqblk_xfs.h b/include/linux/dqblk_xfs.h index 330f23e0c6d1..bf2d65765189 100644 --- a/include/linux/dqblk_xfs.h +++ b/include/linux/dqblk_xfs.h @@ -1,34 +1,22 @@ /* - * Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 1995-2001,2004 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2.1 of the GNU Lesser General Public License * as published by the Free Software Foundation. * - * This program is distributed in the hope that it would be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * Further, this software is distributed without any warranty that it is - * free of the rightful claim of any third person regarding infringement - * or the like. Any license provided herein, whether implied or - * otherwise, applies only to this software file. Patent licenses, if - * any, provided herein do not apply to combinations of this program with - * other software, or any other product whatsoever. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, - * USA. - * - * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, - * Mountain View, CA 94043, or: - * - * http://www.sgi.com - * - * For further information regarding this notice, see: - * - * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + * Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, + * Mountain View, CA 94043, USA, or: http://www.sgi.com */ #ifndef _LINUX_DQBLK_XFS_H #define _LINUX_DQBLK_XFS_H @@ -40,12 +28,12 @@ */ #define XQM_CMD(x) (('X'<<8)+(x)) /* note: forms first QCMD argument */ -#define Q_XQUOTAON XQM_CMD(0x1) /* enable accounting/enforcement */ -#define Q_XQUOTAOFF XQM_CMD(0x2) /* disable accounting/enforcement */ -#define Q_XGETQUOTA XQM_CMD(0x3) /* get disk limits and usage */ -#define Q_XSETQLIM XQM_CMD(0x4) /* set disk limits */ -#define Q_XGETQSTAT XQM_CMD(0x5) /* get quota subsystem status */ -#define Q_XQUOTARM XQM_CMD(0x6) /* free disk space used by dquots */ +#define Q_XQUOTAON XQM_CMD(1) /* enable accounting/enforcement */ +#define Q_XQUOTAOFF XQM_CMD(2) /* disable accounting/enforcement */ +#define Q_XGETQUOTA XQM_CMD(3) /* get disk limits and usage */ +#define Q_XSETQLIM XQM_CMD(4) /* set disk limits */ +#define Q_XGETQSTAT XQM_CMD(5) /* get quota subsystem status */ +#define Q_XQUOTARM XQM_CMD(6) /* free disk space used by dquots */ /* * fs_disk_quota structure: @@ -104,6 +92,19 @@ typedef struct fs_disk_quota { #define FS_DQ_RTBTIMER (1<<8) #define FS_DQ_TIMER_MASK (FS_DQ_BTIMER | FS_DQ_ITIMER | FS_DQ_RTBTIMER) +/* + * Warning counts are set in both super user's dquot and others. For others, + * warnings are set/cleared by the administrators (or automatically by going + * below the soft limit). Superusers warning values set the warning limits + * for the rest. In case these values are zero, the DQ_{F,B}WARNLIMIT values + * defined below are used. + * These values also apply only to the d_fieldmask field for Q_XSETQLIM. + */ +#define FS_DQ_BWARNS (1<<9) +#define FS_DQ_IWARNS (1<<10) +#define FS_DQ_RTBWARNS (1<<11) +#define FS_DQ_WARNS_MASK (FS_DQ_BWARNS | FS_DQ_IWARNS | FS_DQ_RTBWARNS) + /* * Various flags related to quotactl(2). Only relevant to XFS filesystems. */ @@ -111,9 +112,11 @@ typedef struct fs_disk_quota { #define XFS_QUOTA_UDQ_ENFD (1<<1) /* user quota limits enforcement */ #define XFS_QUOTA_GDQ_ACCT (1<<2) /* group quota accounting */ #define XFS_QUOTA_GDQ_ENFD (1<<3) /* group quota limits enforcement */ +#define XFS_QUOTA_PDQ_ACCT (1<<4) /* project quota accounting */ +#define XFS_QUOTA_PDQ_ENFD (1<<5) /* project quota limits enforcement */ #define XFS_USER_QUOTA (1<<0) /* user quota type */ -#define XFS_PROJ_QUOTA (1<<1) /* (IRIX) project quota type */ +#define XFS_PROJ_QUOTA (1<<1) /* project quota type */ #define XFS_GROUP_QUOTA (1<<2) /* group quota type */ /* -- cgit v1.2.3 From 6ad5331ee3c59d40eb8a52078823a0921c83dfa8 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 28 Sep 2004 00:22:31 -0700 Subject: [NET]: Generic network statistics This patch moves the following files in /proc: /proc/net/rt_cache_stat /proc/net/stat/rt_cache /proc/net/ip_conntrack_stat /proc/net/stat/ip_conntrack /proc/net/arp_cache_stat /proc/net/stat/arp_cache /proc/net/clip_arp_cache_stat /proc/net/stat/clip_arp_cache /proc/net/dn_neigh_cache_stat /proc/net/stat/dn_neigh_cache This allows a generic statistics tool to scan for all available statistics by doing readdir(2) on /proc/net/stat It also adds a special first 'template' line to rt_cache and ip_conntrack in order to facilitate compatibility once somebody adds new fields to the output lines. WARNING: This breaks existing rtstat.c and ctstat.c userspace programs (hopefully for the last time). rtstat is non-existant or broken in major distributions anyway, and ctstat is too new for any distros having it picked up. Therefore, we justify this breakage. A new unified statistics tool for routing cache, connection tracking and neighbour cache is under development and will be included with iproute2. Signed-off-by: Harald Welte Signed-off-by: David S. Miller --- fs/proc/root.c | 5 ++++- include/linux/proc_fs.h | 1 + net/core/neighbour.c | 13 +------------ net/ipv4/netfilter/ip_conntrack_standalone.c | 21 +++++++++++++++------ net/ipv4/route.c | 24 ++++++++++++++++++------ 5 files changed, 39 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/root.c b/fs/proc/root.c index 4ece27d02514..6151f0592f28 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -18,7 +18,7 @@ #include #include -struct proc_dir_entry *proc_net, *proc_bus, *proc_root_fs, *proc_root_driver; +struct proc_dir_entry *proc_net, *proc_net_stat, *proc_bus, *proc_root_fs, *proc_root_driver; #ifdef CONFIG_SYSCTL struct proc_dir_entry *proc_sys_root; @@ -53,6 +53,8 @@ void __init proc_root_init(void) } proc_misc_init(); proc_net = proc_mkdir("net", NULL); + proc_net_stat = proc_mkdir("net/stat", NULL); + #ifdef CONFIG_SYSVIPC proc_mkdir("sysvipc", NULL); #endif @@ -157,5 +159,6 @@ EXPORT_SYMBOL(remove_proc_entry); EXPORT_SYMBOL(proc_root); EXPORT_SYMBOL(proc_root_fs); EXPORT_SYMBOL(proc_net); +EXPORT_SYMBOL(proc_net_stat); EXPORT_SYMBOL(proc_bus); EXPORT_SYMBOL(proc_root_driver); diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index abcfc4bca268..c83dd15b728c 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -79,6 +79,7 @@ struct kcore_list { extern struct proc_dir_entry proc_root; extern struct proc_dir_entry *proc_root_fs; extern struct proc_dir_entry *proc_net; +extern struct proc_dir_entry *proc_net_stat; extern struct proc_dir_entry *proc_bus; extern struct proc_dir_entry *proc_root_driver; extern struct proc_dir_entry *proc_root_kcore; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 5f4fca15aa94..a5509ef5570e 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1334,22 +1334,11 @@ void neigh_table_init(struct neigh_table *tbl) panic("cannot create neighbour cache statistics"); #ifdef CONFIG_PROC_FS -#define NC_STAT_SUFFIX "_stat" - { - char *proc_stat_name; - proc_stat_name = kmalloc(strlen(tbl->id) + - strlen(NC_STAT_SUFFIX) + 1, GFP_KERNEL); - if (!proc_stat_name) - panic("cannot allocate neighbour cache proc name buffer"); - strcpy(proc_stat_name, tbl->id); - strcat(proc_stat_name, NC_STAT_SUFFIX); - - tbl->pde = create_proc_entry(proc_stat_name, 0, proc_net); + tbl->pde = create_proc_entry(tbl->id, 0, proc_net_stat); if (!tbl->pde) panic("cannot create neighbour proc dir entry"); tbl->pde->proc_fops = &neigh_stat_seq_fops; tbl->pde->data = tbl; - } #endif tbl->hash_mask = 1; diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c index dbd569b58dcd..9abaf01a72cc 100644 --- a/net/ipv4/netfilter/ip_conntrack_standalone.c +++ b/net/ipv4/netfilter/ip_conntrack_standalone.c @@ -268,10 +268,13 @@ static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos) { int cpu; - for (cpu = *pos; cpu < NR_CPUS; ++cpu) { + if (*pos == 0) + return SEQ_START_TOKEN; + + for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) { if (!cpu_possible(cpu)) continue; - *pos = cpu; + *pos = cpu+1; return &per_cpu(ip_conntrack_stat, cpu); } @@ -282,10 +285,10 @@ static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) { int cpu; - for (cpu = *pos + 1; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos; cpu < NR_CPUS; ++cpu) { if (!cpu_possible(cpu)) continue; - *pos = cpu; + *pos = cpu+1; return &per_cpu(ip_conntrack_stat, cpu); } @@ -301,6 +304,11 @@ static int ct_cpu_seq_show(struct seq_file *seq, void *v) unsigned int nr_conntracks = atomic_read(&ip_conntrack_count); struct ip_conntrack_stat *st = v; + if (v == SEQ_START_TOKEN) { + seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete\n"); + return 0; + } + seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x " "%08x %08x %08x %08x %08x %08x %08x %08x \n", nr_conntracks, @@ -735,10 +743,11 @@ static int init_or_cleanup(int init) &exp_file_ops); if (!proc_exp) goto cleanup_proc; - proc_stat = proc_net_fops_create("ip_conntrack_stat", S_IRUGO, - &ct_cpu_seq_fops); + proc_stat = create_proc_entry("ip_conntrack", S_IRUGO, proc_net_stat); if (!proc_stat) goto cleanup_proc_exp; + + proc_stat->proc_fops = &ct_cpu_seq_fops; proc_stat->owner = THIS_MODULE; #endif diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 73b19d735c90..455cc0e77549 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -356,10 +356,13 @@ static void *rt_cpu_seq_start(struct seq_file *seq, loff_t *pos) { int cpu; - for (cpu = *pos; cpu < NR_CPUS; ++cpu) { + if (*pos == 0) + return SEQ_START_TOKEN; + + for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) { if (!cpu_possible(cpu)) continue; - *pos = cpu; + *pos = cpu+1; return per_cpu_ptr(rt_cache_stat, cpu); } return NULL; @@ -369,10 +372,10 @@ static void *rt_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) { int cpu; - for (cpu = *pos + 1; cpu < NR_CPUS; ++cpu) { + for (cpu = *pos; cpu < NR_CPUS; ++cpu) { if (!cpu_possible(cpu)) continue; - *pos = cpu; + *pos = cpu+1; return per_cpu_ptr(rt_cache_stat, cpu); } return NULL; @@ -387,6 +390,11 @@ static void rt_cpu_seq_stop(struct seq_file *seq, void *v) static int rt_cpu_seq_show(struct seq_file *seq, void *v) { struct rt_cache_stat *st = v; + + if (v == SEQ_START_TOKEN) { + seq_printf(seq, "entries in_hit in_slow_tot in_no_route in_brd in_martian_dst in_martian_src out_hit out_slow_tot out_slow_mc gc_total gc_ignored gc_goal_miss gc_dst_overflow in_hlist_search out_hlist_search\n"); + return 0; + } seq_printf(seq,"%08x %08x %08x %08x %08x %08x %08x %08x " " %08x %08x %08x %08x %08x %08x %08x %08x %08x \n", @@ -2783,12 +2791,16 @@ int __init ip_rt_init(void) add_timer(&rt_secret_timer); #ifdef CONFIG_PROC_FS + { + struct proc_dir_entry *rtstat_pde = NULL; /* keep gcc happy */ if (!proc_net_fops_create("rt_cache", S_IRUGO, &rt_cache_seq_fops) || - !proc_net_fops_create("rt_cache_stat", S_IRUGO, &rt_cpu_seq_fops)) { + !(rtstat_pde = create_proc_entry("rt_cache", S_IRUGO, + proc_net_stat))) { free_percpu(rt_cache_stat); return -ENOMEM; } - + rtstat_pde->proc_fops = &rt_cpu_seq_fops; + } #ifdef CONFIG_NET_CLS_ROUTE create_proc_read_entry("rt_acct", 0, proc_net, ip_rt_acct_read, NULL); #endif -- cgit v1.2.3 From 0f8d253f255a6cea81f34f746322ef48f3299276 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 29 Sep 2004 01:50:46 +0100 Subject: [PCMCIA] Add device ID for TI4520 to yenta_socket table. This fixes the non-functional PCMCIA problem reported on LKML by Dax Kelson. Signed-off-by: Russell King --- drivers/pcmcia/yenta_socket.c | 1 + include/linux/pci_ids.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include/linux') diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 87a95cc5d9c4..d6974a147b9d 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1091,6 +1091,7 @@ static struct pci_device_id yenta_table [] = { CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4410, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4450, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4451, TI12XX), + CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4520, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1250, TI1250), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1410, TI1250), diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index bc69e1f29b46..eed506d0196d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -734,6 +734,7 @@ #define PCI_DEVICE_ID_TI_1251B 0xac1f #define PCI_DEVICE_ID_TI_4410 0xac41 #define PCI_DEVICE_ID_TI_4451 0xac42 +#define PCI_DEVICE_ID_TI_4520 0xac46 #define PCI_DEVICE_ID_TI_1410 0xac50 #define PCI_DEVICE_ID_TI_1420 0xac51 #define PCI_DEVICE_ID_TI_1451A 0xac52 -- cgit v1.2.3 From 8f3f5de30c51738ea039ec0d655c158d0164dc05 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 29 Sep 2004 00:05:39 -0700 Subject: [PATCH] tty locking cleanup and fixes No problems reported other than Linus typo in an unbuildable driver from the last one. This one adds tty_ldisc_flush and also makes tty_wakeup do the queue wake as discussed with Paul. I've then propagated these functions through all the drivers. This means most drivers don't know about the ldisc locking and instead call generic functions that look after it. The result is the removal of a lot of duplicate code both for ldisc referencing and historical code for handling tty ldisc wakeup semantics. A nice side effect is that by adding versions of the two helpers we can switch most of the drivers over before changing the core tty code if preferred. Signed-off-by: Linus Torvalds --- Documentation/tty.txt | 194 +++++++++++ drivers/bluetooth/hci_ldisc.c | 8 +- drivers/char/amiserial.c | 12 +- drivers/char/cyclades.c | 12 +- drivers/char/epca.c | 21 +- drivers/char/esp.c | 13 +- drivers/char/generic_serial.c | 16 +- drivers/char/hvc_console.c | 5 +- drivers/char/hvcs.c | 5 +- drivers/char/isicom.c | 14 +- drivers/char/moxa.c | 16 +- drivers/char/mxser.c | 12 +- drivers/char/n_tty.c | 382 ++++++++++++++++++--- drivers/char/pcmcia/synclink_cs.c | 59 ++-- drivers/char/pcxx.c | 36 +- drivers/char/pty.c | 9 +- drivers/char/riscom8.c | 12 +- drivers/char/rocket.c | 26 +- drivers/char/selection.c | 5 + drivers/char/ser_a2232.c | 5 +- drivers/char/serial167.c | 22 +- drivers/char/serial_tx3912.c | 5 +- drivers/char/specialix.c | 15 +- drivers/char/stallion.c | 13 +- drivers/char/sx.c | 5 +- drivers/char/synclink.c | 53 +-- drivers/char/synclinkmp.c | 49 +-- drivers/char/tty_io.c | 637 ++++++++++++++++++++++++++++++----- drivers/char/tty_ioctl.c | 68 +++- drivers/char/viocons.c | 5 +- drivers/char/vme_scc.c | 8 +- drivers/char/vt_ioctl.c | 3 +- drivers/isdn/capi/capi.c | 90 ++--- drivers/isdn/i4l/isdn_tty.c | 21 +- drivers/macintosh/macserial.c | 16 +- drivers/net/hamradio/mkiss.c | 2 - drivers/net/irda/irtty-sir.c | 7 - drivers/net/ppp_async.c | 18 +- drivers/net/ppp_synctty.c | 2 + drivers/net/slip.c | 24 +- drivers/net/wan/pc300_tty.c | 35 +- drivers/net/wan/sdla_chdlc.c | 20 +- drivers/net/wireless/strip.c | 2 - drivers/s390/char/con3215.c | 10 +- drivers/s390/char/sclp_tty.c | 5 +- drivers/s390/char/sclp_vt220.c | 5 +- drivers/s390/net/ctctty.c | 12 +- drivers/sbus/char/aurora.c | 13 +- drivers/serial/68328serial.c | 17 +- drivers/serial/68360serial.c | 16 +- drivers/serial/mcfserial.c | 17 +- drivers/serial/serial_core.c | 26 +- drivers/tc/zs.c | 13 +- drivers/usb/class/bluetty.c | 11 +- drivers/usb/class/cdc-acm.c | 7 +- drivers/usb/serial/digi_acceleport.c | 12 +- drivers/usb/serial/empeg.c | 4 +- drivers/usb/serial/io_edgeport.c | 7 +- drivers/usb/serial/io_ti.c | 7 +- drivers/usb/serial/ir-usb.c | 4 + drivers/usb/serial/keyspan_pda.c | 8 +- drivers/usb/serial/mct_u232.c | 6 +- drivers/usb/serial/usb-serial.c | 7 +- drivers/usb/serial/whiteheat.c | 3 +- fs/proc/proc_tty.c | 12 +- include/linux/tty.h | 39 ++- include/linux/tty_ldisc.h | 10 + 67 files changed, 1529 insertions(+), 724 deletions(-) create mode 100644 Documentation/tty.txt (limited to 'include/linux') diff --git a/Documentation/tty.txt b/Documentation/tty.txt new file mode 100644 index 000000000000..7c046ac19206 --- /dev/null +++ b/Documentation/tty.txt @@ -0,0 +1,194 @@ + + The Lockronomicon + +Your guide to the ancient and twisted locking policies of the tty layer and +the warped logic behind them. Beware all ye who read on. + +FIXME: still need to work out the full set of BKL assumptions and document +them so they can eventually be killed off. + + +Line Discipline +--------------- + +Line disciplines are registered with tty_register_ldisc() passing the +discipline number and the ldisc structure. At the point of registration the +discipline must be ready to use and it is possible it will get used before +the call returns success. If the call returns an error then it won't get +called. Do not re-use ldisc numbers as they are part of the userspace ABI +and writing over an existing ldisc will cause demons to eat your computer. +After the return the ldisc data has been copied so you may free your own +copy of the structure. You must not re-register over the top of the line +discipline even with the same data or your computer again will be eaten by +demons. + +In order to remove a line discipline call tty_register_ldisc passing NULL. +In ancient times this always worked. In modern times the function will +return -EBUSY if the ldisc is currently in use. Since the ldisc referencing +code manages the module counts this should not usually be a concern. + +Heed this warning: the reference count field of the registered copies of the +tty_ldisc structure in the ldisc table counts the number of lines using this +discipline. The reference count of the tty_ldisc structure within a tty +counts the number of active users of the ldisc at this instant. In effect it +counts the number of threads of execution within an ldisc method (plus those +about to enter and exit although this detail matters not). + +Line Discipline Methods +----------------------- + +TTY side interfaces: + +close() - This is called on a terminal when the line + discipline is being unplugged. At the point of + execution no further users will enter the + ldisc code for this tty. Can sleep. + +open() - Called when the line discipline is attached to + the terminal. No other call into the line + discipline for this tty will occur until it + completes successfully. Can sleep. + +write() - A process is writing data from user space + through the line discipline. Multiple write calls + are serialized by the tty layer for the ldisc. May + sleep. + +flush_buffer() - May be called at any point between open and close. + +chars_in_buffer() - Report the number of bytes in the buffer. + +set_termios() - Called on termios structure changes. The caller + passes the old termios data and the current data + is in the tty. Called under the termios lock so + may not sleep. Serialized against itself only. + +read() - Move data from the line discipline to the user. + Multiple read calls may occur in parallel and the + ldisc must deal with serialization issues. May + sleep. + +poll() - Check the status for the poll/select calls. Multiple + poll calls may occur in parallel. May sleep. + +ioctl() - Called when an ioctl is handed to the tty layer + that might be for the ldisc. Multiple ioctl calls + may occur in parallel. May sleep. + +Driver Side Interfaces: + +receive_buf() - Hand buffers of bytes from the driver to the ldisc + for processing. Semantics currently rather + mysterious 8( + +receive_room() - Can be called by the driver layer at any time when + the ldisc is opened. The ldisc must be able to + handle the reported amount of data at that instant. + Synchronization between active receive_buf and + receive_room calls is down to the driver not the + ldisc. Must not sleep. + +write_wakeup() - May be called at any point between open and close. + The TTY_DO_WRITE_WAKEUP flag indicates if a call + is needed but always races versus calls. Thus the + ldisc must be careful about setting order and to + handle unexpected calls. Must not sleep. + + +Locking + +Callers to the line discipline functions from the tty layer are required to +take line discipline locks. The same is true of calls from the driver side +but not yet enforced. + +Three calls are now provided + + ldisc = tty_ldisc_ref(tty); + +takes a handle to the line discipline in the tty and returns it. If no ldisc +is currently attached or the ldisc is being closed and re-opened at this +point then NULL is returned. While this handle is held the ldisc will not +change or go away. + + tty_ldisc_deref(ldisc) + +Returns the ldisc reference and allows the ldisc to be closed. Returning the +reference takes away your right to call the ldisc functions until you take +a new reference. + + ldisc = tty_ldisc_ref_wait(tty); + +Performs the same function as tty_ldisc_ref except that it will wait for an +ldisc change to complete and then return a reference to the new ldisc. + +While these functions are slightly slower than the old code they should have +minimal impact as most receive logic uses the flip buffers and they only +need to take a reference when they push bits up through the driver. + +A caution: The ldisc->open(), ldisc->close() and driver->set_ldisc +functions are called with the ldisc unavailable. Thus tty_ldisc_ref will +fail in this situation if used within these functions. Ldisc and driver +code calling its own functions must be careful in this case. + + +Driver Interface +---------------- + +open() - Called when a device is opened. May sleep + +close() - Called when a device is closed. At the point of + return from this call the driver must make no + further ldisc calls of any kind. May sleep + +write() - Called to write bytes to the device. May not + sleep. May occur in parallel in special cases. + Because this includes panic paths drivers generally + shouldn't try and do clever locking here. + +put_char() - Stuff a single character onto the queue. The + driver is guaranteed following up calls to + flush_chars. + +flush_chars() - Ask the kernel to write put_char queue + +write_room() - Return the number of characters tht can be stuffed + into the port buffers without overflow (or less). + The ldisc is responsible for being intelligent + about multi-threading of write_room/write calls + +ioctl() - Called when an ioctl may be for the driver + +set_termios() - Called on termios change, may get parallel calls, + may block for now (may change that) + +set_ldisc() - Notifier for discipline change. At the point this + is done the discipline is not yet usable. Can now + sleep (I think) + +throttle() - Called by the ldisc to ask the driver to do flow + control. Serialization including with unthrottle + is the job of the ldisc layer. + +unthrottle() - Called by the ldisc to ask the driver to stop flow + control. + +stop() - Ldisc notifier to the driver to stop output. As with + throttle the serializations with start() are down + to the ldisc layer. + +start() - Ldisc notifier to the driver to start output. + +hangup() - Ask the tty driver to cause a hangup initiated + from the host side. [Can sleep ??] + +break_ctl() - Send RS232 break. Can sleep. Can get called in + parallel, driver must serialize (for now), and + with write calls. + +wait_until_sent() - Wait for characters to exit the hardware queue + of the driver. Can sleep + +send_xchar() - Send XON/XOFF and if possible jump the queue with + it in order to get fast flow control responses. + Cannot sleep ?? + diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 5ba134216991..fb990ec38968 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -188,9 +188,7 @@ static int hci_uart_flush(struct hci_dev *hdev) } /* Flush any pending characters in the driver and discipline. */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - + tty_ldisc_flush(tty); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); @@ -280,7 +278,9 @@ static int hci_uart_tty_open(struct tty_struct *tty) spin_lock_init(&hu->rx_lock); - /* Flush any pending characters in the driver and line discipline */ + /* Flush any pending characters in the driver and line discipline. */ + /* FIXME: why is this needed. Note don't use ldisc_ref here as the + open path is before the ldisc is referencable */ if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index aeb351d1c12c..f11cf4ea0398 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -553,9 +553,7 @@ static void do_softint(unsigned long private_) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } } @@ -1019,9 +1017,7 @@ static void rs_flush_buffer(struct tty_struct *tty) info->xmit.head = info->xmit.tail = 0; local_irq_restore(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1560,8 +1556,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = 0; diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e69dc6d00427..55bd1363b85c 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -970,10 +970,7 @@ do_softint(void *private_) wake_up_interruptible(&info->delta_msr_wait); } if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { - if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } #ifdef Z_WAKE @@ -2850,8 +2847,7 @@ cy_close(struct tty_struct *tty, struct file *filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); CY_LOCK(info, flags); tty->closing = 0; @@ -4554,10 +4550,8 @@ cy_flush_buffer(struct tty_struct *tty) } CY_UNLOCK(info, flags); } + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); } /* cy_flush_buffer */ diff --git a/drivers/char/epca.c b/drivers/char/epca.c index de1f2393b9c2..0f13bef975f5 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -550,9 +550,7 @@ static void pc_close(struct tty_struct * tty, struct file * filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - + tty_ldisc_flush(tty); shutdown(ch); tty->closing = 0; ch->event = 0; @@ -656,10 +654,7 @@ static void pc_hangup(struct tty_struct *tty) cli(); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - + tty_ldisc_flush(tty); shutdown(ch); ch->tty = NULL; @@ -1119,8 +1114,7 @@ static void pc_flush_buffer(struct tty_struct *tty) restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* End pc_flush_buffer */ @@ -2261,9 +2255,7 @@ static void doevent(int crd) { /* Begin if LOWWAIT */ ch->statusflags &= ~LOWWAIT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } /* End if LOWWAIT */ @@ -2280,9 +2272,7 @@ static void doevent(int crd) { /* Begin if EMPTYWAIT */ ch->statusflags &= ~EMPTYWAIT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); @@ -3135,6 +3125,7 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, } else { + /* ldisc lock already held in ioctl */ if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); } diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 87b48d9fd7e6..d67098c45ffd 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -758,10 +758,7 @@ static void do_softint(void *private_) return; if (test_and_clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } @@ -1366,10 +1363,7 @@ static void rs_flush_buffer(struct tty_struct *tty) cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; sti(); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -2065,8 +2059,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = NULL; diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 1027eb7c6e4c..74b8c78c965a 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -436,9 +436,7 @@ void gs_flush_buffer(struct tty_struct *tty) restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); func_exit (); } @@ -578,9 +576,7 @@ void gs_do_softint(void *private_) if (!tty) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } func_exit (); @@ -694,8 +690,8 @@ void gs_close(struct tty_struct * tty, struct file * filp) { unsigned long flags; struct gs_port *port; - - func_enter (); + + func_enter () if (!tty) return; @@ -760,8 +756,8 @@ void gs_close(struct tty_struct * tty, struct file * filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); tty->closing = 0; port->event = 0; diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 84333c1fdf90..3c0af2cad6ee 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -558,10 +558,7 @@ static int hvc_poll(struct hvc_struct *hp) /* Wakeup write queue if necessary */ if (hp->do_wakeup) { hp->do_wakeup = 0; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } bail: spin_unlock_irqrestore(&hp->lock, flags); diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c index 6b0c98dc992e..15b1d8cbc77d 100644 --- a/drivers/char/hvcs.c +++ b/drivers/char/hvcs.c @@ -425,10 +425,7 @@ static void hvcs_try_write(struct hvcs_struct *hvcsd) * a non-existent tty. */ if (tty) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } } diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 1dcaf0d721ab..b454bf2084bf 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -485,10 +485,8 @@ static void isicom_bottomhalf(void * data) if (!tty) return; - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } @@ -1120,8 +1118,8 @@ static void isicom_close(struct tty_struct * tty, struct file * filp) isicom_shutdown_port(port); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); tty->closing = 0; port->tty = NULL; if (port->blocked_open) { @@ -1564,9 +1562,7 @@ static void isicom_flush_buffer(struct tty_struct * tty) restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 0e75a0dc97eb..8ed2faa53e1a 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -618,8 +618,8 @@ static void moxa_close(struct tty_struct *tty, struct file *filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; ch->event = 0; ch->tty = NULL; @@ -693,9 +693,7 @@ static void moxa_flush_buffer(struct tty_struct *tty) if (ch == NULL) return; MoxaPortFlushData(ch->port, 1); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } @@ -954,9 +952,7 @@ static void moxa_poll(unsigned long ignored) if (MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS) { if (!tp->stopped) { ch->statusflags &= ~LOWWAIT; - if ((tp->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tp->ldisc.write_wakeup) - (tp->ldisc.write_wakeup) (tp); + tty_wakeup(tty); wake_up_interruptible(&tp->write_wait); } } @@ -1123,9 +1119,7 @@ static void check_xmit_empty(unsigned long data) if (ch->tty && (ch->statusflags & EMPTYWAIT)) { if (MoxaPortTxQueue(ch->port) == 0) { ch->statusflags &= ~EMPTYWAIT; - if ((ch->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - ch->tty->ldisc.write_wakeup) - (ch->tty->ldisc.write_wakeup) (ch->tty); + tty_wakeup(tty); wake_up_interruptible(&ch->tty->write_wait); return; } diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index a1a1fd6802ba..147c1ba2efe8 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -674,9 +674,7 @@ static void mxser_do_softint(void *private_) tty = info->tty; if (tty) { if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) { @@ -813,8 +811,8 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) mxser_shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; info->event = 0; info->tty = NULL; @@ -972,9 +970,7 @@ static void mxser_flush_buffer(struct tty_struct *tty) info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); + tty_wakeup(tty); } static int mxser_ioctl(struct tty_struct *tty, struct file *file, diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 072197462542..5a3be050e7bf 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -99,11 +99,18 @@ static inline void put_tty_queue(unsigned char c, struct tty_struct *tty) spin_unlock_irqrestore(&tty->read_lock, flags); } -/* - * Check whether to call the driver.unthrottle function. - * We test the TTY_THROTTLED bit first so that it always - * indicates the current state. +/** + * check_unthrottle - allow new receive data + * @tty; tty device + * + * Check whether to call the driver.unthrottle function. + * We test the TTY_THROTTLED bit first so that it always + * indicates the current state. The decision about whether + * it is worth allowing more input has been taken by the caller. + * Can sleep, may be called under the atomic_read semaphore but + * this is not guaranteed. */ + static void check_unthrottle(struct tty_struct * tty) { if (tty->count && @@ -112,10 +119,13 @@ static void check_unthrottle(struct tty_struct * tty) tty->driver->unthrottle(tty); } -/* - * Reset the read buffer counters, clear the flags, - * and make sure the driver is unthrottled. Called - * from n_tty_open() and n_tty_flush_buffer(). +/** + * reset_buffer_flags - reset buffer state + * @tty: terminal to reset + * + * Reset the read buffer counters, clear the flags, + * and make sure the driver is unthrottled. Called + * from n_tty_open() and n_tty_flush_buffer(). */ static void reset_buffer_flags(struct tty_struct *tty) { @@ -129,9 +139,19 @@ static void reset_buffer_flags(struct tty_struct *tty) check_unthrottle(tty); } -/* - * Flush the input buffer +/** + * n_tty_flush_buffer - clean input queue + * @tty: terminal device + * + * Flush the input buffer. Called when the line discipline is + * being closed, when the tty layer wants the buffer flushed (eg + * at hangup) or when the N_TTY line discipline internally has to + * clean the pending queue (for example some signals). + * + * FIXME: tty->ctrl_status is not spinlocked and relies on + * lock_kernel() still. */ + void n_tty_flush_buffer(struct tty_struct * tty) { /* clear everything and unthrottle the driver */ @@ -146,9 +166,14 @@ void n_tty_flush_buffer(struct tty_struct * tty) } } -/* - * Return number of characters buffered to be delivered to user +/** + * n_tty_chars_in_buffer - report available bytes + * @tty: tty device + * + * Report the number of characters buffered to be delivered to user + * at this instant in time. */ + ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) { unsigned long flags; @@ -166,20 +191,47 @@ ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) return n; } +/** + * is_utf8_continuation - utf8 multibyte check + * @c: byte to check + * + * Returns true if the utf8 character 'c' is a multibyte continuation + * character. We use this to correctly compute the on screen size + * of the character when printing + */ + static inline int is_utf8_continuation(unsigned char c) { return (c & 0xc0) == 0x80; } +/** + * is_continuation - multibyte check + * @c: byte to check + * + * Returns true if the utf8 character 'c' is a multibyte continuation + * character and the terminal is in unicode mode. + */ + static inline int is_continuation(unsigned char c, struct tty_struct *tty) { return I_IUTF8(tty) && is_utf8_continuation(c); } -/* - * Perform OPOST processing. Returns -1 when the output device is - * full and the character must be retried. +/** + * opost - output post processor + * @c: character (or partial unicode symbol) + * @tty: terminal device + * + * Perform OPOST processing. Returns -1 when the output device is + * full and the character must be retried. Note that Linux currently + * ignores TABDLY, CRDLY, VTDLY, FFDLY and NLDLY. They simply aren't + * relevant in the world today. If you ever need them, add them here. + * + * Called from both the receive and transmit sides and can be called + * re-entrantly. Relies on lock_kernel() still. */ + static int opost(unsigned char c, struct tty_struct *tty) { int space, spaces; @@ -239,10 +291,20 @@ static int opost(unsigned char c, struct tty_struct *tty) return 0; } -/* - * opost_block --- to speed up block console writes, among other - * things. +/** + * opost_block - block postprocess + * @tty: terminal device + * @inbuf: user buffer + * @nr: number of bytes + * + * This path is used to speed up block console writes, among other + * things when processing blocks of output data. It handles only + * the simple cases normally found and helps to generate blocks of + * symbols for the console driver and thus improve performance. + * + * Called from write_chan under the tty layer write lock. */ + static ssize_t opost_block(struct tty_struct * tty, const unsigned char __user * inbuf, unsigned int nr) { @@ -304,13 +366,27 @@ break_out: } - +/** + * put_char - write character to driver + * @c: character (or part of unicode symbol) + * @tty: terminal device + * + * Queue a byte to the driver layer for output + */ + static inline void put_char(unsigned char c, struct tty_struct *tty) { tty->driver->put_char(tty, c); } -/* Must be called only when L_ECHO(tty) is true. */ +/** + * echo_char - echo characters + * @c: unicode byte to echo + * @tty: terminal device + * + * Echo user input back onto the screen. This must be called only when + * L_ECHO(tty) is true. Called from the driver receive_buf path. + */ static void echo_char(unsigned char c, struct tty_struct *tty) { @@ -331,6 +407,16 @@ static inline void finish_erasing(struct tty_struct *tty) } } +/** + * eraser - handle erase function + * @c: character input + * @tty: terminal device + * + * Perform erase and neccessary output when an erase character is + * present in the stream from the driver layer. Handles the complexities + * of UTF-8 multibyte symbols. + */ + static void eraser(unsigned char c, struct tty_struct *tty) { enum { ERASE, WERASE, KILL } kill_type; @@ -463,6 +549,18 @@ static void eraser(unsigned char c, struct tty_struct *tty) finish_erasing(tty); } +/** + * isig - handle the ISIG optio + * @sig: signal + * @tty: terminal + * @flush: force flush + * + * Called when a signal is being sent due to terminal input. This + * may caus terminal flushing to take place according to the termios + * settings and character used. Called from the driver receive_buf + * path so serialized. + */ + static inline void isig(int sig, struct tty_struct *tty, int flush) { if (tty->pgrp > 0) @@ -474,6 +572,16 @@ static inline void isig(int sig, struct tty_struct *tty, int flush) } } +/** + * n_tty_receive_break - handle break + * @tty: terminal + * + * An RS232 break event has been hit in the incoming bitstream. This + * can cause a variety of events depending upon the termios settings. + * + * Called from the receive_buf path so single threaded. + */ + static inline void n_tty_receive_break(struct tty_struct *tty) { if (I_IGNBRK(tty)) @@ -490,19 +598,40 @@ static inline void n_tty_receive_break(struct tty_struct *tty) wake_up_interruptible(&tty->read_wait); } +/** + * n_tty_receive_overrun - handle overrun reporting + * @tty: terminal + * + * Data arrived faster than we could process it. While the tty + * driver has flagged this the bits that were missed are gone + * forever. + * + * Called from the receive_buf path so single threaded. Does not + * need locking as num_overrun and overrun_time are function + * private. + */ + static inline void n_tty_receive_overrun(struct tty_struct *tty) { char buf[64]; tty->num_overrun++; if (time_before(tty->overrun_time, jiffies - HZ)) { - printk("%s: %d input overrun(s)\n", tty_name(tty, buf), + printk(KERN_WARNING "%s: %d input overrun(s)\n", tty_name(tty, buf), tty->num_overrun); tty->overrun_time = jiffies; tty->num_overrun = 0; } } +/** + * n_tty_receive_parity_error - error notifier + * @tty: terminal device + * @c: character + * + * Process a parity error and queue the right data to indicate + * the error case if neccessary. Locking as per n_tty_receive_buf. + */ static inline void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) { @@ -520,6 +649,16 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty, wake_up_interruptible(&tty->read_wait); } +/** + * n_tty_receive_char - perform processing + * @tty: terminal device + * @c: character + * + * Process an individual character of input received from the driver. + * This is serialized with respect to itself by the rules for the + * driver above. + */ + static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { unsigned long flags; @@ -711,6 +850,16 @@ send_signal: put_tty_queue(c, tty); } +/** + * n_tty_receive_room - receive space + * @tty: terminal + * + * Called by the driver to find out how much data it is + * permitted to feed to the line discipline without any being lost + * and thus to manage flow control. Not serialized. Answers for the + * "instant". + */ + static int n_tty_receive_room(struct tty_struct *tty) { int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; @@ -729,10 +878,13 @@ static int n_tty_receive_room(struct tty_struct *tty) return 0; } -/* - * Required for the ptys, serial driver etc. since processes - * that attach themselves to the master and rely on ASYNC - * IO must be woken up +/** + * n_tty_write_wakeup - asynchronous I/O notifier + * @tty: tty device + * + * Required for the ptys, serial driver etc. since processes + * that attach themselves to the master and rely on ASYNC + * IO must be woken up */ static void n_tty_write_wakeup(struct tty_struct *tty) @@ -745,6 +897,19 @@ static void n_tty_write_wakeup(struct tty_struct *tty) return; } +/** + * n_tty_receive_buf - data receive + * @tty: terminal device + * @cp: buffer + * @fp: flag buffer + * @count: characters + * + * Called by the terminal driver when a block of characters has + * been received. This function must be called from soft contexts + * not from interrupt context. The driver is responsible for making + * calls one at a time and in order (or using flush_to_ldisc) + */ + static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { @@ -828,6 +993,18 @@ int is_ignored(int sig) current->sighand->action[sig-1].sa.sa_handler == SIG_IGN); } +/** + * n_tty_set_termios - termios data changed + * @tty: terminal + * @old: previous data + * + * Called by the tty layer when the user changes termios flags so + * that the line discipline can plan ahead. This function cannot sleep + * and is protected from re-entry by the tty layer. The user is + * guaranteed that this function will not be re-entered or in progress + * when the ldisc is closed. + */ + static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) { if (!tty) @@ -843,7 +1020,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { - local_irq_disable(); // FIXME: is this safe? memset(tty->process_char_map, 0, 256/8); if (I_IGNCR(tty) || I_ICRNL(tty)) @@ -879,7 +1055,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) set_bit(SUSP_CHAR(tty), tty->process_char_map); } clear_bit(__DISABLED_CHAR, tty->process_char_map); - local_irq_enable(); // FIXME: is this safe? tty->raw = 0; tty->real_raw = 0; } else { @@ -893,6 +1068,16 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) } } +/** + * n_tty_close - close the ldisc for this tty + * @tty: device + * + * Called from the terminal layer when this line discipline is + * being shut down, either because of a close or becsuse of a + * discipline change. The function will not be called while other + * ldisc methods are in progress. + */ + static void n_tty_close(struct tty_struct *tty) { n_tty_flush_buffer(tty); @@ -902,11 +1087,22 @@ static void n_tty_close(struct tty_struct *tty) } } +/** + * n_tty_open - open an ldisc + * @tty: terminal to open + * + * Called when this line discipline is being attached to the + * terminal device. Can sleep. Called serialized so that no + * other events will occur in parallel. No further open will occur + * until a close. + */ + static int n_tty_open(struct tty_struct *tty) { if (!tty) return -EINVAL; + /* This one is ugly. Currently a malloc failure here can panic */ if (!tty->read_buf) { tty->read_buf = alloc_buf(); if (!tty->read_buf) @@ -932,14 +1128,23 @@ static inline int input_available_p(struct tty_struct *tty, int amt) return 0; } -/* - * Helper function to speed up read_chan. It is only called when - * ICANON is off; it copies characters straight from the tty queue to - * user space directly. It can be profitably called twice; once to - * drain the space from the tail pointer to the (physical) end of the - * buffer, and once to drain the space from the (physical) beginning of - * the buffer to head pointer. +/** + * copy_from_read_buf - copy read data directly + * @tty: terminal device + * @b: user data + * @nr: size of data + * + * Helper function to speed up read_chan. It is only called when + * ICANON is off; it copies characters straight from the tty queue to + * user space directly. It can be profitably called twice; once to + * drain the space from the tail pointer to the (physical) end of the + * buffer, and once to drain the space from the (physical) beginning of + * the buffer to head pointer. + * + * Called under the tty->atomic_read sem and with TTY_DONT_FLIP set + * */ + static inline int copy_from_read_buf(struct tty_struct *tty, unsigned char __user **b, size_t *nr) @@ -970,25 +1175,18 @@ static inline int copy_from_read_buf(struct tty_struct *tty, extern ssize_t redirected_tty_write(struct file *,const char *,size_t,loff_t *); -static ssize_t read_chan(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) +/** + * job_control - check job control + * @tty: tty + * @file: file handle + * + * Perform job control management checks on this file/tty descriptor + * and if appropriate send any needed signals and return a negative + * error code if action should be taken. + */ + +static int job_control(struct tty_struct *tty, struct file *file) { - unsigned char __user *b = buf; - DECLARE_WAITQUEUE(wait, current); - int c; - int minimum, time; - ssize_t retval = 0; - ssize_t size; - long timeout; - unsigned long flags; - -do_it_again: - - if (!tty->read_buf) { - printk("n_tty_read_chan: called with read_buf == NULL?!?\n"); - return -EIO; - } - /* Job control check -- must be done at start and after every sleep (POSIX.1 7.1.1.4). */ /* NOTE: not yet done after every sleep pending a thorough @@ -1006,7 +1204,48 @@ do_it_again: return -ERESTARTSYS; } } + return 0; +} + + +/** + * read_chan - read function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Perform reads for the line discipline. We are guaranteed that the + * line discipline will not be closed under us but we may get multiple + * parallel readers and must handle this ourselves. We may also get + * a hangup. Always called in user context, may sleep. + * + * This code must be sure never to sleep through a hangup. + */ + +static ssize_t read_chan(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr) +{ + unsigned char __user *b = buf; + DECLARE_WAITQUEUE(wait, current); + int c; + int minimum, time; + ssize_t retval = 0; + ssize_t size; + long timeout; + unsigned long flags; + +do_it_again: + + if (!tty->read_buf) { + printk("n_tty_read_chan: called with read_buf == NULL?!?\n"); + return -EIO; + } + c = job_control(tty, file); + if(c < 0) + return c; + minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; if (!tty->icanon) { @@ -1028,6 +1267,9 @@ do_it_again: } } + /* + * Internal serialization of reads. + */ if (file->f_flags & O_NONBLOCK) { if (down_trylock(&tty->atomic_read)) return -EAGAIN; @@ -1177,6 +1419,21 @@ do_it_again: return retval; } +/** + * write_chan - write function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Write function of the terminal device. This is serialized with + * respect to other write callers but not to termios changes, reads + * and other such events. We must be careful with N_TTY as the receive + * code will echo characters, thus calling driver write methods. + * + * This code must be sure never to sleep through a hangup. + */ + static ssize_t write_chan(struct tty_struct * tty, struct file * file, const unsigned char __user * buf, size_t nr) { @@ -1246,7 +1503,25 @@ break_out: return (b - buf) ? b - buf : retval; } -/* Called without the kernel lock held - fine */ +/** + * normal_poll - poll method for N_TTY + * @tty: terminal device + * @file: file accessing it + * @wait: poll table + * + * Called when the line discipline is asked to poll() for data or + * for special events. This code is not serialized with respect to + * other events save open/close. + * + * This code must be sure never to sleep through a hangup. + * Called without the kernel lock held - fine + * + * FIXME: if someone changes the VMIN or discipline settings for the + * terminal while another process is in poll() the poll does not + * recompute the new limits. Possibly set_termios should issue + * a read wakeup to fix this bug. + */ + static unsigned int normal_poll(struct tty_struct * tty, struct file * file, poll_table *wait) { unsigned int mask = 0; @@ -1287,6 +1562,7 @@ struct tty_ldisc tty_ldisc_N_TTY = { n_tty_ioctl, /* ioctl */ n_tty_set_termios, /* set_termios */ normal_poll, /* poll */ + NULL, /* hangup */ n_tty_receive_buf, /* receive_buf */ n_tty_receive_room, /* receive_room */ n_tty_write_wakeup /* write_wakeup */ diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index cab8e2d313ed..5d7f2154247d 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -516,6 +516,40 @@ static void* mgslpc_get_text_ptr(void) return mgslpc_get_text_ptr; } +/** + * line discipline callback wrappers + * + * The wrappers maintain line discipline references + * while calling into the line discipline. + * + * ldisc_flush_buffer - flush line discipline receive buffers + * ldisc_receive_buf - pass receive data to line discipline + */ + +static void ldisc_flush_buffer(struct tty_struct *tty) +{ + struct tty_ldisc *ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->flush_buffer) + ld->flush_buffer(tty); + tty_ldisc_deref(ld); + } +} + +static void ldisc_receive_buf(struct tty_struct *tty, + const __u8 *data, char *flags, int count) +{ + struct tty_ldisc *ld; + if (!tty) + return; + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->receive_buf) + ld->receive_buf(tty, data, flags, count); + tty_ldisc_deref(ld); + } +} + static dev_link_t *mgslpc_attach(void) { MGSLPC_INFO *info; @@ -969,13 +1003,7 @@ void bh_transmit(MGSLPC_INFO *info) printk("bh_transmit() entry on %s\n", info->device_name); if (tty) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) { - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):calling ldisc.write_wakeup on %s\n", - __FILE__,__LINE__,info->device_name); - (tty->ldisc.write_wakeup)(tty); - } + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } } @@ -1860,11 +1888,9 @@ static void mgslpc_flush_buffer(struct tty_struct *tty) info->tx_count = info->tx_put = info->tx_get = 0; del_timer(&info->tx_timer); spin_unlock_irqrestore(&info->lock,flags); - + wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* Send a high-priority XON/XOFF character @@ -2573,9 +2599,8 @@ static void mgslpc_close(struct tty_struct *tty, struct file * filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + ldisc_flush_buffer(tty); shutdown(info); @@ -4040,11 +4065,7 @@ int rx_get_frame(MGSLPC_INFO *info) hdlcdev_rx(info, buf->data, framesize); else #endif - { - /* Call the line discipline receive callback directly. */ - if (tty && tty->ldisc.receive_buf) - tty->ldisc.receive_buf(tty, buf->data, info->flag_buf, framesize); - } + ldisc_receive_buf(tty, buf->data, info->flag_buf, framesize); } } diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index ebe1ff54b0af..061237bb0c33 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -531,28 +531,11 @@ static void pcxe_close(struct tty_struct * tty, struct file * filp) if(tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if(tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); shutdown(info); tty->closing = 0; info->event = 0; info->tty = NULL; -#ifndef MODULE -/* ldiscs[] is not available in a MODULE -** worth noting that while I'm not sure what this hunk of code is supposed -** to do, it is not present in the serial.c driver. Hmmm. If you know, -** please send me a note. brian@ilinx.com -** Don't know either what this is supposed to do christoph@lameter.com. -*/ - if(tty->ldisc.num != ldiscs[N_TTY].num) { - if(tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if(tty->ldisc.open) - (tty->ldisc.open)(tty); - } -#endif if(info->blocked_open) { if(info->close_delay) { current->state = TASK_INTERRUPTIBLE; @@ -793,9 +776,7 @@ static void pcxe_flush_buffer(struct tty_struct *tty) memoff(ch); restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } static void pcxe_flush_chars(struct tty_struct *tty) @@ -1667,10 +1648,7 @@ static void doevent(int crd) if (event & LOWTX_IND) { if (ch->statusflags & LOWWAIT) { ch->statusflags &= ~LOWWAIT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } @@ -1678,10 +1656,7 @@ static void doevent(int crd) ch->statusflags &= ~TXBUSY; if (ch->statusflags & EMPTYWAIT) { ch->statusflags &= ~EMPTYWAIT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } } @@ -2159,8 +2134,7 @@ static int pcxe_ioctl(struct tty_struct *tty, struct file * file, tty_wait_until_sent(tty, 0); } else { - if(tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); } /* Fall Thru */ diff --git a/drivers/char/pty.c b/drivers/char/pty.c index cac0e98639cf..9826bbd0f67a 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -85,10 +85,7 @@ static void pty_unthrottle(struct tty_struct * tty) if (!o_tty) return; - if ((o_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - o_tty->ldisc.write_wakeup) - (o_tty->ldisc.write_wakeup)(o_tty); - wake_up_interruptible(&o_tty->write_wait); + tty_wakeup(o_tty); set_bit(TTY_THROTTLED, &tty->flags); } @@ -101,6 +98,10 @@ static void pty_unthrottle(struct tty_struct * tty) * (2) avoid redundant copying for cases where count >> receive_room * N.B. Calls from user space may now return an error code instead of * a count. + * + * FIXME: Our pty_write method is called with our ldisc lock held but + * not our partners. We can't just take the other one blindly without + * risking deadlocks. There is also the small matter of TTY_DONT_FLIP */ static int pty_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index a1e85b918740..a35ea03a3227 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1123,8 +1123,8 @@ static void rc_close(struct tty_struct * tty, struct file * filp) rc_shutdown_port(bp, port); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; port->event = 0; port->tty = NULL; @@ -1297,9 +1297,7 @@ static void rc_flush_buffer(struct tty_struct *tty) restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } static int rc_tiocmget(struct tty_struct *tty, struct file *file) @@ -1640,9 +1638,7 @@ static void do_softint(void *private_) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } } diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 046f5fd62b08..72af1bec91e5 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -250,12 +250,16 @@ static void rp_do_receive(struct r_port *info, CHANNEL_t * cp, unsigned int ChanStatus) { unsigned int CharNStat; - int ToRecv, wRecv, space, count; + int ToRecv, wRecv, space = 0, count; unsigned char *cbuf; char *fbuf; + struct tty_ldisc *ld; + + ld = tty_ldisc_ref(tty); ToRecv = sGetRxCnt(cp); - space = tty->ldisc.receive_room(tty); + if (ld) + space = ld->receive_room(tty); if (space > 2 * TTY_FLIPBUF_SIZE) space = 2 * TTY_FLIPBUF_SIZE; cbuf = tty->flip.char_buf; @@ -354,7 +358,8 @@ static void rp_do_receive(struct r_port *info, count += ToRecv; } /* Push the data up to the tty layer */ - tty->ldisc.receive_buf(tty, tty->flip.char_buf, tty->flip.flag_buf, count); + ld->receive_buf(tty, tty->flip.char_buf, tty->flip.flag_buf, count); + tty_ldisc_deref(ld); } /* @@ -408,8 +413,7 @@ static void rp_do_transmit(struct r_port *info) clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); if (info->xmit_cnt < WAKEUP_CHARS) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); #ifdef ROCKETPORT_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait); @@ -1022,7 +1026,7 @@ static void rp_close(struct tty_struct *tty, struct file *filp) unsigned long flags; int timeout; CHANNEL_t *cp; - + if (rocket_paranoia_check(info, "rp_close")) return; @@ -1101,8 +1105,8 @@ static void rp_close(struct tty_struct *tty, struct file *filp) if (TTY_DRIVER_FLUSH_BUFFER_EXISTS(tty)) TTY_DRIVER_FLUSH_BUFFER(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); @@ -1727,8 +1731,7 @@ end_intr: end: if (info->xmit_cnt < WAKEUP_CHARS) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); #ifdef ROCKETPORT_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait); @@ -1802,8 +1805,7 @@ static void rp_flush_buffer(struct tty_struct *tty) #ifdef ROCKETPORT_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait); #endif - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); + tty_wakeup(tty); cp = &info->channel; sFlushTxFIFO(cp); diff --git a/drivers/char/selection.c b/drivers/char/selection.c index 995c675d709c..4675eda44a62 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -277,12 +277,15 @@ int paste_selection(struct tty_struct *tty) { struct vt_struct *vt = (struct vt_struct *) tty->driver_data; int pasted = 0, count; + struct tty_ldisc *ld; DECLARE_WAITQUEUE(wait, current); acquire_console_sem(); poke_blanked_console(); release_console_sem(); + ld = tty_ldisc_ref_wait(tty); + add_wait_queue(&vt->paste_wait, &wait); while (sel_buffer && sel_buffer_lth > pasted) { set_current_state(TASK_INTERRUPTIBLE); @@ -297,6 +300,8 @@ int paste_selection(struct tty_struct *tty) } remove_wait_queue(&vt->paste_wait, &wait); current->state = TASK_RUNNING; + + tty_ldisc_deref(ld); return 0; } diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c index f0b966876f35..6b4e9d155f50 100644 --- a/drivers/char/ser_a2232.c +++ b/drivers/char/ser_a2232.c @@ -599,10 +599,7 @@ int ch, err, n, p; /* WakeUp if output buffer runs low */ if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) { - if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && port->gs.tty->ldisc.write_wakeup){ - (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); - } - wake_up_interruptible(&port->gs.tty->write_wait); + tty_wakeup(port->gs.tty); } } // if the port is used } // for every port on the board diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 03dde8350d5b..885621edb4a6 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -756,11 +756,7 @@ do_softint(void *private_) wake_up_interruptible(&info->open_wait); } if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { - if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } /* do_softint */ @@ -1339,10 +1335,7 @@ cy_flush_buffer(struct tty_struct *tty) local_irq_save(flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; local_irq_restore(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* cy_flush_buffer */ @@ -1842,18 +1835,9 @@ cy_close(struct tty_struct * tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); info->event = 0; info->tty = 0; - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) - (tty->ldisc.open)(tty); - } if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; diff --git a/drivers/char/serial_tx3912.c b/drivers/char/serial_tx3912.c index 39717ed687ca..50ed6dfec03d 100644 --- a/drivers/char/serial_tx3912.c +++ b/drivers/char/serial_tx3912.c @@ -191,12 +191,9 @@ static inline void transmit_char_pio(struct rs_port *port) } if (port->gs.xmit_cnt <= port->gs.wakeup_chars) { - if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - port->gs.tty->ldisc.write_wakeup) - (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); + tty_wakeup(port->gs.tty); rs_dprintk (TX3912_UART_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", port->gs.wakeup_chars); - wake_up_interruptible(&port->gs.tty->write_wait); } } diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index fe46db2412d0..bf82f06d4011 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -1464,8 +1464,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) sx_shutdown_port(bp, port); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); tty->closing = 0; port->event = 0; port->tty = NULL; @@ -1642,10 +1641,8 @@ static void sx_flush_buffer(struct tty_struct *tty) port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; restore_flags(flags); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); } @@ -2048,12 +2045,8 @@ static void do_softint(void *private_) if(!(tty = port->tty)) return; - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - } + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) + tty_wakeup(tty); } static struct tty_operations sx_ops = { diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index a0b3ba7b0586..90018ce17c57 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -1197,8 +1197,7 @@ static void stl_close(struct tty_struct *tty, struct file *filp) portp->tx.tail = (char *) NULL; } set_bit(TTY_IO_ERROR, &tty->flags); - if (tty->ldisc.flush_buffer) - (tty->ldisc.flush_buffer)(tty); + tty_ldisc_flush(tty); tty->closing = 0; portp->tty = (struct tty_struct *) NULL; @@ -1809,10 +1808,7 @@ static void stl_flushbuffer(struct tty_struct *tty) return; stl_flush(portp); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /*****************************************************************************/ @@ -2193,10 +2189,7 @@ static void stl_offintr(void *private) lock_kernel(); if (test_bit(ASYI_TXLOW, &portp->istate)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } if (test_bit(ASYI_DCDCHANGE, &portp->istate)) { clear_bit(ASYI_DCDCHANGE, &portp->istate); diff --git a/drivers/char/sx.c b/drivers/char/sx.c index 1ca3d2543d75..a990eb7aa6b5 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1046,12 +1046,9 @@ static void sx_transmit_chars (struct sx_port *port) } if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) { - if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - port->gs.tty->ldisc.write_wakeup) - (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); + tty_wakeup(port->gs.tty); sx_dprintk (SX_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", port->gs.wakeup_chars); - wake_up_interruptible(&port->gs.tty->write_wait); } clear_bit (SX_PORT_TRANSMIT_LOCK, &port->locks); diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index dec600eb79f1..0a1cc1d3630f 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -975,6 +975,29 @@ static inline int mgsl_paranoia_check(struct mgsl_struct *info, return 0; } +/** + * line discipline callback wrappers + * + * The wrappers maintain line discipline references + * while calling into the line discipline. + * + * ldisc_receive_buf - pass receive data to line discipline + */ + +static void ldisc_receive_buf(struct tty_struct *tty, + const __u8 *data, char *flags, int count) +{ + struct tty_ldisc *ld; + if (!tty) + return; + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->receive_buf) + ld->receive_buf(tty, data, flags, count); + tty_ldisc_deref(ld); + } +} + /* mgsl_stop() throttle (stop) transmitter * * Arguments: tty pointer to tty info structure @@ -1135,13 +1158,7 @@ void mgsl_bh_transmit(struct mgsl_struct *info) __FILE__,__LINE__,info->device_name); if (tty) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) { - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):calling ldisc.write_wakeup on %s\n", - __FILE__,__LINE__,info->device_name); - (tty->ldisc.write_wakeup)(tty); - } + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } @@ -2397,11 +2414,8 @@ static void mgsl_flush_buffer(struct tty_struct *tty) spin_unlock_irqrestore(&info->irq_spinlock,flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - -} /* end of mgsl_flush_buffer() */ + tty_wakeup(tty); +} /* mgsl_send_xchar() * @@ -3235,9 +3249,8 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); shutdown(info); @@ -6810,11 +6823,7 @@ int mgsl_get_rx_frame(struct mgsl_struct *info) hdlcdev_rx(info,info->intermediate_rxbuffer,framesize); else #endif - { - /* Call the line discipline receive callback directly. */ - if ( tty && tty->ldisc.receive_buf ) - tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); - } + ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); } } /* Free the buffers used by this frame. */ @@ -6986,9 +6995,7 @@ int mgsl_get_raw_rx_frame(struct mgsl_struct *info) memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize); info->icount.rxok++; - /* Call the line discipline receive callback directly. */ - if ( tty && tty->ldisc.receive_buf ) - tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); + ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); } /* Free the buffers used by this frame. */ diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 1d2c9118afb9..5648ed35562f 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -699,6 +699,29 @@ static inline int sanity_check(SLMP_INFO *info, return 0; } +/** + * line discipline callback wrappers + * + * The wrappers maintain line discipline references + * while calling into the line discipline. + * + * ldisc_receive_buf - pass receive data to line discipline + */ + +static void ldisc_receive_buf(struct tty_struct *tty, + const __u8 *data, char *flags, int count) +{ + struct tty_ldisc *ld; + if (!tty) + return; + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->receive_buf) + ld->receive_buf(tty, data, flags, count); + tty_ldisc_deref(ld); + } +} + /* tty callbacks */ /* Called when a port is opened. Init and enable port. @@ -846,8 +869,7 @@ static void close(struct tty_struct *tty, struct file *filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); shutdown(info); @@ -1252,9 +1274,7 @@ static void flush_buffer(struct tty_struct *tty) spin_unlock_irqrestore(&info->lock,flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* throttle (stop) transmitter @@ -2123,13 +2143,7 @@ void bh_transmit(SLMP_INFO *info) __FILE__,__LINE__,info->device_name); if (tty) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) { - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):%s calling ldisc.write_wakeup\n", - __FILE__,__LINE__,info->device_name); - (tty->ldisc.write_wakeup)(tty); - } + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } } @@ -5051,15 +5065,8 @@ CheckAgain: hdlcdev_rx(info,info->tmp_rx_buf,framesize); else #endif - { - if ( tty && tty->ldisc.receive_buf ) { - /* Call the line discipline receive callback directly. */ - tty->ldisc.receive_buf(tty, - info->tmp_rx_buf, - info->flag_buf, - framesize); - } - } + ldisc_receive_buf(tty,info->tmp_rx_buf, + info->flag_buf, framesize); } } /* Free the buffers used by this frame. */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 68304e241e90..57464870db3f 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -92,6 +92,7 @@ #include #include #include +#include #include #include @@ -120,11 +121,17 @@ struct termios tty_std_termios = { /* for the benefit of tty drivers */ EXPORT_SYMBOL(tty_std_termios); +/* This list gets poked at by procfs and various bits of boot up code. This + could do with some rationalisation such as pulling the tty proc function + into this file */ + LIST_HEAD(tty_drivers); /* linked list of tty drivers */ -struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */ -/* Semaphore to protect creating and releasing a tty */ +/* Semaphore to protect creating and releasing a tty. This is shared with + vt.c for deeply disgusting hack reasons */ DECLARE_MUTEX(tty_sem); +/* Lock for tty_termios changes - private to tty_io/tty_ioctl */ +spinlock_t tty_termios_lock = SPIN_LOCK_UNLOCKED; #ifdef CONFIG_UNIX98_PTYS extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ @@ -224,65 +231,324 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) return 0; } +/* + * This is probably overkill for real world processors but + * they are not on hot paths so a little discipline won't do + * any harm. + */ + +static void tty_set_termios_ldisc(struct tty_struct *tty, int num) +{ + unsigned long flags; + spin_lock_irqsave(&tty_termios_lock, flags); + tty->termios->c_line = num; + spin_unlock_irqrestore(&tty_termios_lock, flags); +} + +/* + * This guards the refcounted line discipline lists. The lock + * must be taken with irqs off because there are hangup path + * callers who will do ldisc lookups and cannot sleep. + */ + +static spinlock_t tty_ldisc_lock = SPIN_LOCK_UNLOCKED; +static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); +static struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table */ + int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc) { + unsigned long flags; + int ret = 0; + if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; + spin_lock_irqsave(&tty_ldisc_lock, flags); if (new_ldisc) { - ldiscs[disc] = *new_ldisc; - ldiscs[disc].flags |= LDISC_FLAG_DEFINED; - ldiscs[disc].num = disc; - } else - memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc)); + tty_ldiscs[disc] = *new_ldisc; + tty_ldiscs[disc].num = disc; + tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED; + tty_ldiscs[disc].refcount = 0; + } else { + if(tty_ldiscs[disc].refcount) + ret = -EBUSY; + else + tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED; + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return 0; + return ret; } EXPORT_SYMBOL(tty_register_ldisc); -/* Set the discipline of a tty line. */ +struct tty_ldisc *tty_ldisc_get(int disc) +{ + unsigned long flags; + struct tty_ldisc *ld; + + if (disc < N_TTY || disc >= NR_LDISCS) + return NULL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + + ld = &tty_ldiscs[disc]; + /* Check the entry is defined */ + if(ld->flags & LDISC_FLAG_DEFINED) + { + /* If the module is being unloaded we can't use it */ + if (!try_module_get(ld->owner)) + ld = NULL; + else /* lock it */ + ld->refcount++; + } + else + ld = NULL; + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return ld; +} + +EXPORT_SYMBOL_GPL(tty_ldisc_get); + +void tty_ldisc_put(int disc) +{ + struct tty_ldisc *ld; + unsigned long flags; + + if (disc < N_TTY || disc >= NR_LDISCS) + BUG(); + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld = &tty_ldiscs[disc]; + if(ld->refcount == 0) + BUG(); + ld->refcount --; + module_put(ld->owner); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + +EXPORT_SYMBOL_GPL(tty_ldisc_put); + +void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) +{ + tty->ldisc = *ld; + tty->ldisc.refcount = 0; +} + +/** + * tty_ldisc_try - internal helper + * @tty: the tty + * + * Make a single attempt to grab and bump the refcount on + * the tty ldisc. Return 0 on failure or 1 on success. This is + * used to implement both the waiting and non waiting versions + * of tty_ldisc_ref + */ + +static int tty_ldisc_try(struct tty_struct *tty) +{ + unsigned long flags; + struct tty_ldisc *ld; + int ret = 0; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld = &tty->ldisc; + if(test_bit(TTY_LDISC, &tty->flags)) + { + ld->refcount++; + ret = 1; + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return ret; +} + +/** + * tty_ldisc_ref_wait - wait for the tty ldisc + * @tty: tty device + * + * Dereference the line discipline for the terminal and take a + * reference to it. If the line discipline is in flux then + * wait patiently until it changes. + * + * Note: Must not be called from an IRQ/timer context. The caller + * must also be careful not to hold other locks that will deadlock + * against a discipline change, such as an existing ldisc reference + * (which we check for) + */ + +struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) +{ + /* wait_event is a macro */ + wait_event(tty_ldisc_wait, tty_ldisc_try(tty)); + if(tty->ldisc.refcount == 0) + printk(KERN_ERR "tty_ldisc_ref_wait\n"); + return &tty->ldisc; +} + +EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); + +/** + * tty_ldisc_ref - get the tty ldisc + * @tty: tty device + * + * Dereference the line discipline for the terminal and take a + * reference to it. If the line discipline is in flux then + * return NULL. Can be called from IRQ and timer functions. + */ + +struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) +{ + if(tty_ldisc_try(tty)) + return &tty->ldisc; + return NULL; +} + +EXPORT_SYMBOL_GPL(tty_ldisc_ref); + +/** + * tty_ldisc_deref - free a tty ldisc reference + * @ld: reference to free up + * + * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May + * be called in IRQ context. + */ + +void tty_ldisc_deref(struct tty_ldisc *ld) +{ + unsigned long flags; + + if(ld == NULL) + BUG(); + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if(ld->refcount == 0) + printk(KERN_ERR "tty_ldisc_deref: no references.\n"); + else + ld->refcount--; + if(ld->refcount == 0) + wake_up(&tty_ldisc_wait); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} + +EXPORT_SYMBOL_GPL(tty_ldisc_deref); + +/** + * tty_ldisc_enable - allow ldisc use + * @tty: terminal to activate ldisc on + * + * Set the TTY_LDISC flag when the line discipline can be called + * again. Do neccessary wakeups for existing sleepers. + * + * Note: nobody should set this bit except via this function. Clearing + * directly is allowed. + */ + +static void tty_ldisc_enable(struct tty_struct *tty) +{ + set_bit(TTY_LDISC, &tty->flags); + wake_up(&tty_ldisc_wait); +} + +/** + * tty_set_ldisc - set line discipline + * @tty: the terminal to set + * @ldisc: the line discipline + * + * Set the discipline of a tty line. Must be called from a process + * context. + */ + static int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval = 0; struct tty_ldisc o_ldisc; char buf[64]; + int work; + unsigned long flags; + struct tty_ldisc *ld; if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS)) return -EINVAL; + +restart: + + if (tty->ldisc.num == ldisc) + return 0; /* We are already in the desired discipline */ + + ld = tty_ldisc_get(ldisc); /* Eduardo Blanco */ /* Cyrus Durgin */ - if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) { + if (ld == NULL) { request_module("tty-ldisc-%d", ldisc); + ld = tty_ldisc_get(ldisc); } - if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) + if (ld == NULL) return -EINVAL; - if (tty->ldisc.num == ldisc) - return 0; /* We are already in the desired discipline */ - - if (!try_module_get(ldiscs[ldisc].owner)) - return -EINVAL; - o_ldisc = tty->ldisc; tty_wait_until_sent(tty, 0); + + /* + * Make sure we don't change while someone holds a + * reference to the line discipline. The TTY_LDISC bit + * prevents anyone taking a reference once it is clear. + * We need the lock to avoid racing reference takers. + */ + + spin_lock_irqsave(&tty_ldisc_lock, flags); + if(tty->ldisc.refcount) + { + /* Free the new ldisc we grabbed. Must drop the lock + first. */ + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + tty_ldisc_put(ldisc); + /* + * There are several reasons we may be busy, including + * random momentary I/O traffic. We must therefore + * retry. We could distinguish between blocking ops + * and retries if we made tty_ldisc_wait() smarter. That + * is up for discussion. + */ + if(wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0) + return -ERESTARTSYS; + goto restart; + } + clear_bit(TTY_LDISC, &tty->flags); + clear_bit(TTY_DONT_FLIP, &tty->flags); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + /* + * From this point on we know nobody has an ldisc + * usage reference, nor can they obtain one until + * we say so later on. + */ + + work = cancel_delayed_work(&tty->flip.work); + /* + * Wait for ->hangup_work and ->flip.work handlers to terminate + */ + + flush_scheduled_work(); /* Shutdown the current discipline. */ if (tty->ldisc.close) (tty->ldisc.close)(tty); /* Now set up the new line discipline. */ - tty->ldisc = ldiscs[ldisc]; - tty->termios->c_line = ldisc; + tty_ldisc_assign(tty, ld); + tty_set_termios_ldisc(tty, ldisc); if (tty->ldisc.open) retval = (tty->ldisc.open)(tty); if (retval < 0) { - tty->ldisc = o_ldisc; - tty->termios->c_line = tty->ldisc.num; + tty_ldisc_put(ldisc); + /* There is an outstanding reference here so this is safe */ + tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num)); + tty_set_termios_ldisc(tty, tty->ldisc.num); if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) { - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; + tty_ldisc_put(o_ldisc.num); + /* This driver is always present */ + tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); + tty_set_termios_ldisc(tty, N_TTY); if (tty->ldisc.open) { int r = tty->ldisc.open(tty); @@ -292,12 +558,27 @@ static int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_name(tty, buf), r); } } - } else { - module_put(o_ldisc.owner); } + /* At this point we hold a reference to the new ldisc and a + a reference to the old ldisc. If we ended up flipping back + to the existing ldisc we have two references to it */ if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc) tty->driver->set_ldisc(tty); + + tty_ldisc_put(o_ldisc.num); + + /* + * Allow ldisc referencing to occur as soon as the driver + * ldisc callback completes. + */ + + tty_ldisc_enable(tty); + + /* Restart it in case no characters kick it off. Safe if + already running */ + if(work) + schedule_delayed_work(&tty->flip.work, 1); return retval; } @@ -413,6 +694,53 @@ static struct file_operations hung_up_tty_fops = { static spinlock_t redirect_lock = SPIN_LOCK_UNLOCKED; static struct file *redirect; + +/** + * tty_wakeup - request more data + * @tty: terminal + * + * Internal and external helper for wakeups of tty. This function + * informs the line discipline if present that the driver is ready + * to receive more output data. + */ + +void tty_wakeup(struct tty_struct *tty) +{ + struct tty_ldisc *ld; + + if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) { + ld = tty_ldisc_ref(tty); + if(ld) { + if(ld->write_wakeup) + ld->write_wakeup(tty); + tty_ldisc_deref(ld); + } + } + wake_up_interruptible(&tty->write_wait); +} + +EXPORT_SYMBOL_GPL(tty_wakeup); + +/** + * tty_ldisc_flush - flush line discipline queue + * @tty: tty + * + * Flush the line discipline queue (if any) for this tty. If there + * is no line discipline active this is a no-op. + */ + +void tty_ldisc_flush(struct tty_struct *tty) +{ + struct tty_ldisc *ld = tty_ldisc_ref(tty); + if(ld) { + if(ld->flush_buffer) + ld->flush_buffer(tty); + tty_ldisc_deref(ld); + } +} + +EXPORT_SYMBOL_GPL(tty_ldisc_flush); + /* * This can be called by the "eventd" kernel thread. That is process synchronous, * but doesn't hold any locks, so we need to make sure we have the appropriate @@ -424,6 +752,7 @@ void do_tty_hangup(void *data) struct file * cons_filp = NULL; struct file *filp, *f = NULL; struct task_struct *p; + struct tty_ldisc *ld; int closecount = 0, n; if (!tty) @@ -441,6 +770,7 @@ void do_tty_hangup(void *data) check_tty_count(tty, "do_tty_hangup"); file_list_lock(); + /* This breaks for file handles being sent over AF_UNIX sockets ? */ list_for_each_entry(filp, &tty->tty_files, f_list) { if (filp->f_op->write == redirected_tty_write) cons_filp = filp; @@ -453,21 +783,25 @@ void do_tty_hangup(void *data) file_list_unlock(); /* FIXME! What are the locking issues here? This may me overdoing things.. - * this question is especially important now that we've removed the irqlock. */ - { - unsigned long flags; + * this question is especially important now that we've removed the irqlock. */ - local_irq_save(flags); // FIXME: is this safe? - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + ld = tty_ldisc_ref(tty); + if(ld != NULL) /* We may have no line discipline at this point */ + { + if (ld->flush_buffer) + ld->flush_buffer(tty); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - local_irq_restore(flags); // FIXME: is this safe? + ld->write_wakeup) + ld->write_wakeup(tty); + if (ld->hangup) + ld->hangup(tty); } + /* FIXME: Once we trust the LDISC code better we can wait here for + ldisc completion and fix the driver call race */ + wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); @@ -476,22 +810,19 @@ void do_tty_hangup(void *data) * N_TTY. */ if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) + { + unsigned long flags; + spin_lock_irqsave(&tty_termios_lock, flags); *tty->termios = tty->driver->init_termios; - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - module_put(tty->ldisc.owner); - - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) { - int i = (tty->ldisc.open)(tty); - if (i < 0) - printk(KERN_ERR "do_tty_hangup: N_TTY open: " - "error %d\n", -i); - } + spin_unlock_irqrestore(&tty_termios_lock, flags); } + /* Defer ldisc switch */ + /* tty_deferred_ldisc_switch(N_TTY); + + This should get done automatically when the port closes and + tty_release is called */ + read_lock(&tasklist_lock); if (tty->session > 0) { do_each_task_pid(tty->session, PIDTYPE_SID, p) { @@ -523,6 +854,17 @@ void do_tty_hangup(void *data) tty->driver->close(tty, cons_filp); } else if (tty->driver->hangup) (tty->driver->hangup)(tty); + + /* We don't want to have driver/ldisc interactions beyond + the ones we did here. The driver layer expects no + calls after ->hangup() from the ldisc side. However we + can't yet guarantee all that */ + + set_bit(TTY_HUPPED, &tty->flags); + if (ld) { + tty_ldisc_enable(tty); + tty_ldisc_deref(ld); + } unlock_kernel(); if (f) fput(f); @@ -638,9 +980,9 @@ void start_tty(struct tty_struct *tty) } if (tty->driver->start) (tty->driver->start)(tty); - if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + + /* If we have a running line discipline it may need kicking */ + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } @@ -652,6 +994,7 @@ static ssize_t tty_read(struct file * file, char __user * buf, size_t count, int i; struct tty_struct * tty; struct inode *inode; + struct tty_ldisc *ld; tty = (struct tty_struct *)file->private_data; inode = file->f_dentry->d_inode; @@ -660,11 +1003,15 @@ static ssize_t tty_read(struct file * file, char __user * buf, size_t count, if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; + /* We want to wait for the line discipline to sort out in this + situation */ + ld = tty_ldisc_ref_wait(tty); lock_kernel(); - if (tty->ldisc.read) - i = (tty->ldisc.read)(tty,file,buf,count); + if (ld->read) + i = (ld->read)(tty,file,buf,count); else i = -EIO; + tty_ldisc_deref(ld); unlock_kernel(); if (i > 0) inode->i_atime = CURRENT_TIME; @@ -726,16 +1073,23 @@ static ssize_t tty_write(struct file * file, const char __user * buf, size_t cou { struct tty_struct * tty; struct inode *inode = file->f_dentry->d_inode; - + ssize_t ret; + struct tty_ldisc *ld; + tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode, "tty_write")) return -EIO; if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; - if (!tty->ldisc.write) - return -EIO; - return do_tty_write(tty->ldisc.write, tty, file, + + ld = tty_ldisc_ref_wait(tty); + if (!ld->write) + ret = -EIO; + else + ret = do_tty_write(ld->write, tty, file, (const unsigned char __user *)buf, count); + tty_ldisc_deref(ld); + return ret; } ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t count, @@ -932,6 +1286,7 @@ static int init_dev(struct tty_driver *driver, int idx, * If we fail here just call release_mem to clean up. No need * to decrement the use counts, as release_mem doesn't care. */ + if (tty->ldisc.open) { retval = (tty->ldisc.open)(tty); if (retval) @@ -944,7 +1299,9 @@ static int init_dev(struct tty_driver *driver, int idx, (tty->ldisc.close)(tty); goto release_mem_out; } + tty_ldisc_enable(o_tty); } + tty_ldisc_enable(tty); goto success; /* @@ -973,6 +1330,9 @@ fast_track: tty->count++; tty->driver = driver; /* N.B. why do this every time?? */ + /* FIXME */ + if(!test_bit(TTY_LDISC, &tty->flags)) + printk(KERN_ERR "init_dev but no ldisc\n"); success: *ret_tty = tty; @@ -1076,6 +1436,7 @@ static void release_dev(struct file * filp) int devpts_master, devpts; int idx; char buf[64]; + unsigned long flags; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "release_dev")) @@ -1152,7 +1513,6 @@ static void release_dev(struct file * filp) } } #endif - if (tty->driver->close) tty->driver->close(tty, filp); @@ -1276,36 +1636,58 @@ static void release_dev(struct file * filp) #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "freeing tty structure..."); #endif - /* * Prevent flush_to_ldisc() from rescheduling the work for later. Then - * kill any delayed work. + * kill any delayed work. As this is the final close it does not + * race with the set_ldisc code path. */ + clear_bit(TTY_LDISC, &tty->flags); clear_bit(TTY_DONT_FLIP, &tty->flags); cancel_delayed_work(&tty->flip.work); /* * Wait for ->hangup_work and ->flip.work handlers to terminate */ + flush_scheduled_work(); - + + /* + * Wait for any short term users (we know they are just driver + * side waiters as the file is closing so user count on the file + * side is zero. + */ + spin_lock_irqsave(&tty_ldisc_lock, flags); + while(tty->ldisc.refcount) + { + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); + spin_lock_irqsave(&tty_ldisc_lock, flags); + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); /* * Shutdown the current line discipline, and reset it to N_TTY. * N.B. why reset ldisc when we're releasing the memory?? + * + * FIXME: this MUST get fixed for the new reflocking */ if (tty->ldisc.close) (tty->ldisc.close)(tty); - module_put(tty->ldisc.owner); + tty_ldisc_put(tty->ldisc.num); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; + /* + * Switch the line discipline back + */ + tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); + tty_set_termios_ldisc(tty,N_TTY); if (o_tty) { + /* FIXME: could o_tty be in setldisc here ? */ + clear_bit(TTY_LDISC, &o_tty->flags); if (o_tty->ldisc.close) (o_tty->ldisc.close)(o_tty); - module_put(o_tty->ldisc.owner); - o_tty->ldisc = ldiscs[N_TTY]; + tty_ldisc_put(o_tty->ldisc.num); + tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY)); + tty_set_termios_ldisc(o_tty,N_TTY); } - /* * The release_mem function takes care of the details of clearing * the slots and preserving the termios structure. @@ -1345,6 +1727,7 @@ static int tty_open(struct inode * inode, struct file * filp) unsigned short saved_flags = filp->f_flags; nonseekable_open(inode, filp); + retry_open: noctty = filp->f_flags & O_NOCTTY; index = -1; @@ -1508,14 +1891,18 @@ static int tty_release(struct inode * inode, struct file * filp) static unsigned int tty_poll(struct file * filp, poll_table * wait) { struct tty_struct * tty; + struct tty_ldisc *ld; + int ret = 0; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_poll")) return 0; - - if (tty->ldisc.poll) - return (tty->ldisc.poll)(tty, filp, wait); - return 0; + + ld = tty_ldisc_ref_wait(tty); + if (ld->poll) + ret = (ld->poll)(tty, filp, wait); + tty_ldisc_deref(ld); + return ret; } static int tty_fasync(int fd, struct file * filp, int on) @@ -1547,12 +1934,15 @@ static int tty_fasync(int fd, struct file * filp, int on) static int tiocsti(struct tty_struct *tty, char __user *p) { char ch, mbz = 0; - + struct tty_ldisc *ld; + if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN)) return -EPERM; if (get_user(ch, p)) return -EFAULT; - tty->ldisc.receive_buf(tty, &ch, &mbz, 1); + ld = tty_ldisc_ref_wait(tty); + ld->receive_buf(tty, &ch, &mbz, 1); + tty_ldisc_deref(ld); return 0; } @@ -1800,6 +2190,7 @@ int tty_ioctl(struct inode * inode, struct file * file, struct tty_struct *tty, *real_tty; void __user *p = (void __user *)arg; int retval; + struct tty_ldisc *ld; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode, "tty_ioctl")) @@ -1889,6 +2280,7 @@ int tty_ioctl(struct inode * inode, struct file * file, case TIOCGSID: return tiocgsid(tty, real_tty, p); case TIOCGETD: + /* FIXME: check this is ok */ return put_user(tty->ldisc.num, (int __user *)p); case TIOCSETD: return tiocsetd(tty, p); @@ -1927,16 +2319,19 @@ int tty_ioctl(struct inode * inode, struct file * file, return tty_tiocmset(tty, file, cmd, p); } if (tty->driver->ioctl) { - int retval = (tty->driver->ioctl)(tty, file, cmd, arg); + retval = (tty->driver->ioctl)(tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } - if (tty->ldisc.ioctl) { - int retval = (tty->ldisc.ioctl)(tty, file, cmd, arg); - if (retval != -ENOIOCTLCMD) - return retval; + ld = tty_ldisc_ref_wait(tty); + retval = -EINVAL; + if (ld->ioctl) { + retval = ld->ioctl(tty, file, cmd, arg); + if (retval == -ENOIOCTLCMD) + retval = -EINVAL; } - return -EINVAL; + tty_ldisc_deref(ld); + return retval; } @@ -1969,14 +2364,21 @@ static void __do_SAK(void *arg) int session; int i; struct file *filp; + struct tty_ldisc *disc; if (!tty) return; session = tty->session; - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + /* We don't want an ldisc switch during this */ + disc = tty_ldisc_ref(tty); + if (disc && disc->flush_buffer) + disc->flush_buffer(tty); + tty_ldisc_deref(disc); + if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); + read_lock(&tasklist_lock); do_each_task_pid(session, PIDTYPE_SID, p) { if (p->signal->tty == tty || session > 0) { @@ -2028,24 +2430,29 @@ EXPORT_SYMBOL(do_SAK); /* * This routine is called out of the software interrupt to flush data - * from the flip buffer to the line discipline. + * from the flip buffer to the line discipline. */ + static void flush_to_ldisc(void *private_) { struct tty_struct *tty = (struct tty_struct *) private_; unsigned char *cp; char *fp; int count; - unsigned long flags; + unsigned long flags; + struct tty_ldisc *disc; + + disc = tty_ldisc_ref(tty); + if (disc == NULL) /* !TTY_LDISC */ + return; if (test_bit(TTY_DONT_FLIP, &tty->flags)) { /* * Do it after the next timer tick: */ schedule_delayed_work(&tty->flip.work, 1); - return; + goto out; } - spin_lock_irqsave(&tty->read_lock, flags); if (tty->flip.buf_num) { cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE; @@ -2064,7 +2471,31 @@ static void flush_to_ldisc(void *private_) tty->flip.count = 0; spin_unlock_irqrestore(&tty->read_lock, flags); - tty->ldisc.receive_buf(tty, cp, fp, count); + disc->receive_buf(tty, cp, fp, count); +out: + tty_ldisc_deref(disc); +} + +/* + * Call the ldisc flush directly from a driver. This function may + * return an error and need retrying by the user. + */ + +int tty_push_data(struct tty_struct *tty, unsigned char *cp, unsigned char *fp, int count) +{ + int ret = 0; + struct tty_ldisc *disc; + + disc = tty_ldisc_ref(tty); + if(test_bit(TTY_DONT_FLIP, &tty->flags)) + ret = -EAGAIN; + else if(disc == NULL) + ret = -EIO; + else + disc->receive_buf(tty, cp, fp, count); + tty_ldisc_deref(disc); + return ret; + } /* @@ -2086,9 +2517,20 @@ static int baud_table[] = { static int n_baud_table = ARRAY_SIZE(baud_table); +/** + * tty_termios_baud_rate + * @termios: termios structure + * + * Convert termios baud rate data into a speed. This should be called + * with the termios lock held if this termios is a terminal termios + * structure. May change the termios data. + */ + int tty_termios_baud_rate(struct termios *termios) { - unsigned int cbaud = termios->c_cflag & CBAUD; + unsigned int cbaud; + + cbaud = termios->c_cflag & CBAUD; if (cbaud & CBAUDEX) { cbaud &= ~CBAUDEX; @@ -2098,12 +2540,20 @@ int tty_termios_baud_rate(struct termios *termios) else cbaud += 15; } - return baud_table[cbaud]; } EXPORT_SYMBOL(tty_termios_baud_rate); +/** + * tty_get_baud_rate - get tty bit rates + * @tty: tty to query + * + * Returns the baud rate as an integer for this terminal. The + * termios lock must be held by the caller and the terminal bit + * flags may be updated. + */ + int tty_get_baud_rate(struct tty_struct *tty) { int baud = tty_termios_baud_rate(tty->termios); @@ -2122,6 +2572,17 @@ int tty_get_baud_rate(struct tty_struct *tty) EXPORT_SYMBOL(tty_get_baud_rate); +/** + * tty_flip_buffer_push - terminal + * @tty: tty to push + * + * Queue a push of the terminal flip buffers to the line discipline. This + * function must not be called from IRQ context if tty->low_latency is set. + * + * In the event of the queue being busy for flipping the work will be + * held off and retried later. + */ + void tty_flip_buffer_push(struct tty_struct *tty) { if (tty->low_latency) @@ -2139,7 +2600,7 @@ static void initialize_tty_struct(struct tty_struct *tty) { memset(tty, 0, sizeof(struct tty_struct)); tty->magic = TTY_MAGIC; - tty->ldisc = ldiscs[N_TTY]; + tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); tty->pgrp = -1; tty->flip.char_buf_ptr = tty->flip.char_buf; tty->flip.flag_buf_ptr = tty->flip.flag_buf; diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index ae924c7cb00c..3ed25cc8a833 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -29,6 +29,8 @@ #undef DEBUG +extern spinlock_t tty_termios_lock; + /* * Internal flag options for termios setting behavior */ @@ -98,8 +100,18 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios { int canon_change; struct termios old_termios = *tty->termios; + struct tty_ldisc *ld; + unsigned long flags; + + /* + * Perform the actual termios internal changes under lock. + */ + + + /* FIXME: we need to decide on some locking/ordering semantics + for the set_termios notification eventually */ + spin_lock_irqsave(&tty_termios_lock, flags); - local_irq_disable(); // FIXME: is this safe? *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON; @@ -109,12 +121,13 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios tty->canon_data = 0; tty->erasing = 0; } - local_irq_enable(); // FIXME: is this safe? + + if (canon_change && !L_ICANON(tty) && tty->read_cnt) /* Get characters left over from canonical mode. */ wake_up_interruptible(&tty->read_wait); - /* see if packet mode change of state */ + /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { int old_flow = ((old_termios.c_iflag & IXON) && @@ -132,17 +145,23 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios wake_up_interruptible(&tty->link->read_wait); } } - + if (tty->driver->set_termios) (*tty->driver->set_termios)(tty, &old_termios); - if (tty->ldisc.set_termios) - (*tty->ldisc.set_termios)(tty, &old_termios); + ld = tty_ldisc_ref(tty); + if (ld != NULL) { + if (ld->set_termios) + (ld->set_termios)(tty, &old_termios); + tty_ldisc_deref(ld); + } + spin_unlock_irqrestore(&tty_termios_lock, flags); } static int set_termios(struct tty_struct * tty, void __user *arg, int opt) { struct termios tmp_termios; + struct tty_ldisc *ld; int retval = tty_check_change(tty); if (retval) @@ -159,9 +178,14 @@ static int set_termios(struct tty_struct * tty, void __user *arg, int opt) return -EFAULT; } - if ((opt & TERMIOS_FLUSH) && tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - + ld = tty_ldisc_ref(tty); + + if (ld != NULL) { + if ((opt & TERMIOS_FLUSH) && ld->flush_buffer) + ld->flush_buffer(tty); + tty_ldisc_deref(ld); + } + if (opt & TERMIOS_WAIT) { tty_wait_until_sent(tty, 0); if (signal_pending(current)) @@ -225,12 +249,16 @@ static int get_sgflags(struct tty_struct * tty) static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) { struct sgttyb tmp; + unsigned long flags; + spin_lock_irqsave(&tty_termios_lock, flags); tmp.sg_ispeed = 0; tmp.sg_ospeed = 0; tmp.sg_erase = tty->termios->c_cc[VERASE]; tmp.sg_kill = tty->termios->c_cc[VKILL]; tmp.sg_flags = get_sgflags(tty); + spin_unlock_irqrestore(&tty_termios_lock, flags); + return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -269,12 +297,16 @@ static int set_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) retval = tty_check_change(tty); if (retval) return retval; - termios = *tty->termios; + if (copy_from_user(&tmp, sgttyb, sizeof(tmp))) return -EFAULT; + + spin_lock_irqsave(&tty_termios_lock, flags); + termios = *tty->termios; termios.c_cc[VERASE] = tmp.sg_erase; termios.c_cc[VKILL] = tmp.sg_kill; set_sgflags(&termios, tmp.sg_flags); + spin_unlock_irqrestore(&tty_termios_lock, flags); change_termios(tty, &termios); return 0; } @@ -365,6 +397,8 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file, struct tty_struct * real_tty; void __user *p = (void __user *)arg; int retval; + struct tty_ldisc *ld; + unsigned long flags; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) @@ -443,22 +477,26 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file, retval = tty_check_change(tty); if (retval) return retval; + + ld = tty_ldisc_ref(tty); switch (arg) { case TCIFLUSH: - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + if (ld->flush_buffer) + ld->flush_buffer(tty); break; case TCIOFLUSH: - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + if (ld->flush_buffer) + ld->flush_buffer(tty); /* fall through */ case TCOFLUSH: if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); break; default: + tty_ldisc_deref(ld); return -EINVAL; } + tty_ldisc_deref(ld); return 0; case TIOCOUTQ: return put_user(tty->driver->chars_in_buffer ? @@ -504,9 +542,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file, case TIOCSSOFTCAR: if (get_user(arg, (unsigned int __user *) arg)) return -EFAULT; + spin_lock_irqsave(&tty_termios_lock, flags); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); + spin_unlock_irqrestore(&tty_termios_lock, flags); return 0; default: return -ENOIOCTLCMD; diff --git a/drivers/char/viocons.c b/drivers/char/viocons.c index df9d4de9bc3b..dcfcef4be160 100644 --- a/drivers/char/viocons.c +++ b/drivers/char/viocons.c @@ -422,10 +422,7 @@ static void send_buffers(struct port_info *pi) pi->overflowMessage = 0; if (pi->tty) { - if ((pi->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - (pi->tty->ldisc.write_wakeup)) - (pi->tty->ldisc.write_wakeup)(pi->tty); - wake_up_interruptible(&pi->tty->write_wait); + tty_wakeup(pi->tty); } } diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index 6f78334173cb..19ba83635dd7 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c @@ -544,12 +544,8 @@ static irqreturn_t scc_tx_int(int irq, void *data, struct pt_regs *fp) SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); /* disable tx_int on next tx underrun? */ port->gs.flags &= ~GS_TX_INTEN; } - if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) { - if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - port->gs.tty->ldisc.write_wakeup) - (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); - wake_up_interruptible(&port->gs.tty->write_wait); - } + if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) + tty_wakeup(port->gs.tty); SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); return IRQ_HANDLED; diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 682b44e9daf0..dd8e2f7817f5 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -536,8 +536,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, default: return -EINVAL; } - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); return 0; case KDGKBMODE: diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 3429d57e297e..b49a308116c3 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -436,51 +436,62 @@ static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) struct sk_buff *nskb; int datalen; u16 errcode, datahandle; - + struct tty_ldisc *ld; + datalen = skb->len - CAPIMSG_LEN(skb->data); - if (mp->tty) { - if (mp->tty->ldisc.receive_buf == 0) { - printk(KERN_ERR "capi: ldisc has no receive_buf function\n"); - return -1; - } - if (mp->ttyinstop) { + if (mp->tty == NULL) + { +#ifdef _DEBUG_DATAFLOW + printk(KERN_DEBUG "capi: currently no receiver\n"); +#endif + return -1; + } + + ld = tty_ldisc_ref(mp->tty); + if (ld == NULL) + return -1; + if (ld->receive_buf == NULL) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) - printk(KERN_DEBUG "capi: recv tty throttled\n"); + printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n"); #endif - return -1; - } - if (mp->tty->ldisc.receive_room && - mp->tty->ldisc.receive_room(mp->tty) < datalen) { + goto bad; + } + if (mp->ttyinstop) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) - printk(KERN_DEBUG "capi: no room in tty\n"); + printk(KERN_DEBUG "capi: recv tty throttled\n"); #endif - return -1; - } - if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) { - printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); - return -1; - } - datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); - errcode = capi20_put_message(mp->ap, nskb); - if (errcode != CAPI_NOERROR) { - printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", - errcode); - kfree_skb(nskb); - return -1; - } - (void)skb_pull(skb, CAPIMSG_LEN(skb->data)); -#ifdef _DEBUG_DATAFLOW - printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", - datahandle, skb->len); + goto bad; + } + if (ld->receive_room && + ld->receive_room(mp->tty) < datalen) { +#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) + printk(KERN_DEBUG "capi: no room in tty\n"); #endif - mp->tty->ldisc.receive_buf(mp->tty, skb->data, NULL, skb->len); - kfree_skb(skb); - return 0; - + goto bad; } + if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) { + printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); + goto bad; + } + datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); + errcode = capi20_put_message(mp->ap, nskb); + if (errcode != CAPI_NOERROR) { + printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", + errcode); + kfree_skb(nskb); + goto bad; + } + (void)skb_pull(skb, CAPIMSG_LEN(skb->data)); #ifdef _DEBUG_DATAFLOW - printk(KERN_DEBUG "capi: currently no receiver\n"); + printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", + datahandle, skb->len); #endif + ld->receive_buf(mp->tty, skb->data, NULL, skb->len); + kfree_skb(skb); + tty_ldisc_deref(ld); + return 0; +bad: + tty_ldisc_deref(ld); return -1; } @@ -614,6 +625,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { + datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2); #ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n", @@ -633,10 +645,8 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) #endif kfree_skb(skb); (void)capiminor_del_ack(mp, datahandle); - if (mp->tty) { - if (mp->tty->ldisc.write_wakeup) - mp->tty->ldisc.write_wakeup(mp->tty); - } + if (mp->tty) + tty_wakeup(tty); (void)handle_minor_send(mp); } else { diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 02b912ab138c..b408ce22802f 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -296,10 +296,7 @@ isdn_tty_tint(modem_info * info) info->send_outstanding++; info->msr &= ~UART_MSR_CTS; info->lsr &= ~UART_LSR_TEMT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); return; } if (slen < 0) { @@ -1174,10 +1171,7 @@ isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int co /* If DLE decoding results in zero-transmit, but * c originally was non-zero, do a wakeup. */ - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); info->msr |= UART_MSR_CTS; info->lsr |= UART_LSR_TEMT; } @@ -1290,9 +1284,7 @@ isdn_tty_flush_buffer(struct tty_struct *tty) isdn_tty_cleanup_xmit(info); info->xmit_count = 0; wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); + tty_wakeup(tty); } static void @@ -1747,10 +1739,10 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) } dev->modempoll--; isdn_tty_shutdown(info); + if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); info->tty = NULL; info->ncarrier = 0; tty->closing = 0; @@ -2681,8 +2673,7 @@ isdn_tty_modem_result(int code, modem_info * info) if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { return; } - if (info->tty->ldisc.flush_buffer) - info->tty->ldisc.flush_buffer(info->tty); + tty_ldisc_flush(tty); if ((info->flags & ISDN_ASYNC_CHECK_CD) && (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) && (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) { diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c index 76ae482eb01d..a3a5372f7aeb 100644 --- a/drivers/macintosh/macserial.c +++ b/drivers/macintosh/macserial.c @@ -713,12 +713,8 @@ static void do_softint(void *private_) if (!tty) return; - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - } + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) + tty_wakeup(tty); } static int startup(struct mac_serial * info) @@ -1564,10 +1560,7 @@ static void rs_flush_buffer(struct tty_struct *tty) spin_lock_irqsave(&info->lock, flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; spin_unlock_irqrestore(&info->lock, flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1994,8 +1987,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = 0; diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index c7076aa0b3c3..e2a28b562446 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -602,8 +602,6 @@ static int ax25_open(struct tty_struct *tty) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); /* Restore default settings */ ax->dev->type = ARPHRD_AX25; diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index b67441d0a234..13b6e627a086 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -509,13 +509,6 @@ static int irtty_open(struct tty_struct *tty) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); -/* from old irtty - but what is it good for? - * we _are_ the ldisc and we _don't_ implement flush_buffer! - * - * if (tty->ldisc.flush_buffer) - * tty->ldisc.flush_buffer(tty); - */ - /* apply mtt override */ sir_tty_drv.qos_mtt_bits = qos_mtt_bits; diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index e44b189c6f26..b64f284c18f8 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -122,6 +122,9 @@ static struct ppp_channel_ops async_ops = { * frees the memory that ppp_asynctty_receive is using. The best * way to fix this is to use a rwlock in the tty struct, but for now * we use a single global rwlock for all ttys in ppp line discipline. + * + * FIXME: this is no longer true. The _close path for the ldisc is + * now guaranteed to be sane. */ static rwlock_t disc_data_lock = RW_LOCK_UNLOCKED; @@ -144,7 +147,8 @@ static void ap_put(struct asyncppp *ap) } /* - * Called when a tty is put into PPP line discipline. + * Called when a tty is put into PPP line discipline. Called in process + * context. */ static int ppp_asynctty_open(struct tty_struct *tty) @@ -255,6 +259,11 @@ ppp_asynctty_write(struct tty_struct *tty, struct file *file, return -EAGAIN; } +/* + * Called in process context only. May be re-entered by multiple + * ioctl calling threads. + */ + static int ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) @@ -716,7 +725,8 @@ flush: /* * Flush output from our internal buffers. - * Called for the TCFLSH ioctl. + * Called for the TCFLSH ioctl. Can be entered in parallel + * but this is covered by the xmit_lock. */ static void ppp_async_flush_output(struct asyncppp *ap) @@ -816,7 +826,9 @@ process_input_packet(struct asyncppp *ap) skb_trim(skb, 0); } -/* called when the tty driver has data for us. */ +/* Called when the tty driver has data for us. Runs parallel with the + other ldisc functions but will not be re-entered */ + static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf, char *flags, int count) diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index a8f8908587a4..a59c8f354947 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -175,6 +175,8 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count) * frees the memory that ppp_synctty_receive is using. The best * way to fix this is to use a rwlock in the tty struct, but for now * we use a single global rwlock for all ttys in ppp line discipline. + * + * FIXME: Fixed in tty_io nowdays. */ static rwlock_t disc_data_lock = RW_LOCK_UNLOCKED; diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 6209a35aaf83..0c1995bc2848 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -672,7 +672,9 @@ static int slip_receive_room(struct tty_struct *tty) * Handle the 'receiver data ready' interrupt. * This function is called by the 'tty_io' module in the kernel when * a block of SLIP data has been received, which can now be decapsulated - * and sent on to some IP layer for further processing. + * and sent on to some IP layer for further processing. This will not + * be re-entered while running but other ldisc functions may be called + * in parallel */ static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) @@ -841,9 +843,11 @@ sl_alloc(dev_t line) * SLIP line discipline is called for. Because we are * sure the tty line exists, we only have to link it to * a free SLIP channel... + * + * Called in process context serialized from other ldisc calls. */ -static int -slip_open(struct tty_struct *tty) + +static int slip_open(struct tty_struct *tty) { struct slip *sl; int err; @@ -876,11 +880,11 @@ slip_open(struct tty_struct *tty) tty->disc_data = sl; sl->line = tty_devnum(tty); sl->pid = current->pid; + + /* FIXME: already done before we were called - seems this can go */ if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - + if (!test_bit(SLF_INUSE, &sl->flags)) { /* Perform the low-level SLIP initialization. */ if ((err = sl_alloc_bufs(sl, SL_MTU)) != 0) @@ -923,6 +927,9 @@ err_exit: } /* + + FIXME: 1,2 are fixed 3 was never true anyway. + Let me to blame a bit. 1. TTY module calls this funstion on soft interrupt. 2. TTY module calls this function WITH MASKED INTERRUPTS! @@ -941,9 +948,8 @@ err_exit: /* * Close down a SLIP channel. - * This means flushing out any pending queues, and then restoring the - * TTY line discipline to what it was before it got hooked to SLIP - * (which usually is TTY again). + * This means flushing out any pending queues, and then returning. This + * call is serialized against other ldisc functions. */ static void slip_close(struct tty_struct *tty) diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index 181f356636ed..26fe097925ce 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -634,14 +634,8 @@ static void cpc_tty_flush_buffer(struct tty_struct *tty) } CPC_TTY_DBG("%s: call wake_up_interruptible\n",cpc_tty->name); - - wake_up_interruptible(&tty->write_wait); - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){ - CPC_TTY_DBG("%s: call line disc. wake up\n",cpc_tty->name); - tty->ldisc.write_wakeup(tty); - } + tty_wakeup(tty); return; } @@ -692,9 +686,11 @@ static void cpc_tty_rx_work(void * data) st_cpc_tty_area *cpc_tty; volatile st_cpc_rx_buf * buf; char flags=0,flg_rx=1; + struct tty_ldisc *ld; if (cpc_tty_cnt == 0) return; + for (i=0; (i < 4) && flg_rx ; i++) { flg_rx = 0; port = (int) data; @@ -702,11 +698,18 @@ static void cpc_tty_rx_work(void * data) cpc_tty = &cpc_tty_area[port]; if ((buf=cpc_tty->buf_rx.first) != 0) { - - if (cpc_tty->tty && (cpc_tty->tty->ldisc.receive_buf)) { - CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name); - cpc_tty->tty->ldisc.receive_buf(cpc_tty->tty, (char *)(buf->data), - &flags, buf->size); + + if(cpc_tty->tty) + { + ld = tty_ldisc_ref(cpc_tty); + if(ld) + { + if (ld->receive_buf)) { + CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name); + ld->receive_buf(cpc_tty->tty, (char *)(buf->data), &flags, buf->size); + } + tty_ldisc_deref(ld); + } } cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next; kfree((unsigned char *)buf); @@ -916,13 +919,7 @@ static void cpc_tty_tx_work(void *data) CPC_TTY_DBG("%s: the interface is not opened\n",cpc_tty->name); return; } - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){ - CPC_TTY_DBG("%s:call line disc. wakeup\n",cpc_tty->name); - tty->ldisc.write_wakeup (tty); - } - - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } /* diff --git a/drivers/net/wan/sdla_chdlc.c b/drivers/net/wan/sdla_chdlc.c index b00fb7837e72..e66c250ea8d4 100644 --- a/drivers/net/wan/sdla_chdlc.c +++ b/drivers/net/wan/sdla_chdlc.c @@ -3628,11 +3628,7 @@ static void tty_poll_work (void* data) if ((tty=card->tty)==NULL) return; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); #if defined(SERIAL_HAVE_POLL_WAIT) wake_up_interruptible(&tty->poll_wait); #endif @@ -3857,6 +3853,7 @@ static void wanpipe_tty_receive(sdla_t *card, unsigned addr, unsigned int len) char fp=0; struct tty_struct *tty; int i; + struct tty_ldisc *ld; if (!card->tty_open){ dbg_printk(KERN_INFO "%s: TTY not open during receive\n", @@ -3944,8 +3941,11 @@ static void wanpipe_tty_receive(sdla_t *card, unsigned addr, unsigned int len) len -= offset; } sdla_peek(&card->hw, addr, card->tty_rx+offset, len); - if (tty->ldisc.receive_buf){ - tty->ldisc.receive_buf(tty,card->tty_rx,&fp,olen); + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->receive_buf) + ld->receive_buf(tty,card->tty_rx,&fp,olen); + tty_ldisc_deref(ld); }else{ if (net_ratelimit()){ printk(KERN_INFO @@ -4252,14 +4252,10 @@ static void wanpipe_tty_flush_buffer(struct tty_struct *tty) if (!tty) return; - wake_up_interruptible(&tty->write_wait); #if defined(SERIAL_HAVE_POLL_WAIT) wake_up_interruptible(&tty->poll_wait); #endif - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - + tty_wakeup(tty); return; } diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c index c9331f589645..7f44588cb6e7 100644 --- a/drivers/net/wireless/strip.c +++ b/drivers/net/wireless/strip.c @@ -2666,8 +2666,6 @@ static int strip_open(struct tty_struct *tty) tty->disc_data = strip_info; if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); /* * Restore default settings diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index e92bb9473037..9c72ffb655f8 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -366,10 +366,7 @@ raw3215_tasklet(void *data) tty = raw->tty; if (tty != NULL && RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } @@ -1055,10 +1052,7 @@ tty3215_flush_buffer(struct tty_struct *tty) raw = (struct raw3215_info *) tty->driver_data; raw3215_flush_buffer(raw); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index a1ad3758080e..9cb4613db3f6 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -277,10 +277,7 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) wake_up(&sclp_tty_waitq); /* check if the tty needs a wake up call */ if (sclp_tty != NULL) { - if ((sclp_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - sclp_tty->ldisc.write_wakeup) - (sclp_tty->ldisc.write_wakeup)(sclp_tty); - wake_up_interruptible(&sclp_tty->write_wait); + tty_wakeup(tty); } } diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 22760a1208d8..c516e002b61c 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -139,10 +139,7 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request) wake_up(&sclp_vt220_waitq); /* Check if the tty needs a wake up call */ if (sclp_vt220_tty != NULL) { - if ((sclp_vt220_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - (sclp_vt220_tty->ldisc.write_wakeup != NULL)) - (sclp_vt220_tty->ldisc.write_wakeup)(sclp_vt220_tty); - wake_up_interruptible(&sclp_vt220_tty->write_wait); + tty_wakeup(tty); } } diff --git a/drivers/s390/net/ctctty.c b/drivers/s390/net/ctctty.c index 6290b5c56d56..2806b2d156c3 100644 --- a/drivers/s390/net/ctctty.c +++ b/drivers/s390/net/ctctty.c @@ -307,10 +307,7 @@ ctc_tty_tint(ctc_tty_info * info) info->flags &= ~CTC_ASYNC_TX_LINESTAT; if (tty) { - if (wake && (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } return (skb_queue_empty(&info->tx_queue) ? 0 : 1); @@ -589,9 +586,7 @@ ctc_tty_flush_buffer(struct tty_struct *tty) info->lsr |= UART_LSR_TEMT; spin_unlock_irqrestore(&ctc_tty_lock, flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); + tty_wakeup(tty); ex: DBF_TEXT_(trace, 2, "ex: %s ", __FUNCTION__); return; @@ -1066,8 +1061,7 @@ ctc_tty_close(struct tty_struct *tty, struct file *filp) skb_queue_purge(&info->tx_queue); info->lsr |= UART_LSR_TEMT; } - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); info->tty = 0; tty->closing = 0; if (info->blocked_open) { diff --git a/drivers/sbus/char/aurora.c b/drivers/sbus/char/aurora.c index d45619f3b563..1ff81bec8741 100644 --- a/drivers/sbus/char/aurora.c +++ b/drivers/sbus/char/aurora.c @@ -1531,8 +1531,7 @@ static void aurora_close(struct tty_struct * tty, struct file * filp) aurora_shutdown_port(bp, port); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); tty->closing = 0; port->event = 0; port->tty = 0; @@ -1743,10 +1742,7 @@ static void aurora_flush_buffer(struct tty_struct *tty) port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); #ifdef AURORA_DEBUG printk("aurora_flush_buffer: end\n"); #endif @@ -2223,10 +2219,7 @@ static void do_softint(void *private_) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } #ifdef AURORA_DEBUG printk("do_softint: end\n"); diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c index 5c499be24135..6dd522466d34 100644 --- a/drivers/serial/68328serial.c +++ b/drivers/serial/68328serial.c @@ -435,10 +435,7 @@ static void do_softint(void *private) return; #if 0 if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } #endif } @@ -858,10 +855,7 @@ static void rs_flush_buffer(struct tty_struct *tty) cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; sti(); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1185,11 +1179,13 @@ static void rs_close(struct tty_struct *tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = 0; +#warning "This is not and has never been valid so fix it" +#if 0 if (tty->ldisc.num != ldiscs[N_TTY].num) { if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -1198,6 +1194,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) if (tty->ldisc.open) (tty->ldisc.open)(tty); } +#endif if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; diff --git a/drivers/serial/68360serial.c b/drivers/serial/68360serial.c index c0a89c547dea..cd2d62cd1582 100644 --- a/drivers/serial/68360serial.c +++ b/drivers/serial/68360serial.c @@ -700,12 +700,8 @@ static void do_softint(void *private_) if (!tty) return; - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - } + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) + tty_wakeup(tty); } @@ -1152,10 +1148,7 @@ static void rs_360_flush_buffer(struct tty_struct *tty) /* There is nothing to "flush", whatever we gave the CPM * is on its way out. */ - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); info->flags &= ~TX_WAKEUP; } @@ -1716,8 +1709,7 @@ static void rs_360_close(struct tty_struct *tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = 0; diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c index dfed452048df..7f1b9ee7e03f 100644 --- a/drivers/serial/mcfserial.c +++ b/drivers/serial/mcfserial.c @@ -424,11 +424,7 @@ static void mcfrs_offintr(void *private) tty = info->tty; if (!tty) return; - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } @@ -835,10 +831,7 @@ static void mcfrs_flush_buffer(struct tty_struct *tty) info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; local_irq_restore(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1232,11 +1225,12 @@ static void mcfrs_close(struct tty_struct *tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; info->event = 0; info->tty = 0; +#if 0 if (tty->ldisc.num != ldiscs[N_TTY].num) { if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -1245,6 +1239,7 @@ static void mcfrs_close(struct tty_struct *tty, struct file * filp) if (tty->ldisc.open) (tty->ldisc.open)(tty); } +#endif if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index f387fdd958a2..2f88f6da9698 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -107,15 +107,7 @@ static void uart_start(struct tty_struct *tty) static void uart_tasklet_action(unsigned long data) { struct uart_state *state = (struct uart_state *)data; - struct tty_struct *tty; - - tty = state->info->tty; - if (tty) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - tty->ldisc.write_wakeup(tty); - wake_up_interruptible(&tty->write_wait); - } + tty_wakeup(state->info->tty); } static inline void @@ -581,10 +573,7 @@ static void uart_flush_buffer(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); uart_circ_clear(&state->info->xmit); spin_unlock_irqrestore(&port->lock, flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1216,7 +1205,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) { struct uart_state *state = tty->driver_data; struct uart_port *port; - + BUG_ON(!kernel_locked()); if (!state || !state->port) @@ -1239,12 +1228,12 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * one, we've got real problems, since it means the * serial port won't be shutdown. */ - printk("uart_close: bad serial port count; tty->count is 1, " + printk(KERN_ERR "uart_close: bad serial port count; tty->count is 1, " "state->count is %d\n", state->count); state->count = 1; } if (--state->count < 0) { - printk("rs_close: bad serial port count for %s: %d\n", + printk(KERN_ERR "uart_close: bad serial port count for %s: %d\n", tty->name, state->count); state->count = 0; } @@ -1280,8 +1269,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) uart_shutdown(state); uart_flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); + tty->closing = 0; state->info->tty = NULL; diff --git a/drivers/tc/zs.c b/drivers/tc/zs.c index 73dd2612cbbc..c5befc9af125 100644 --- a/drivers/tc/zs.c +++ b/drivers/tc/zs.c @@ -679,10 +679,7 @@ static void do_softint(void *private_) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } @@ -1006,10 +1003,7 @@ static void rs_flush_buffer(struct tty_struct *tty) cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; sti(); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1403,8 +1397,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = 0; diff --git a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c index 2e8f5200ca40..d4e643f27f86 100644 --- a/drivers/usb/class/bluetty.c +++ b/drivers/usb/class/bluetty.c @@ -992,17 +992,10 @@ static void bluetooth_softint(void *private) dbg("%s", __FUNCTION__); - if (!bluetooth) { + if (!bluetooth) return; - } - - tty = bluetooth->tty; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { - dbg("%s - write wakeup call.", __FUNCTION__); - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + tty_wakeup(&bluetooth->tty); } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 37af64263c1b..dd3f5bea9a75 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -248,16 +248,11 @@ out: static void acm_softint(void *private) { struct acm *acm = private; - struct tty_struct *tty = acm->tty; dbg("Entering acm_softint.\n"); if (!ACM_READY(acm)) return; - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - - wake_up_interruptible(&tty->write_wait); + tty_wakeup(acm->tty); } /* diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index f187b3165d57..eef146950bb7 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -624,14 +624,7 @@ static void digi_wakeup_write( struct usb_serial_port *port ) wake_up_interruptible( &port->write_wait ); /* wake up line discipline */ - if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup ) - (tty->ldisc.write_wakeup)(tty); - - /* wake up other tty processes */ - wake_up_interruptible( &tty->write_wait ); - /* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */ - + tty_wakeup(tty); } @@ -1569,8 +1562,7 @@ dbg( "digi_close: TOP: port=%d, open_count=%d", priv->dp_port_num, port->open_co /* flush driver and line discipline buffers */ if( tty->driver->flush_buffer ) tty->driver->flush_buffer( tty ); - if( tty->ldisc.flush_buffer ) - tty->ldisc.flush_buffer( tty ); + tty_ldisc_flush(tty); if (port->serial->dev) { /* wait for transmit idle */ diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 972f3b91d41c..b70d12eddb51 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -516,7 +516,9 @@ static void empeg_set_termios (struct usb_serial_port *port, struct termios *old */ port->tty->low_latency = 1; - /* Notify the tty driver that the termios have changed. */ + /* Notify the tty driver that the termios have changed. + FIXME: Why - the ldisc will do this anyway and NULL is not + a valid previous state */ port->tty->ldisc.set_termios(port->tty, NULL); return; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index fc3a536f3696..fa9fbd1f6a7d 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -900,12 +900,7 @@ static void edge_bulk_out_data_callback (struct urb *urb, struct pt_regs *regs) if (tty && edge_port->open) { /* let the tty driver wakeup if it has a special write_wakeup function */ - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { - (tty->ldisc.write_wakeup)(tty); - } - - /* tell the tty driver that something has changed */ - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } // Release the Write URB diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 55be299fdf84..81d2ac711347 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1796,12 +1796,7 @@ static void edge_bulk_out_callback (struct urb *urb, struct pt_regs *regs) tty = port->tty; if (tty) { /* let the tty driver wakeup if it has a special write_wakeup function */ - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { - (tty->ldisc.write_wakeup)(tty); - } - - /* tell the tty driver that something has changed */ - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index cb42cc7c3224..3d1571695bde 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -449,6 +449,10 @@ static void ir_read_bulk_callback (struct urb *urb, struct pt_regs *regs) */ tty = port->tty; + /* + * FIXME: must not do this in IRQ context, + * must honour TTY_DONT_FLIP + */ tty->ldisc.receive_buf( tty, data+1, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index fd6c1b8388aa..cd441d783cbe 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -186,13 +186,7 @@ static void keyspan_pda_wakeup_write( struct usb_serial_port *port ) wake_up_interruptible( &port->write_wait ); /* wake up line discipline */ - if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup ) - (tty->ldisc.write_wakeup)(tty); - - /* wake up other tty processes */ - wake_up_interruptible( &tty->write_wait ); - /* For 2.2.16 backport -- wake_up_interruptible( &tty->poll_wait ); */ + tty_wakeup(tty); } static void keyspan_pda_request_unthrottle( struct usb_serial *serial ) diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index b4961f0d2509..212906c74f25 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -579,11 +579,7 @@ static void mct_u232_write_bulk_callback (struct urb *urb, struct pt_regs *regs) if (write_blocking) { wake_up_interruptible(&port->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - + tty_wakeup(tty); } else { /* from generic_write_bulk_callback */ schedule_work(&port->work); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 0e59c134134c..03e8a7e1c8ee 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -805,12 +805,7 @@ void usb_serial_port_softint(void *private) if (!tty) return; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { - dbg("%s - write wakeup call.", __FUNCTION__); - (tty->ldisc.write_wakeup)(tty); - } - - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } static void port_release(struct device *dev) diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index b27741bf5497..1b759018e8b8 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -668,8 +668,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) if (port->tty->driver->flush_buffer) port->tty->driver->flush_buffer(port->tty); - if (port->tty->ldisc.flush_buffer) - port->tty->ldisc.flush_buffer(port->tty); + tty_ldisc_flush(port->tty); firm_report_tx_done(port); diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index a0d4404cc916..93cbcbaf6f0c 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -15,9 +15,6 @@ #include #include -extern struct tty_ldisc ldiscs[]; - - static int tty_ldiscs_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); @@ -159,12 +156,15 @@ static int tty_ldiscs_read_proc(char *page, char **start, off_t off, int i; int len = 0; off_t begin = 0; - + struct tty_ldisc *ld; + for (i=0; i < NR_LDISCS; i++) { - if (!(ldiscs[i].flags & LDISC_FLAG_DEFINED)) + ld = tty_ldisc_get(i); + if (ld == NULL) continue; len += sprintf(page+len, "%-10s %2d\n", - ldiscs[i].name ? ldiscs[i].name : "???", i); + ld->name ? ld->name : "???", i); + tty_ldisc_put(i); if (len+begin > off+count) break; if (len+begin < off) { diff --git a/include/linux/tty.h b/include/linux/tty.h index 7481d70fbf41..24b4a7383b53 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -306,26 +306,27 @@ struct tty_struct { * tty->write. Thus, you must use the inline functions set_bit() and * clear_bit() to make things atomic. */ -#define TTY_THROTTLED 0 -#define TTY_IO_ERROR 1 -#define TTY_OTHER_CLOSED 2 -#define TTY_EXCLUSIVE 3 -#define TTY_DEBUG 4 -#define TTY_DO_WRITE_WAKEUP 5 -#define TTY_PUSH 6 -#define TTY_CLOSING 7 -#define TTY_DONT_FLIP 8 -#define TTY_HW_COOK_OUT 14 -#define TTY_HW_COOK_IN 15 -#define TTY_PTY_LOCK 16 -#define TTY_NO_WRITE_SPLIT 17 +#define TTY_THROTTLED 0 /* Call unthrottle() at threshold min */ +#define TTY_IO_ERROR 1 /* Canse an I/O error (may be no ldisc too) */ +#define TTY_OTHER_CLOSED 2 /* Other side (if any) has closed */ +#define TTY_EXCLUSIVE 3 /* Exclusive open mode */ +#define TTY_DEBUG 4 /* Debugging */ +#define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */ +#define TTY_PUSH 6 /* n_tty private */ +#define TTY_CLOSING 7 /* ->close() in progress */ +#define TTY_DONT_FLIP 8 /* Defer buffer flip */ +#define TTY_LDISC 9 /* Line discipline attached */ +#define TTY_HW_COOK_OUT 14 /* Hardware can do output cooking */ +#define TTY_HW_COOK_IN 15 /* Hardware can do input cooking */ +#define TTY_PTY_LOCK 16 /* pty private */ +#define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ +#define TTY_HUPPED 18 /* Post driver->hangup() */ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) extern void tty_write_flush(struct tty_struct *); extern struct termios tty_std_termios; -extern struct tty_ldisc ldiscs[]; extern int fg_console, last_console, want_console; extern int kmsg_redirect; @@ -362,6 +363,16 @@ extern void tty_flip_buffer_push(struct tty_struct *tty); extern int tty_get_baud_rate(struct tty_struct *tty); extern int tty_termios_baud_rate(struct termios *termios); +extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *); +extern void tty_ldisc_deref(struct tty_ldisc *); +extern struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *); + +extern struct tty_ldisc *tty_ldisc_get(int); +extern void tty_ldisc_put(int); + +extern void tty_wakeup(struct tty_struct *tty); +extern void tty_ldisc_flush(struct tty_struct *tty); + struct semaphore; extern struct semaphore tty_sem; diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index cb7ca58e44e8..deacf01e50e8 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -95,6 +95,13 @@ * that line discpline should try to send more characters to the * low-level driver for transmission. If the line discpline does * not have any more data to send, it can just return. + * + * int (*hangup)(struct tty_struct *) + * + * Called on a hangup. Tells the discipline that it should + * cease I/O to the tty driver. Can sleep. The driver should + * seek to perform this action quickly but should wait until + * any pending driver I/O is completed. */ #include @@ -122,6 +129,7 @@ struct tty_ldisc { void (*set_termios)(struct tty_struct *tty, struct termios * old); unsigned int (*poll)(struct tty_struct *, struct file *, struct poll_table_struct *); + int (*hangup)(struct tty_struct *tty); /* * The following routines are called from below. @@ -132,6 +140,8 @@ struct tty_ldisc { void (*write_wakeup)(struct tty_struct *); struct module *owner; + + int refcount; }; #define TTY_LDISC_MAGIC 0x5403 -- cgit v1.2.3 From 77eb355870ff3c0ded507a3fdd8ca84ea6d1d6c3 Mon Sep 17 00:00:00 2001 From: Randolph Chung Date: Wed, 29 Sep 2004 03:35:37 -0700 Subject: [PATCH] add missing declaration to fix kernel/compat.c warning Fixes a warning in kernel/compat.c. Signed-off-by: Randolph Chung Signed-off-by: Linus Torvalds --- include/linux/syscalls.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux') diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index c08b42e2bee4..bd26cf298d9a 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -114,6 +114,9 @@ asmlinkage long sys_getitimer(int which, struct itimerval __user *value); asmlinkage long sys_setitimer(int which, struct itimerval __user *value, struct itimerval __user *ovalue); +asmlinkage long sys_timer_create(clockid_t which_clock, + struct sigevent __user *timer_event_spec, + timer_t __user * created_timer_id); asmlinkage long sys_timer_gettime(timer_t timer_id, struct itimerspec __user *setting); asmlinkage long sys_timer_getoverrun(timer_t timer_id); -- cgit v1.2.3