From 9de9e3d8cf5a3309173ef806dcb7ecca93bd768b Mon Sep 17 00:00:00 2001 From: eeb Date: Thu, 17 Jul 2003 13:03:16 +0000 Subject: [PATCH] * merged current b_devel --- .../patches/ext3-map_inode_page.patch | 86 + .../patches/ext3_map_inode_page_chaos.patch | 85 + .../patches/linux-2.4.18-14-kgdb-1.6.patch | 3165 ++++++++++++++++++++ .../patches/vfs_intent-2.4.20-hp.patch | 1793 +++++++++++ lustre/kernel_patches/pc/ext3-map_inode_page.pc | 3 + .../kernel_patches/pc/ext3_map_inode_page_chaos.pc | 2 + .../kernel_patches/pc/linux-2.4.18-14-kgdb-1.6.pc | 26 + lustre/kernel_patches/pc/vfs_intent-2.4.20-hp.pc | 14 + lustre/kernel_patches/series/chaos-kgdb | 22 + lustre/kernel_patches/txt/ext3-map_inode_page.txt | 3 + .../txt/ext3_map_inode_page_chaos.txt | 3 + 11 files changed, 5202 insertions(+) create mode 100644 lustre/kernel_patches/patches/ext3-map_inode_page.patch create mode 100644 lustre/kernel_patches/patches/ext3_map_inode_page_chaos.patch create mode 100644 lustre/kernel_patches/patches/linux-2.4.18-14-kgdb-1.6.patch create mode 100644 lustre/kernel_patches/patches/vfs_intent-2.4.20-hp.patch create mode 100644 lustre/kernel_patches/pc/ext3-map_inode_page.pc create mode 100644 lustre/kernel_patches/pc/ext3_map_inode_page_chaos.pc create mode 100644 lustre/kernel_patches/pc/linux-2.4.18-14-kgdb-1.6.pc create mode 100644 lustre/kernel_patches/pc/vfs_intent-2.4.20-hp.pc create mode 100644 lustre/kernel_patches/series/chaos-kgdb create mode 100644 lustre/kernel_patches/txt/ext3-map_inode_page.txt create mode 100644 lustre/kernel_patches/txt/ext3_map_inode_page_chaos.txt diff --git a/lustre/kernel_patches/patches/ext3-map_inode_page.patch b/lustre/kernel_patches/patches/ext3-map_inode_page.patch new file mode 100644 index 0000000..c9b7c15 --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-map_inode_page.patch @@ -0,0 +1,86 @@ + + + + fs/ext3/ext3-exports.c | 3 ++ + fs/ext3/inode.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 58 insertions(+) + +--- linux-2.4.20-l18/fs/ext3/inode.c~ext3-map_inode_page Tue Jul 8 20:07:23 2003 ++++ linux-2.4.20-l18-phil/fs/ext3/inode.c Wed Jul 9 03:56:41 2003 +@@ -2867,3 +2867,58 @@ int ext3_prep_san_write(struct inode *in + ret = ret2; + return ret; + } ++ ++int ext3_map_inode_page(struct inode *inode, struct page *page, ++ unsigned long *blocks, int *created, int create) ++{ ++ unsigned int blocksize, blocks_per_page; ++ unsigned long iblock; ++ struct buffer_head dummy; ++ void *handle; ++ int i, rc = 0, failed = 0, needed_blocks; ++ ++ blocksize = inode->i_sb->s_blocksize; ++ blocks_per_page = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; ++ iblock = page->index >> (PAGE_SHIFT - inode->i_sb->s_blocksize_bits); ++ ++ for (i = 0; i < blocks_per_page; i++, iblock++) { ++ blocks[i] = ext3_bmap(inode->i_mapping, iblock); ++ if (blocks[i] == 0) { ++ failed++; ++ created[i] = -1; ++ } else { ++ created[i] = 0; ++ } ++ } ++ ++ if (failed == 0 || create == 0) ++ return 0; ++ ++ needed_blocks = ext3_writepage_trans_blocks(inode) * failed; ++ lock_kernel(); ++ handle = ext3_journal_start(inode, needed_blocks); ++ unlock_kernel(); ++ if (IS_ERR(handle)) ++ return PTR_ERR(handle); ++ ++ iblock = page->index >> (PAGE_SHIFT - inode->i_sb->s_blocksize_bits); ++ for (i = 0; i < blocks_per_page; i++, iblock++) { ++ if (blocks[i] != 0) ++ continue; ++ ++ rc = ext3_get_block_handle(handle, inode, iblock, &dummy, 1); ++ if (rc) { ++ printk(KERN_INFO "ext3_map_inode_page: error reading " ++ "block %ld\n", iblock); ++ goto out; ++ } ++ blocks[i] = dummy.b_blocknr; ++ created[i] = 1; ++ } ++ ++ out: ++ lock_kernel(); ++ ext3_journal_stop(handle, inode); ++ unlock_kernel(); ++ return rc; ++} +--- linux-2.4.20-l18/fs/ext3/ext3-exports.c~ext3-map_inode_page Tue Jul 8 20:07:10 2003 ++++ linux-2.4.20-l18-phil/fs/ext3/ext3-exports.c Tue Jul 8 20:09:02 2003 +@@ -9,6 +9,8 @@ + + int ext3_prep_san_write(struct inode *inode, long *blocks, + int nblocks, loff_t newsize); ++int ext3_map_inode_page(struct inode *inode, struct page *page, ++ unsigned long *block, int *created, int create); + + EXPORT_SYMBOL(ext3_force_commit); + EXPORT_SYMBOL(ext3_bread); +@@ -18,3 +20,4 @@ EXPORT_SYMBOL(ext3_xattr_get); + EXPORT_SYMBOL(ext3_xattr_list); + EXPORT_SYMBOL(ext3_xattr_set); + EXPORT_SYMBOL(ext3_prep_san_write); ++EXPORT_SYMBOL(ext3_map_inode_page); + +_ diff --git a/lustre/kernel_patches/patches/ext3_map_inode_page_chaos.patch b/lustre/kernel_patches/patches/ext3_map_inode_page_chaos.patch new file mode 100644 index 0000000..1b9296b --- /dev/null +++ b/lustre/kernel_patches/patches/ext3_map_inode_page_chaos.patch @@ -0,0 +1,85 @@ + + + + fs/ext3/ext3-exports.c | 12 ++++++++++ + fs/ext3/inode.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 67 insertions(+) + +--- linux-2.4.18-p4smp/fs/ext3/inode.c~ext3_map_inode_page_chaos 2003-07-09 13:32:38.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/fs/ext3/inode.c 2003-07-09 17:11:19.000000000 -0600 +@@ -2892,3 +2892,58 @@ int ext3_prep_san_write(struct inode *in + ret = ret2; + return ret; + } ++ ++int ext3_map_inode_page(struct inode *inode, struct page *page, ++ unsigned long *blocks, int *created, int create) ++{ ++ unsigned int blocksize, blocks_per_page; ++ unsigned long iblock; ++ struct buffer_head dummy; ++ void *handle; ++ int i, rc = 0, failed = 0, needed_blocks; ++ ++ blocksize = inode->i_sb->s_blocksize; ++ blocks_per_page = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; ++ iblock = page->index >> (PAGE_SHIFT - inode->i_sb->s_blocksize_bits); ++ ++ for (i = 0; i < blocks_per_page; i++, iblock++) { ++ blocks[i] = ext3_bmap(inode->i_mapping, iblock); ++ if (blocks[i] == 0) { ++ failed++; ++ created[i] = -1; ++ } else { ++ created[i] = 0; ++ } ++ } ++ ++ if (failed == 0 || create == 0) ++ return 0; ++ ++ needed_blocks = ext3_writepage_trans_blocks(inode) * failed; ++ lock_kernel(); ++ handle = ext3_journal_start(inode, needed_blocks); ++ unlock_kernel(); ++ if (IS_ERR(handle)) ++ return PTR_ERR(handle); ++ ++ iblock = page->index >> (PAGE_SHIFT - inode->i_sb->s_blocksize_bits); ++ for (i = 0; i < blocks_per_page; i++, iblock++) { ++ if (blocks[i] != 0) ++ continue; ++ ++ rc = ext3_get_block_handle(handle, inode, iblock, &dummy, 1); ++ if (rc) { ++ printk(KERN_INFO "ext3_map_inode_page: error reading " ++ "block %ld\n", iblock); ++ goto out; ++ } ++ blocks[i] = dummy.b_blocknr; ++ created[i] = 1; ++ } ++ ++ out: ++ lock_kernel(); ++ ext3_journal_stop(handle, inode); ++ unlock_kernel(); ++ return rc; ++} +--- /dev/null 2003-01-30 03:24:37.000000000 -0700 ++++ linux-2.4.18-p4smp-braam/fs/ext3/ext3-exports.c 2003-07-09 17:11:19.000000000 -0600 +@@ -0,0 +1,12 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++extern int ext3_map_inode_page(struct inode *inode, struct page *page, ++ unsigned long *blocks, int *created, int create); ++EXPORT_SYMBOL(ext3_map_inode_page); + +_ diff --git a/lustre/kernel_patches/patches/linux-2.4.18-14-kgdb-1.6.patch b/lustre/kernel_patches/patches/linux-2.4.18-14-kgdb-1.6.patch new file mode 100644 index 0000000..deca885 --- /dev/null +++ b/lustre/kernel_patches/patches/linux-2.4.18-14-kgdb-1.6.patch @@ -0,0 +1,3165 @@ + Documentation/Configure.help | 23 + Documentation/i386/gdb-serial.txt | 386 ++++++++++ + Makefile | 4 + arch/i386/Makefile | 6 + arch/i386/config.in | 39 - + arch/i386/kernel/Makefile | 18 + arch/i386/kernel/entry.S | 27 + arch/i386/kernel/gdbstart.c | 148 ++++ + arch/i386/kernel/gdbstub.c | 1402 ++++++++++++++++++++++++++++++++++++++ + arch/i386/kernel/nmi.c | 30 + arch/i386/kernel/signal.c | 3 + arch/i386/kernel/traps.c | 44 + + arch/i386/mm/fault.c | 31 + drivers/char/Makefile | 1 + drivers/char/gdbserial.c | 280 +++++++ + drivers/char/serial.c | 147 +++ + drivers/char/tty_io.c | 6 + include/asm-i386/ioctls.h | 1 + include/asm-i386/page.h | 4 + include/asm-i386/processor.h | 3 + include/linux/dcache.h | 1 + include/linux/gdb.h | 63 + + include/linux/sched.h | 13 + init/main.c | 9 + kernel/ksyms.c | 5 + kernel/sched.c | 18 + 26 files changed, 2679 insertions(+), 33 deletions(-) + +--- linux-2.4.18-p4smp/arch/i386/config.in~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/arch/i386/config.in 2003-07-09 13:18:12.000000000 -0600 +@@ -456,25 +456,26 @@ mainmenu_option next_comment + comment 'Kernel hacking' + + bool 'Kernel debugging' CONFIG_DEBUG_KERNEL +-if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then +- bool ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER +- bool ' Use Scan technique for doing stack trace' CONFIG_STACK_TRACE_SCAN +- if [ "$CONFIG_FRAME_POINTER" = "y" ]; then +- bool ' Use Frame Pointer Walk technique for doing stack trace' CONFIG_STACK_TRACE_FPTR +- fi +- int 'Number of parameters to show in stack trace' CONFIG_STACK_TRACE_PARAM_COUNT 4 +- bool ' Debug high memory support' CONFIG_DEBUG_HIGHMEM +- bool ' Debug memory allocations' CONFIG_DEBUG_SLAB +- bool ' Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT +- bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ +- bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK +- bool ' Kernel Core Dump Facility' CONFIG_MCL_COREDUMP +- if [ "$CONFIG_MCL_COREDUMP" = "y" ]; then +- bool ' Reboot using bootimg' CONFIG_BOOTIMG +- fi +- bool ' OPROFILE support' CONFIG_OPROFILE +-fi +- ++bool 'KGDB: Remote (serial) kernel debugging with gdb' CONFIG_X86_REMOTE_DEBUG ++ if [ "$CONFIG_X86_REMOTE_DEBUG" != "n" ]; then ++ bool 'KGDB: Thread analysis' CONFIG_KGDB_THREAD ++ bool 'KGDB: Console messages through gdb' CONFIG_GDB_CONSOLE ++ else ++ bool 'Kernel debugging' CONFIG_DEBUG_KERNEL ++ if [ "$CONFIG_DEBUG_KERNEL" != "n" ]; then ++ bool ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER ++ bool ' Debug high memory support' CONFIG_DEBUG_HIGHMEM ++ bool ' Debug memory allocations' CONFIG_DEBUG_SLAB ++ bool ' Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT ++ bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ ++ bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK ++ bool ' Kernel Core Dump Facility' CONFIG_MCL_COREDUMP ++ if [ "$CONFIG_MCL_COREDUMP" = "y" ]; then ++ bool ' Reboot using bootimg' CONFIG_BOOTIMG ++ fi ++ bool ' OPROFILE support' CONFIG_OPROFILE ++ fi ++ fi + endmenu + + source lib/Config.in +--- linux-2.4.18-p4smp/arch/i386/kernel/entry.S~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/entry.S 2003-07-09 13:15:23.000000000 -0600 +@@ -266,7 +266,7 @@ ret_from_exception: + + ALIGN + reschedule: +- call SYMBOL_NAME(schedule) # test ++ call SYMBOL_NAME(user_schedule) # test + jmp ret_from_sys_call + + ENTRY(divide_error) +@@ -404,6 +404,31 @@ ENTRY(spurious_interrupt_bug) + pushl $ SYMBOL_NAME(do_spurious_interrupt_bug) + jmp error_code + ++#ifdef CONFIG_KGDB_THREAD ++ENTRY(kern_schedule) ++ pushl %ebp ++ movl %esp, %ebp ++ pushl %ss ++ pushl %ebp ++ pushfl ++ pushl %cs ++ pushl 4(%ebp) ++ pushl %eax ++ pushl %es ++ pushl %ds ++ pushl %eax ++ pushl (%ebp) ++ pushl %edi ++ pushl %esi ++ pushl %edx ++ pushl %ecx ++ pushl %ebx ++ call kern_do_schedule ++ movl %ebp, %esp ++ pop %ebp ++ ret ++#endif ++ + .data + ENTRY(sys_call_table) + .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/ +--- /dev/null 2003-01-30 03:24:37.000000000 -0700 ++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/gdbstart.c 2003-07-09 13:15:23.000000000 -0600 +@@ -0,0 +1,148 @@ ++/* ++ * This program opens a tty file and issues the GDB stub activating ++ * ioctl on it. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TIOCGDB 0x547F ++ ++char *tty_name = "/dev/ttyS0" ; /* COM1 port */ ++int speed = 9600 ; /* default speed */ ++struct termios save_ts ; /* original term struct */ ++ ++void print_usage(void) ++{ ++ printf("gdbstub [-s speed] [-t tty-dev]\n") ; ++ printf(" defaults: /dev/ttyS0 with speed unmodified by this program\n"); ++ ++} /* print_usage */ ++ ++void tty_err(char *msg) ++{ ++ char buf[100] ; ++ ++ strcpy(buf, msg) ; ++ strcat(buf, ": ") ; ++ strcat(buf, tty_name) ; ++ perror(buf) ; ++ exit(1) ; ++ ++} /* tty_err */ ++ ++ ++void setup_term(int fd) ++{ ++ struct termios ts ; ++ int speed_code ; ++ ++ if (tcgetattr(fd, &ts) < 0) tty_err("tcgetattr") ; ++ ++ save_ts = ts ; ++ switch (speed) ++ { ++ case 4800: ++ speed_code = B4800 ; ++ break ; ++ case 9600: ++ speed_code = B9600 ; ++ break ; ++ case 19200: ++ speed_code = B19200 ; ++ break ; ++ case 38400: ++ speed_code = B38400 ; ++ break ; ++ case 57600: ++ speed_code = B57600 ; ++ break ; ++ case 115200: ++ speed_code = B115200 ; ++ break ; ++ case 230400: ++ speed_code = B230400 ; ++ break ; ++ default: ++ printf("Invalid speed: %d\n", speed) ; ++ exit(1) ; ++ } ++ ++ ts.c_cflag = CS8 | CREAD | CLOCAL ; ++ if (cfsetospeed(&ts, speed_code) < 0) tty_err("cfsetospeed") ; ++ if (cfsetispeed(&ts, speed_code) < 0) tty_err("cfsetispeed") ; ++ ++ if (tcsetattr(fd, TCSANOW, &ts) < 0) tty_err("tcsetattr") ; ++ ++} /* setup_term */ ++ ++void main(int argc, char **argv) ++{ ++ int opt ; ++ int fil ; ++ int rslt ; ++ ++ while ((opt = getopt(argc, argv, "hs:t:")) > 0) ++ { ++ switch (opt) ++ { ++ case 's': ++ speed = atol(optarg) ; ++ break ; ++ case 't': ++ tty_name = optarg ; ++ break ; ++ case ':': ++ printf("Invalid option\n") ; ++ break ; ++ case '?': ++ case 'h': ++ default: ++ print_usage() ; ++ return ; ++ } ++ } ++ ++ fil = open(tty_name, O_RDWR) ; ++ if (fil < 0) ++ { ++ perror(tty_name) ; ++ return ; ++ } ++ ++ ++ setup_term(fil) ; ++ ++ /* ++ * When we issue this ioctl, control will not return until ++ * the debugger running on the remote host machine says "go". ++ */ ++ printf("\nAbout to activate GDB stub in the kernel on %s\n", tty_name) ; ++ printf("Hit CR to continue, kill program to abort -- ") ; ++ getchar() ; ++ sync() ; ++ rslt = ioctl(fil, TIOCGDB, 0) ; ++ if (rslt < 0) ++ { ++ perror("TIOCGDB ioctl") ; ++ return ; ++ } ++ ++ printf("\nGDB stub successfully activated\n") ; ++ ++ for (;;) ++ { ++ pause() ; ++ } ++ ++ if (tcsetattr(fil, TCSANOW, &save_ts) < 0) tty_err("tcsetattr") ; ++ ++} /* main */ +--- /dev/null 2003-01-30 03:24:37.000000000 -0700 ++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/gdbstub.c 2003-07-09 13:15:23.000000000 -0600 +@@ -0,0 +1,1402 @@ ++/* ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2, or (at your option) any ++ * later version. ++ * ++ * 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. ++ * ++ */ ++ ++/* ++ * Copyright (C) 2000-2001 VERITAS Software Corporation. ++ */ ++/**************************************************************************** ++ * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ ++ * ++ * Module name: remcom.c $ ++ * Revision: 1.34 $ ++ * Date: 91/03/09 12:29:49 $ ++ * Contributor: Lake Stevens Instrument Division$ ++ * ++ * Description: low level support for gdb debugger. $ ++ * ++ * Considerations: only works on target hardware $ ++ * ++ * Written by: Glenn Engel $ ++ * Updated by: Amit Kale ++ * ModuleState: Experimental $ ++ * ++ * NOTES: See Below $ ++ * ++ * Modified for 386 by Jim Kingdon, Cygnus Support. ++ * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe ++ * Integrated into 2.2.5 kernel by Tigran Aivazian ++ * thread support, ++ * support for multiple processors, ++ * support for ia-32(x86) hardware debugging, ++ * Console support, ++ * handling nmi watchdog ++ * Amit S. Kale ( akale@veritas.com ) ++ * ++ * ++ * To enable debugger support, two things need to happen. One, a ++ * call to set_debug_traps() is necessary in order to allow any breakpoints ++ * or error conditions to be properly intercepted and reported to gdb. ++ * Two, a breakpoint needs to be generated to begin communication. This ++ * is most easily accomplished by a call to breakpoint(). Breakpoint() ++ * simulates a breakpoint by executing an int 3. ++ * ++ ************* ++ * ++ * The following gdb commands are supported: ++ * ++ * command function Return value ++ * ++ * g return the value of the CPU registers hex data or ENN ++ * G set the value of the CPU registers OK or ENN ++ * ++ * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN ++ * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN ++ * ++ * c Resume at current address SNN ( signal NN) ++ * cAA..AA Continue at address AA..AA SNN ++ * ++ * s Step one instruction SNN ++ * sAA..AA Step one instruction from AA..AA SNN ++ * ++ * k kill ++ * ++ * ? What was the last sigval ? SNN (signal NN) ++ * ++ * All commands and responses are sent with a packet which includes a ++ * checksum. A packet consists of ++ * ++ * $#. ++ * ++ * where ++ * :: ++ * :: < two hex digits computed as modulo 256 sum of > ++ * ++ * When a packet is received, it is first acknowledged with either '+' or '-'. ++ * '+' indicates a successful transfer. '-' indicates a failed transfer. ++ * ++ * Example: ++ * ++ * Host: Reply: ++ * $m0,10#2a +$00010203040506070809101112131415#42 ++ * ++ ****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for linux pt_regs struct */ ++#include ++#ifdef CONFIG_GDB_CONSOLE ++#include ++#endif ++#include ++ ++/************************************************************************ ++ * ++ * external low-level support routines ++ */ ++typedef void (*Function)(void); /* pointer to a function */ ++ ++/* Thread reference */ ++typedef unsigned char threadref[8]; ++ ++extern int putDebugChar(int); /* write a single character */ ++extern int getDebugChar(void); /* read and return a single char */ ++ ++/************************************************************************/ ++/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ ++/* at least NUMREGBYTES*2 are needed for register packets */ ++/* Longer buffer is needed to list all threads */ ++#define BUFMAX 1024 ++ ++static char initialized; /* boolean flag. != 0 means we've been initialized */ ++ ++static const char hexchars[]="0123456789abcdef"; ++ ++/* Number of bytes of registers. */ ++#define NUMREGBYTES 64 ++/* ++ * Note that this register image is in a different order than ++ * the register image that Linux produces at interrupt time. ++ * ++ * Linux's register image is defined by struct pt_regs in ptrace.h. ++ * Just why GDB uses a different order is a historical mystery. ++ */ ++enum regnames {_EAX, /* 0 */ ++ _ECX, /* 1 */ ++ _EDX, /* 2 */ ++ _EBX, /* 3 */ ++ _ESP, /* 4 */ ++ _EBP, /* 5 */ ++ _ESI, /* 6 */ ++ _EDI, /* 7 */ ++ _PC /* 8 also known as eip */, ++ _PS /* 9 also known as eflags */, ++ _CS, /* 10 */ ++ _SS, /* 11 */ ++ _DS, /* 12 */ ++ _ES, /* 13 */ ++ _FS, /* 14 */ ++ _GS}; /* 15 */ ++ ++ ++ ++/*************************** ASSEMBLY CODE MACROS *************************/ ++/* */ ++ ++#define BREAKPOINT() asm(" int $3"); ++ ++/* Put the error code here just in case the user cares. */ ++int gdb_i386errcode; ++/* Likewise, the vector number here (since GDB only gets the signal ++ number through the usual means, and that's not very specific). */ ++int gdb_i386vector = -1; ++ ++#if KGDB_MAX_NO_CPUS != 8 ++#error change the definition of slavecpulocks ++#endif ++ ++static spinlock_t slavecpulocks[KGDB_MAX_NO_CPUS] = { SPIN_LOCK_UNLOCKED, ++ SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED, ++ SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED, ++ SPIN_LOCK_UNLOCKED }; ++volatile int procindebug[KGDB_MAX_NO_CPUS]; ++ ++volatile unsigned kgdb_step = 0; ++ ++volatile unsigned kgdb_lock = 0; ++ ++enum gdb_bptype ++{ ++ bp_breakpoint = '0', ++ bp_hardware_breakpoint, ++ bp_write_watchpoint, ++ bp_read_watchpoint, ++ bp_access_watchpoint ++}; ++ ++enum gdb_bpstate ++{ ++ bp_disabled, ++ bp_enabled ++}; ++ ++#define BREAK_INSTR_SIZE 1 ++ ++struct gdb_breakpoint ++{ ++ unsigned int bpt_addr; ++ unsigned char saved_instr[BREAK_INSTR_SIZE]; ++ enum gdb_bptype type; ++ enum gdb_bpstate state; ++}; ++ ++typedef struct gdb_breakpoint gdb_breakpoint_t; ++ ++#define MAX_BREAKPOINTS 16 ++ ++gdb_breakpoint_t kgdb_break[MAX_BREAKPOINTS]; ++static char gdb_bpt_instr[BREAK_INSTR_SIZE] = {0xcc}; ++ ++static void kgdb_usercode (void) ++{ ++} ++ ++int hex(char ch) ++{ ++ if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10); ++ if ((ch >= '0') && (ch <= '9')) return (ch-'0'); ++ if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10); ++ return (-1); ++} ++ ++ ++/* scan for the sequence $# */ ++void getpacket(char * buffer) ++{ ++ unsigned char checksum; ++ unsigned char xmitcsum; ++ int i; ++ int count; ++ char ch; ++ ++ do { ++ /* wait around for the start character, ignore all other characters */ ++ while ((ch = (getDebugChar() & 0x7f)) != '$'); ++ checksum = 0; ++ xmitcsum = -1; ++ ++ count = 0; ++ ++ /* now, read until a # or end of buffer is found */ ++ while (count < BUFMAX) { ++ ch = getDebugChar() & 0x7f; ++ if (ch == '#') break; ++ checksum = checksum + ch; ++ buffer[count] = ch; ++ count = count + 1; ++ } ++ buffer[count] = 0; ++ ++ if (ch == '#') { ++ xmitcsum = hex(getDebugChar() & 0x7f) << 4; ++ xmitcsum += hex(getDebugChar() & 0x7f); ++ ++ if (checksum != xmitcsum) putDebugChar('-'); /* failed checksum */ ++ else { ++ putDebugChar('+'); /* successful transfer */ ++ /* if a sequence char is present, reply the sequence ID */ ++ if (buffer[2] == ':') { ++ putDebugChar( buffer[0] ); ++ putDebugChar( buffer[1] ); ++ /* remove sequence chars from buffer */ ++ count = strlen(buffer); ++ for (i=3; i <= count; i++) buffer[i-3] = buffer[i]; ++ } ++ } ++ } ++ } while (checksum != xmitcsum); ++ ++} ++ ++/* send the packet in buffer. */ ++ ++ ++void putpacket(char * buffer) ++{ ++ unsigned char checksum; ++ int count; ++ char ch; ++ ++ /* $#. */ ++ do { ++ putDebugChar('$'); ++ checksum = 0; ++ count = 0; ++ ++ while ((ch=buffer[count])) { ++ if (! putDebugChar(ch)) return; ++ checksum += ch; ++ count += 1; ++ } ++ ++ putDebugChar('#'); ++ putDebugChar(hexchars[checksum >> 4]); ++ putDebugChar(hexchars[checksum % 16]); ++ ++ } while ((getDebugChar() & 0x7f) != '+'); ++ ++} ++ ++static char remcomInBuffer[BUFMAX]; ++static char remcomOutBuffer[BUFMAX]; ++static short error; ++ ++static void regs_to_gdb_regs(int *gdb_regs, struct pt_regs *regs) ++{ ++ gdb_regs[_EAX] = regs->eax; ++ gdb_regs[_EBX] = regs->ebx; ++ gdb_regs[_ECX] = regs->ecx; ++ gdb_regs[_EDX] = regs->edx; ++ gdb_regs[_ESI] = regs->esi; ++ gdb_regs[_EDI] = regs->edi; ++ gdb_regs[_EBP] = regs->ebp; ++ gdb_regs[ _DS] = regs->xds; ++ gdb_regs[ _ES] = regs->xes; ++ gdb_regs[ _PS] = regs->eflags; ++ gdb_regs[ _CS] = regs->xcs; ++ gdb_regs[ _PC] = regs->eip; ++ gdb_regs[_ESP] = (int) (®s->esp) ; ++ gdb_regs[ _SS] = __KERNEL_DS; ++ gdb_regs[ _FS] = 0xFFFF; ++ gdb_regs[ _GS] = 0xFFFF; ++} /* regs_to_gdb_regs */ ++ ++static void gdb_regs_to_regs(int *gdb_regs, struct pt_regs *regs) ++{ ++ regs->eax = gdb_regs[_EAX] ; ++ regs->ebx = gdb_regs[_EBX] ; ++ regs->ecx = gdb_regs[_ECX] ; ++ regs->edx = gdb_regs[_EDX] ; ++ regs->esi = gdb_regs[_ESI] ; ++ regs->edi = gdb_regs[_EDI] ; ++ regs->ebp = gdb_regs[_EBP] ; ++ regs->xds = gdb_regs[ _DS] ; ++ regs->xes = gdb_regs[ _ES] ; ++ regs->eflags= gdb_regs[ _PS] ; ++ regs->xcs = gdb_regs[ _CS] ; ++ regs->eip = gdb_regs[ _PC] ; ++#if 0 /* can't change these */ ++ regs->esp = gdb_regs[_ESP] ; ++ regs->xss = gdb_regs[ _SS] ; ++ regs->fs = gdb_regs[ _FS] ; ++ regs->gs = gdb_regs[ _GS] ; ++#endif ++ ++} /* gdb_regs_to_regs */ ++ ++/* Indicate to caller of mem2hex or hex2mem that there has been an ++ error. */ ++static volatile int kgdb_memerr = 0; ++volatile int kgdb_memerr_expected = 0; ++static volatile int kgdb_memerr_cnt = 0; ++static int garbage_loc = -1 ; ++ ++int ++get_char (char *addr) ++{ ++ return *addr; ++} ++ ++void ++set_char ( char *addr, int val) ++{ ++ *addr = val; ++} ++ ++static void ++get_mem (char *addr, ++ unsigned char *buf, ++ int count) ++{ ++ while (count) { ++ *buf++ = get_char(addr++); ++ if (kgdb_memerr) ++ return; ++ count--; ++ } ++} ++ ++static void ++set_mem (char *addr, ++ char *buf, ++ int count) ++{ ++ while (count) { ++ set_char(addr++,*buf++); ++ if (kgdb_memerr) ++ return; ++ count--; ++ } ++} ++ ++/* convert the memory pointed to by mem into hex, placing result in buf */ ++/* return a pointer to the last char put in buf (null) */ ++/* If MAY_FAULT is non-zero, then we should set kgdb_memerr in response to ++ a fault; if zero treat a fault like any other fault in the stub. */ ++char* mem2hex( char* mem, ++ char* buf, ++ int count, ++ int may_fault) ++{ ++ int i; ++ unsigned char ch; ++ ++ if (may_fault) ++ { ++ kgdb_memerr_expected = 1 ; ++ kgdb_memerr = 0 ; ++ } ++ for (i=0;i> 4]; ++ *buf++ = hexchars[ch % 16]; ++ } ++ *buf = 0; ++ if (may_fault) ++ kgdb_memerr_expected = 0 ; ++ return(buf); ++} ++ ++/* convert the hex array pointed to by buf into binary to be placed in mem */ ++/* return a pointer to the character AFTER the last byte written */ ++char* hex2mem( char* buf, ++ char* mem, ++ int count, ++ int may_fault) ++{ ++ int i; ++ unsigned char ch; ++ ++ if (may_fault) ++ { ++ kgdb_memerr_expected = 1 ; ++ kgdb_memerr = 0 ; ++ } ++ for (i=0;i=0) ++ { ++ *intValue = (*intValue <<4) | hexValue; ++ numChars ++; ++ } ++ else ++ break; ++ ++ (*ptr)++; ++ } ++ ++ return (numChars); ++} ++ ++#ifdef CONFIG_KGDB_THREAD ++static int ++stubhex ( ++ int ch) ++{ ++ if (ch >= 'a' && ch <= 'f') ++ return ch - 'a' + 10; ++ if (ch >= '0' && ch <= '9') ++ return ch - '0'; ++ if (ch >= 'A' && ch <= 'F') ++ return ch - 'A' + 10; ++ return -1; ++} ++ ++ ++static int ++stub_unpack_int ( ++ char *buff, ++ int fieldlength) ++{ ++ int nibble; ++ int retval = 0; ++ ++ while (fieldlength) ++ { ++ nibble = stubhex (*buff++); ++ retval |= nibble; ++ fieldlength--; ++ if (fieldlength) ++ retval = retval << 4; ++ } ++ return retval; ++} ++#endif ++ ++static char * ++pack_hex_byte ( ++ char *pkt, ++ int byte) ++{ ++ *pkt++ = hexchars[(byte >> 4) & 0xf]; ++ *pkt++ = hexchars[(byte & 0xf)]; ++ return pkt; ++} ++ ++#define BUF_THREAD_ID_SIZE 16 ++ ++#ifdef CONFIG_KGDB_THREAD ++static char * ++pack_threadid ( ++ char *pkt, ++ threadref *id) ++{ ++ char *limit; ++ unsigned char *altid; ++ ++ altid = (unsigned char *) id; ++ limit = pkt + BUF_THREAD_ID_SIZE; ++ while (pkt < limit) ++ pkt = pack_hex_byte (pkt, *altid++); ++ return pkt; ++} ++ ++static char * ++unpack_byte ( ++ char *buf, ++ int *value) ++{ ++ *value = stub_unpack_int (buf, 2); ++ return buf + 2; ++} ++ ++static char * ++unpack_threadid ( ++ char *inbuf, ++ threadref *id) ++{ ++ char *altref; ++ char *limit = inbuf + BUF_THREAD_ID_SIZE; ++ int x, y; ++ ++ altref = (char *) id; ++ ++ while (inbuf < limit) ++ { ++ x = stubhex (*inbuf++); ++ y = stubhex (*inbuf++); ++ *altref++ = (x << 4) | y; ++ } ++ return inbuf; ++} ++#endif ++ ++void ++int_to_threadref ( ++ threadref *id, ++ int value) ++{ ++ unsigned char *scan; ++ ++ scan = (unsigned char *) id; ++ { ++ int i = 4; ++ while (i--) ++ *scan++ = 0; ++ } ++ *scan++ = (value >> 24) & 0xff; ++ *scan++ = (value >> 16) & 0xff; ++ *scan++ = (value >> 8) & 0xff; ++ *scan++ = (value & 0xff); ++} ++ ++#ifdef CONFIG_KGDB_THREAD ++static int ++threadref_to_int ( ++ threadref *ref) ++{ ++ int i, value = 0; ++ unsigned char *scan; ++ ++ scan = (char *) ref; ++ scan += 4; ++ i = 4; ++ while (i-- > 0) ++ value = (value << 8) | ((*scan++) & 0xff); ++ return value; ++} ++ ++ ++struct task_struct * ++getthread ( ++ int pid) ++{ ++ task_t *thread; ++ thread = find_task_by_pid(pid); ++ if (thread) { ++ return thread; ++ } ++ thread = &init_task; ++ do { ++ if (thread->pid == pid) { ++ return thread; ++ } ++ thread = thread->next_task; ++ } while (thread != &init_task); ++ return NULL; ++} ++#endif ++ ++struct hw_breakpoint { ++ unsigned enabled; ++ unsigned type; ++ unsigned len; ++ unsigned addr; ++} breakinfo[4] = { { enabled:0 }, { enabled:0 }, { enabled:0 }, { enabled:0 }}; ++ ++void correct_hw_break( void ) ++{ ++ int breakno; ++ int correctit; ++ int breakbit; ++ unsigned dr7; ++ ++ asm volatile ( ++ "movl %%db7, %0\n" ++ : "=r" (dr7) ++ : ); ++ do ++ { ++ unsigned addr0, addr1, addr2, addr3; ++ asm volatile ( ++ "movl %%db0, %0\n" ++ "movl %%db1, %1\n" ++ "movl %%db2, %2\n" ++ "movl %%db3, %3\n" ++ : "=r" (addr0), "=r" (addr1), "=r" (addr2), ++ "=r" (addr3) : ); ++ } while (0); ++ correctit = 0; ++ for (breakno = 0; breakno < 3; breakno++) { ++ breakbit = 2 << (breakno << 1); ++ if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { ++ correctit = 1; ++ dr7 |= breakbit; ++ dr7 &= ~(0xf0000 << (breakno << 2)); ++ dr7 |= (((breakinfo[breakno].len << 2) | ++ breakinfo[breakno].type) << 16) << ++ (breakno << 2); ++ switch (breakno) { ++ case 0: ++ asm volatile ("movl %0, %%dr0\n" ++ : ++ : "r" (breakinfo[breakno].addr) ); ++ break; ++ ++ case 1: ++ asm volatile ("movl %0, %%dr1\n" ++ : ++ : "r" (breakinfo[breakno].addr) ); ++ break; ++ ++ case 2: ++ asm volatile ("movl %0, %%dr2\n" ++ : ++ : "r" (breakinfo[breakno].addr) ); ++ break; ++ ++ case 3: ++ asm volatile ("movl %0, %%dr3\n" ++ : ++ : "r" (breakinfo[breakno].addr) ); ++ break; ++ } ++ } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled){ ++ correctit = 1; ++ dr7 &= ~breakbit; ++ dr7 &= ~(0xf0000 << (breakno << 2)); ++ } ++ } ++ if (correctit) { ++ asm volatile ( "movl %0, %%db7\n" ++ : ++ : "r" (dr7)); ++ } ++} ++ ++int remove_hw_break( ++ unsigned breakno) ++{ ++ if (!breakinfo[breakno].enabled) { ++ return -1; ++ } ++ breakinfo[breakno].enabled = 0; ++ return 0; ++} ++ ++int set_hw_break( ++ unsigned breakno, ++ unsigned type, ++ unsigned len, ++ unsigned addr) ++{ ++ if (breakinfo[breakno].enabled) { ++ return -1; ++ } ++ breakinfo[breakno].enabled = 1; ++ breakinfo[breakno].type = type; ++ breakinfo[breakno].len = len; ++ breakinfo[breakno].addr = addr; ++ return 0; ++} ++ ++static int ++set_break ( ++ unsigned addr) ++{ ++ int i, breakno = -1; ++ ++ for (i = 0; i < MAX_BREAKPOINTS; i++) { ++ if ((kgdb_break[i].state == bp_enabled) && ++ (kgdb_break[i].bpt_addr == addr)) { ++ breakno = -1; ++ break; ++ } ++ ++ if (kgdb_break[i].state == bp_disabled) { ++ if ((breakno == -1) || (kgdb_break[i].bpt_addr == addr)) ++ breakno = i; ++ } ++ } ++ if (breakno == -1) ++ return -1; ++ ++ get_mem((char *)addr,kgdb_break[breakno].saved_instr,BREAK_INSTR_SIZE); ++ if (kgdb_memerr) ++ return -1; ++ ++ set_mem((char *)addr,gdb_bpt_instr,BREAK_INSTR_SIZE); ++ if (kgdb_memerr) ++ return -1; ++ ++ kgdb_break[breakno].state = bp_enabled; ++ kgdb_break[breakno].type = bp_breakpoint; ++ kgdb_break[breakno].bpt_addr = addr; ++ ++ return 0; ++} ++ ++static int ++remove_break (unsigned addr) ++{ ++ int i; ++ ++ for (i=0; ithread.kgdbregs = regs; ++ ++ /* Wait till master processor goes completely into the debugger */ ++ while (!procindebug[kgdb_lock - 1]) { ++ int i = 10; /* an arbitrary number */ ++ ++ while (--i) ++ asm volatile ("nop": : : "memory"); ++ ++ } ++ ++ /* Wait till master processor is done with debugging */ ++ spin_lock(slavecpulocks + processor); ++ correct_hw_break(); ++ ++ /* Signal the master processor that we are done */ ++ procindebug[processor] = 0; ++ spin_unlock(slavecpulocks + processor); ++ local_irq_restore(flags); ++} ++ ++void ++printexceptioninfo( ++ int exceptionNo, ++ int errorcode, ++ char *buffer) ++{ ++ unsigned dr6; ++ int i; ++ switch (exceptionNo) { ++ case 1: /* debug exception */ ++ break; ++ case 3: /* breakpoint */ ++ sprintf(buffer, "Software breakpoint"); ++ return; ++ default: ++ sprintf(buffer, "Details not available"); ++ return; ++ } ++ asm volatile ("movl %%db6, %0\n" ++ : "=r" (dr6) ++ : ); ++ if (dr6 & 0x4000) { ++ sprintf(buffer, "Single step"); ++ return; ++ } ++ for (i = 0; i < 4; ++i) { ++ if (dr6 & (1 << i)) { ++ sprintf(buffer, "Hardware breakpoint %d", i); ++ return; ++ } ++ } ++ sprintf(buffer, "Unknown trap"); ++ return; ++} ++ ++/* ++ * This function does all command procesing for interfacing to gdb. ++ * ++ * NOTE: The INT nn instruction leaves the state of the interrupt ++ * enable flag UNCHANGED. That means that when this routine ++ * is entered via a breakpoint (INT 3) instruction from code ++ * that has interrupts enabled, then interrupts will STILL BE ++ * enabled when this routine is entered. The first thing that ++ * we do here is disable interrupts so as to prevent recursive ++ * entries and bothersome serial interrupts while we are ++ * trying to run the serial port in polled mode. ++ * ++ * For kernel version 2.1.xx the cli() actually gets a spin lock so ++ * it is always necessary to do a restore_flags before returning ++ * so as to let go of that lock. ++ */ ++int handle_exception(int exceptionVector, ++ int signo, ++ int err_code, ++ struct pt_regs *linux_regs) ++{ ++ struct task_struct *usethread = NULL; ++ int addr, length; ++ int breakno, breaktype; ++ char *ptr; ++ int newPC; ++ unsigned long flags; ++ int gdb_regs[NUMREGBYTES/4]; ++ int i; ++ int dr6; ++ int ret; ++ int maxbpt, bpt_done; ++ unsigned long bptaddr; ++ struct pt_regs tempregs; ++#ifdef CONFIG_KGDB_THREAD ++ int nothreads; ++ int maxthreads; ++ int threadid; ++ threadref thref; ++ struct task_struct *thread = NULL; ++#endif ++ unsigned procid; ++ ++#define regs (*linux_regs) ++ ++ /* ++ * If the entry is not from the kernel then return to the Linux ++ * trap handler and let it process the interrupt normally. ++ */ ++ if ((linux_regs->eflags & VM_MASK) || (3 & linux_regs->xcs)) { ++ return(0); ++ } ++ ++ if (kgdb_memerr_expected) ++ { ++ /* ++ * This fault occured because of the get_char or set_char ++ * routines. These two routines use either eax of edx to ++ * indirectly reference the location in memory that they ++ * are working with. For a page fault, when we return ++ * the instruction will be retried, so we have to make ++ * sure that these registers point to valid memory. ++ */ ++ kgdb_memerr = 1 ; /* set mem error flag */ ++ kgdb_memerr_expected = 0 ; ++ kgdb_memerr_cnt++ ; /* helps in debugging */ ++ regs.eax = (long) &garbage_loc ; /* make valid address */ ++ regs.edx = (long) &garbage_loc ; /* make valid address */ ++ return(0) ; ++ } ++ ++ /* Hold kgdb_lock */ ++ procid = smp_processor_id(); ++ while (cmpxchg(&kgdb_lock, 0, (procid + 1)) != 0) { ++ int i = 25; /* an arbitrary number */ ++ ++ while (--i) ++ asm volatile ("nop": : : "memory"); ++ } ++ ++ kgdb_step = 0; ++ ++ local_irq_save(flags); ++ ++ /* Disable hardware debugging while we are in kgdb */ ++ __asm__("movl %0,%%db7" ++ : /* no output */ ++ : "r" (0)); ++ ++ for (i = 0; i < smp_num_cpus; i++) { ++ spin_lock(&slavecpulocks[i]); ++ } ++ ++ /* spin_lock code is good enough as a barrier so we don't ++ * need one here */ ++ procindebug[smp_processor_id()] = 1; ++ ++ /* Master processor is completely in the debugger */ ++ ++ gdb_i386vector = exceptionVector; ++ gdb_i386errcode = err_code ; ++ ++ /* reply to host that an exception has occurred */ ++ remcomOutBuffer[0] = 'S'; ++ remcomOutBuffer[1] = hexchars[signo >> 4]; ++ remcomOutBuffer[2] = hexchars[signo % 16]; ++ remcomOutBuffer[3] = 0; ++ ++ putpacket(remcomOutBuffer); ++ ++ while (1==1) { ++ error = 0; ++ remcomOutBuffer[0] = 0; ++ getpacket(remcomInBuffer); ++ switch (remcomInBuffer[0]) { ++ case '?' : ++ remcomOutBuffer[0] = 'S'; ++ remcomOutBuffer[1] = hexchars[signo >> 4]; ++ remcomOutBuffer[2] = hexchars[signo % 16]; ++ remcomOutBuffer[3] = 0; ++ break; ++ case 'g' : /* return the value of the CPU registers */ ++ if (!usethread || usethread == current) { ++ regs_to_gdb_regs(gdb_regs, ®s) ; ++ } else { ++ memset(gdb_regs, 0, NUMREGBYTES); ++ if (usethread->thread.kgdbregs) { ++ kgdb_memerr_expected = 1 ; ++ kgdb_memerr = 0; ++ get_char((char *)usethread->thread.kgdbregs); ++ kgdb_memerr_expected = 0; ++ if (kgdb_memerr) { ++ *(((char *)&tempregs) + i) = '\0'; ++ gdb_regs[_PC] = kgdb_usercode; ++ } else { ++ regs_to_gdb_regs(gdb_regs, ++ usethread->thread.kgdbregs); ++ } ++ } else { ++ gdb_regs[_PC] = kgdb_usercode; ++ } ++ } ++ mem2hex((char*) gdb_regs, remcomOutBuffer, NUMREGBYTES, 0); ++ break; ++ case 'G' : /* set the value of the CPU registers - return OK */ ++ hex2mem(&remcomInBuffer[1], (char*) gdb_regs, NUMREGBYTES, 0); ++ if (!usethread || usethread == current) { ++ gdb_regs_to_regs(gdb_regs, ®s) ; ++ strcpy(remcomOutBuffer,"OK"); ++ } else { ++ strcpy(remcomOutBuffer,"E00"); ++ } ++ break; ++ ++ /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ ++ case 'm' : ++ /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ ++ ptr = &remcomInBuffer[1]; ++ if (hexToInt(&ptr,&addr)) ++ if (*(ptr++) == ',') ++ if (hexToInt(&ptr,&length)) ++ { ++ ptr = 0; ++ mem2hex((char*) addr, remcomOutBuffer, length, 1); ++ if (kgdb_memerr) { ++ strcpy (remcomOutBuffer, "E03"); ++ } ++ } ++ ++ if (ptr) ++ { ++ strcpy(remcomOutBuffer,"E01"); ++ } ++ break; ++ ++ /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ ++ case 'M' : ++ /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ ++ ptr = &remcomInBuffer[1]; ++ if (hexToInt(&ptr,&addr)) ++ if (*(ptr++) == ',') ++ if (hexToInt(&ptr,&length)) ++ if (*(ptr++) == ':') ++ { ++ hex2mem(ptr, (char*) addr, length, 1); ++ ++ if (kgdb_memerr) { ++ strcpy (remcomOutBuffer, "E03"); ++ } else { ++ strcpy(remcomOutBuffer,"OK"); ++ } ++ ++ ptr = 0; ++ } ++ if (ptr) ++ { ++ strcpy(remcomOutBuffer,"E02"); ++ } ++ break; ++ ++ /* cAA..AA Continue at address AA..AA(optional) */ ++ /* sAA..AA Step one instruction from AA..AA(optional) */ ++ /* D Detach from debugger */ ++ case 'c' : ++ case 's' : ++ case 'D' : ++ ++ /* try to read optional parameter, pc unchanged if no parm */ ++ ptr = &remcomInBuffer[1]; ++ if (hexToInt(&ptr,&addr)) ++ { ++ regs.eip = addr; ++ } ++ ++ newPC = regs.eip ; ++ ++ /* clear the trace bit */ ++ regs.eflags &= 0xfffffeff; ++ ++ /* set the trace bit if we're stepping */ ++ if (remcomInBuffer[0] == 's') { ++ regs.eflags |= 0x100; ++ kgdb_step = 1; ++ } ++ if (remcomInBuffer[0] == 'D') { ++ clear_disabled_break(); ++ strcpy(remcomOutBuffer,"+"); ++ putpacket(remcomOutBuffer); ++ } ++ ++ asm volatile ("movl %%db6, %0\n" ++ : "=r" (dr6) ++ : ); ++ if (!(dr6 & 0x4000)) { ++ for (breakno = 0; breakno < 4; ++breakno) { ++ if (dr6 & (1 << breakno)) { ++ if (breakinfo[breakno].type == 0) { ++ /* Set restore flag */ ++ regs.eflags |= 0x10000; ++ break; ++ } ++ } ++ } ++ } ++ correct_hw_break(); ++ asm volatile ( ++ "movl %0, %%db6\n" ++ : ++ : "r" (0) ); ++ ++ procindebug[smp_processor_id()] = 0; ++ ++ /* Signal the slave processors to quit from ++ * the debugger */ ++ for (i = 0; i < smp_num_cpus; i++) { ++ spin_unlock(&slavecpulocks[i]); ++ } ++ ++ /* Wait till all the processors have quit ++ * from the debugger */ ++ ++ for (i = 0; i < smp_num_cpus; i++) { ++ while (procindebug[i]) { ++ int j = 10; /* an arbitrary number */ ++ ++ while (--j) { ++ asm volatile ("nop" ++ : : : "memory"); ++ } ++ } ++ } ++ ++ /* Release kgdb_lock */ ++ asm volatile ( ++#ifndef CONFIG_X86_OOSTORE ++ "movb $0,%0" ++#else ++ "lock movb $0,%0" ++#endif ++ : "=m" (kgdb_lock) : : "memory"); ++ ++ local_irq_restore(flags) ; ++ return(0) ; ++ ++ /* kill the program */ ++ case 'k' : /* do nothing */ ++ break; ++ ++ /* query */ ++ case 'q' : ++ switch (remcomInBuffer[1]) { ++#ifdef CONFIG_KGDB_THREAD ++ case 'L': ++ /* List threads */ ++ unpack_byte(remcomInBuffer+3, &maxthreads); ++ unpack_threadid(remcomInBuffer+5, &thref); ++ ++ remcomOutBuffer[0] = 'q'; ++ remcomOutBuffer[1] = 'M'; ++ remcomOutBuffer[4] = '0'; ++ pack_threadid(remcomOutBuffer+5, &thref); ++ ++ threadid = threadref_to_int(&thref); ++ for (nothreads = 0; ++ nothreads < maxthreads && threadid< PID_MAX; ++ threadid++ ) ++ { ++ read_lock(&tasklist_lock); ++ thread = getthread(threadid); ++ read_unlock(&tasklist_lock); ++ if (thread) { ++ int_to_threadref(&thref, threadid); ++ pack_threadid(remcomOutBuffer+21+nothreads*16, ++ &thref); ++ nothreads++; ++ } ++ } ++ if (threadid == PID_MAX) { ++ remcomOutBuffer[4] = '1'; ++ } ++ pack_hex_byte(remcomOutBuffer+2, nothreads); ++ remcomOutBuffer[21+nothreads*16] = '\0'; ++ break; ++ ++ case 'C': ++ /* Current thread id */ ++ remcomOutBuffer[0] = 'Q'; ++ remcomOutBuffer[1] = 'C'; ++ threadid = current->pid; ++ int_to_threadref(&thref, threadid); ++ pack_threadid(remcomOutBuffer+2, &thref); ++ remcomOutBuffer[18] = '\0'; ++ break; ++#endif ++ ++ case 'E': ++ /* Print exception info */ ++ printexceptioninfo(exceptionVector, err_code, remcomOutBuffer); ++ break; ++ } ++ break; ++ ++#ifdef CONFIG_KGDB_THREAD ++ /* task related */ ++ case 'H' : ++ switch (remcomInBuffer[1]) { ++ case 'g': ++ ptr = &remcomInBuffer[2]; ++ hexToInt(&ptr, &threadid); ++ thread = getthread(threadid); ++ if (!thread) { ++ remcomOutBuffer[0] = 'E'; ++ remcomOutBuffer[1] = '\0'; ++ break; ++ } ++ usethread = thread; ++ /* follow through */ ++ case 'c': ++ remcomOutBuffer[0] = 'O'; ++ remcomOutBuffer[1] = 'K'; ++ remcomOutBuffer[2] = '\0'; ++ break; ++ } ++ break; ++ ++ /* Query thread status */ ++ case 'T': ++ ptr = &remcomInBuffer[1]; ++ hexToInt(&ptr, &threadid); ++ thread = getthread(threadid); ++ if (thread) { ++ remcomOutBuffer[0] = 'O'; ++ remcomOutBuffer[1] = 'K'; ++ remcomOutBuffer[2] = '\0'; ++ } else { ++ remcomOutBuffer[0] = 'E'; ++ remcomOutBuffer[1] = '\0'; ++ } ++ break; ++#endif ++ ++ case 'Y': ++ ptr = &remcomInBuffer[1]; ++ hexToInt(&ptr, &breakno); ++ ptr++; ++ hexToInt(&ptr, &breaktype); ++ ptr++; ++ hexToInt(&ptr, &length); ++ ptr++; ++ hexToInt(&ptr, &addr); ++ if (set_hw_break(breakno & 0x3, breaktype & 0x3 , length & 0x3, addr) ++ == 0) { ++ strcpy(remcomOutBuffer, "OK"); ++ } else { ++ strcpy(remcomOutBuffer, "ERROR"); ++ } ++ break; ++ ++ /* Remove hardware breakpoint */ ++ case 'y': ++ ptr = &remcomInBuffer[1]; ++ hexToInt(&ptr, &breakno); ++ if (remove_hw_break(breakno & 0x3) == 0) { ++ strcpy(remcomOutBuffer, "OK"); ++ } else { ++ strcpy(remcomOutBuffer, "ERROR"); ++ } ++ break; ++ case 'Z': ++ case 'z': ++ ptr = &remcomInBuffer[1]; ++ if (*ptr++ != bp_breakpoint) ++ break; ++ if (*(ptr++) != ',') { ++ strcpy(remcomOutBuffer, "ERROR"); ++ break; ++ } ++ hexToInt(&ptr, &addr); ++ if (remcomInBuffer[0] == 'Z') ++ ret = set_break(addr); ++ else ++ ret = remove_break(addr); ++ ++ if (ret == 0) { ++ strcpy(remcomOutBuffer, "OK"); ++ } else { ++ strcpy(remcomOutBuffer, "ERROR"); ++ } ++ break; ++ ++ } /* switch */ ++ ++ /* reply to the request */ ++ putpacket(remcomOutBuffer); ++ } ++} ++ ++/* this function is used to set up exception handlers for tracing and ++ breakpoints */ ++void set_debug_traps(void) ++{ ++ int i = 0; ++ /* ++ * linux_debug_hook is defined in traps.c. We store a pointer ++ * to our own exception handler into it. ++ */ ++ linux_debug_hook = handle_exception ; ++ ++ for (i=0; i 0) { ++ if ((count << 1) > (BUFMAX - 2)) { ++ wcount = (BUFMAX - 2) >> 1; ++ } else { ++ wcount = count; ++ } ++ count -= wcount; ++ for (i = 0; i < wcount; i++) { ++ bufptr = pack_hex_byte(bufptr, s[i]); ++ } ++ *bufptr = '\0'; ++ s += wcount; ++ ++ putpacket(gdbconbuf); ++ ++ } ++} ++#endif ++static int __init kgdb_opt_gdb(void) ++{ ++ gdb_enter = 1; ++ return 1; ++} ++static int __init kgdb_opt_gdbttyS(char *str) ++{ ++ gdb_ttyS = simple_strtoul(str,NULL,10); ++ return 1; ++} ++static int __init kgdb_opt_gdbbaud(char *str) ++{ ++ gdb_baud = simple_strtoul(str,NULL,10); ++ return 1; ++} ++ ++/* ++ * Sequence of these lines has to be maintained because gdb option is a prefix ++ * of the other two options ++ */ ++ ++__setup("gdbttyS=", kgdb_opt_gdbttyS); ++__setup("gdbbaud=", kgdb_opt_gdbbaud); ++__setup("gdb", kgdb_opt_gdb); +--- linux-2.4.18-p4smp/arch/i386/kernel/Makefile~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/Makefile 2003-07-09 13:15:23.000000000 -0600 +@@ -10,7 +10,15 @@ + .S.o: + $(CC) $(AFLAGS) -traditional -c $< -o $*.o + +-all: kernel.o head.o init_task.o ++ifeq ($(CONFIG_X86_REMOTE_DEBUG),y) ++GDBSTART=gdbstart ++GDBCLEAN= -rm -f gdbstart /sbin/gdbstart ++else ++GDBSTART= ++GDBCLEAN= ++endif ++ ++all: kernel.o head.o init_task.o $(GDBSTART) + + O_TARGET := kernel.o + +@@ -33,6 +41,7 @@ endif + obj-$(CONFIG_MCA) += mca.o + obj-$(CONFIG_E820_PROC) += e820.o rpmhelper.o + obj-$(CONFIG_MTRR) += mtrr.o ++obj-$(CONFIG_X86_REMOTE_DEBUG) += gdbstub.o + obj-$(CONFIG_X86_MSR) += msr.o + obj-$(CONFIG_X86_CPUID) += cpuid.o + obj-$(CONFIG_MICROCODE) += microcode.o +@@ -51,4 +60,11 @@ obj-$(CONFIG_ABI) += lcall7.o + obj-$(CONFIG_MCL_COREDUMP) += crash.o + + ++gdbstart: gdbstart.o ++ ${HOSTCC} -o gdbstart gdbstart.o ++gdbstart.o: gdbstart.c ++ ${HOSTCC} -c -o gdbstart.o gdbstart.c ++ ++kernelclean: dummy ++ + include $(TOPDIR)/Rules.make +--- linux-2.4.18-p4smp/arch/i386/kernel/nmi.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/nmi.c 2003-07-09 13:15:23.000000000 -0600 +@@ -25,6 +25,20 @@ + #include + #include + ++#ifdef CONFIG_X86_REMOTE_DEBUG ++extern gdb_debug_hook * linux_debug_hook; ++#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) \ ++ { \ ++ if (linux_debug_hook != (gdb_debug_hook *) NULL && !user_mode(regs)) \ ++ { \ ++ (*linux_debug_hook)(trapnr, signr, error_code, regs) ; \ ++ after; \ ++ } \ ++ } ++#else ++#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) ++#endif ++ + unsigned int nmi_watchdog = NMI_NONE; + static unsigned int nmi_hz = HZ; + unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ +@@ -347,7 +361,20 @@ void nmi_watchdog_tick (struct pt_regs * + x86_do_profile(regs->eip); + + sum = irq_stat[cpu].apic_timer_irqs; ++#ifdef CONFIG_X86_REMOTE_DEBUG ++ if (kgdb_lock) { ++ ++ /* ++ * The machine is in kgdb, hold this cpu if already ++ * not held. ++ */ + ++ if (!procindebug[cpu] && kgdb_lock != (cpu + 1)) { ++ gdb_wait(regs); ++ } ++ alert_counter[cpu] = 0; ++ } else ++#endif + if (last_irq_sums[cpu] == sum) { + /* + * Ayiee, looks like this CPU is stuck ... +@@ -355,6 +382,9 @@ void nmi_watchdog_tick (struct pt_regs * + */ + alert_counter[cpu]++; + if (alert_counter[cpu] == 5*nmi_hz) { ++ ++ CHK_REMOTE_DEBUG(2,SIGSEGV,0,regs,) ++ + spin_lock(&nmi_print_lock); + /* + * We are in trouble anyway, lets at least try +--- linux-2.4.18-p4smp/arch/i386/kernel/signal.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/signal.c 2003-07-09 13:15:23.000000000 -0600 +@@ -701,7 +701,8 @@ int do_signal(struct pt_regs *regs, sigs + * have been cleared if the watchpoint triggered + * inside the kernel. + */ +- __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7])); ++ if (current->thread.debugreg[7]) ++ __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7])); + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, ka, &info, oldset, regs); +--- linux-2.4.18-p4smp/arch/i386/kernel/traps.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/arch/i386/kernel/traps.c 2003-07-09 13:19:30.000000000 -0600 +@@ -49,12 +49,33 @@ + #endif + + #include ++#ifdef CONFIG_X86_REMOTE_DEBUG ++#include ++#endif + #include + + #ifdef CONFIG_MCL_COREDUMP + #include + #endif + ++#ifdef CONFIG_X86_REMOTE_DEBUG ++#include ++#endif ++ ++#ifdef CONFIG_X86_REMOTE_DEBUG ++gdb_debug_hook * linux_debug_hook; ++#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) \ ++ { \ ++ if (linux_debug_hook != (gdb_debug_hook *) NULL && !user_mode(regs)) \ ++ { \ ++ (*linux_debug_hook)(trapnr, signr, error_code, regs) ; \ ++ after; \ ++ } \ ++ } ++#else ++#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) ++#endif ++ + asmlinkage int system_call(void); + asmlinkage void lcall7(void); + asmlinkage void lcall27(void); +@@ -409,6 +430,7 @@ void die(const char * str, struct pt_reg + bust_spinlocks(1); + handle_BUG(regs); + printk("%s: %04lx\n", str, err & 0xffff); ++ CHK_REMOTE_DEBUG(1,SIGTRAP,err,regs,) + show_registers(regs); + if (netdump_func) + netdump_func(regs); +@@ -479,6 +501,7 @@ static void inline do_trap(int trapnr, i + #define DO_ERROR(trapnr, signr, str, name) \ + asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ + { \ ++ CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,)\ + do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ + } + +@@ -496,7 +519,10 @@ asmlinkage void do_##name(struct pt_regs + #define DO_VM86_ERROR(trapnr, signr, str, name) \ + asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ + { \ ++ CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,goto skip_trap)\ + do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ ++skip_trap: \ ++ return; \ + } + + #define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +@@ -548,6 +574,7 @@ gp_in_kernel: + regs->eip = fixup; + return; + } ++ CHK_REMOTE_DEBUG(13,SIGSEGV,error_code,regs,) + die("general protection fault", regs, error_code); + } + } +@@ -659,7 +686,7 @@ asmlinkage void do_debug(struct pt_regs + __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); + + /* If the user set TF, it's simplest to clear it right away. */ +- if ((eip >=PAGE_OFFSET) && (regs->eflags & TF_MASK)) ++ if ((eip >=PAGE_OFFSET) && (regs->eflags & TF_MASK) && !kgdb_step) + goto clear_TF; + + /* Mask out spurious debug traps due to lazy DR7 setting */ +@@ -675,7 +702,7 @@ asmlinkage void do_debug(struct pt_regs + tsk->thread.debugreg[6] = condition; + + /* Mask out spurious TF errors due to lazy TF clearing */ +- if (condition & DR_STEP) { ++ if (condition & DR_STEP && !kgdb_step) { + /* + * The TF error should be masked out only if the current + * process is not traced and if the TRAP flag has been set +@@ -698,11 +725,13 @@ asmlinkage void do_debug(struct pt_regs + info.si_errno = 0; + info.si_code = TRAP_BRKPT; + +- /* If this is a kernel mode trap, save the user PC on entry to +- * the kernel, that's what the debugger can make sense of. +- */ +- info.si_addr = ((regs->xcs & 3) == 0) ? (void *)tsk->thread.eip : +- (void *)regs->eip; ++ ++ /* If this is a kernel mode trap, we need to reset db7 to allow us ++ * to continue sanely */ ++ if ((regs->xcs & 3) == 0) ++ goto clear_dr7; ++ ++ info.si_addr = (void *)regs->eip; + force_sig_info(SIGTRAP, &info, tsk); + + /* Disable additional traps. They'll be re-enabled when +@@ -712,6 +741,7 @@ clear_dr7: + __asm__("movl %0,%%db7" + : /* no output */ + : "r" (0)); ++ CHK_REMOTE_DEBUG(1,SIGTRAP,error_code,regs,) + return; + + debug_vm86: +--- linux-2.4.18-p4smp/arch/i386/Makefile~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/arch/i386/Makefile 2003-07-09 13:15:23.000000000 -0600 +@@ -105,6 +105,11 @@ arch/i386/mm: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/i386/mm + + MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot ++ifeq ($(CONFIG_X86_REMOTE_DEBUG),y) ++CLEANKERNEL = $(MAKE) -C arch/$(ARCH)/kernel kernelclean ++else ++CLEANKERNEL = ++endif + + vmlinux: arch/i386/vmlinux.lds + +@@ -140,6 +145,7 @@ install: vmlinux + + archclean: + @$(MAKEBOOT) clean ++ @$(CLEANKERNEL) + + archmrproper: + +--- linux-2.4.18-p4smp/arch/i386/mm/fault.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/arch/i386/mm/fault.c 2003-07-09 13:15:23.000000000 -0600 +@@ -2,6 +2,11 @@ + * linux/arch/i386/mm/fault.c + * + * Copyright (C) 1995 Linus Torvalds ++ * ++ * Change History ++ * ++ * Tigran Aivazian Remote debugging support. ++ * + */ + + #include +@@ -19,6 +24,9 @@ + #include + #include + #include /* For unblank_screen() */ ++#ifdef CONFIG_X86_REMOTE_DEBUG ++#include ++#endif + + #include + #include +@@ -183,6 +191,15 @@ asmlinkage void do_page_fault(struct pt_ + if (in_interrupt() || !mm) + goto no_context; + ++#ifdef CONFIG_X86_REMOTE_DEBUG ++ if (kgdb_memerr_expected) { ++ if (linux_debug_hook != (gdb_debug_hook *) NULL) { ++ (*linux_debug_hook)(14, SIGSEGV, error_code, regs) ; ++ return; /* return w/modified regs */ ++ } ++ } ++#endif ++ + down_read(&mm->mmap_sem); + + vma = find_vma(mm, address); +@@ -301,6 +318,19 @@ no_context: + return; + } + ++#ifdef CONFIG_X86_REMOTE_DEBUG ++ if (kgdb_memerr_expected) { ++ if (linux_debug_hook != (gdb_debug_hook *) NULL) { ++ (*linux_debug_hook)(14, SIGSEGV, error_code, regs); ++ return; /* Return with modified registers */ ++ } ++ } else { ++ if (linux_debug_hook != (gdb_debug_hook *) NULL) { ++ (*linux_debug_hook)(14, SIGSEGV, error_code, regs); ++ } ++ } ++#endif ++ + /* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. +@@ -308,6 +338,7 @@ no_context: + + bust_spinlocks(1); + ++ + if (address < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else +--- linux-2.4.18-p4smp/Documentation/Configure.help~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/Documentation/Configure.help 2003-07-09 13:15:23.000000000 -0600 +@@ -20274,6 +20274,29 @@ CONFIG_SGIWD93_SCSI + If you have a Western Digital WD93 SCSI controller on + an SGI MIPS system, say Y. Otherwise, say N. + ++KGDB: Remote (serial) kernel debugging with gdb ++CONFIG_X86_REMOTE_DEBUG ++ If you say Y here, it will be possible to remotely debug the x86 ++ kernel using gdb. This enlarges your kernel image disk size by ++ several megabytes and requires a machine with more than 16 MB, ++ better 32 MB RAM to avoid excessive linking time. ++ To use this feature you need to perform some basic setup described ++ briefly in Documentation/i386/gdb-serial.txt. ++ This is only useful for kernel hackers. If unsure, say N. ++ ++KGDB: Thread analysis ++CONFIG_KGDB_THREAD ++ With thread analysis enabled, gdb can talk to kgdb stub to list ++ threads and to get stack trace for a thread. This option also enables ++ some code which helps gdb get exact status of thread. Thread analysis ++ adds some overhead to schedule and down functions. You can disable this ++ option if you do not want to compromise on speed. ++ ++KGDB: Console messagegs through gdb ++CONFIG_GDB_CONSOLE ++ If you say Y here, console messages will appear through gdb. ++ Other consoles such as tty or ttyS will continue to work as usual. ++ + Magic System Request Key support + CONFIG_MAGIC_SYSRQ + If you say Y here, you will have some control over the system even +--- /dev/null 2003-01-30 03:24:37.000000000 -0700 ++++ linux-2.4.18-p4smp-braam/Documentation/i386/gdb-serial.txt 2003-07-09 13:15:23.000000000 -0600 +@@ -0,0 +1,386 @@ ++Version ++======= ++ ++This version of the gdbstub package was developed and tested on ++kernel version 2.3.48. It will not install on a 2.2 kernel. It may ++not work on earlier versions of 2.3 kernels. It is possible that ++it will continue to work on later versions of 2.3 and then ++versions of 2.4 (I hope). ++ ++ ++Debugging Setup ++=============== ++ ++Designate one machine as the "development" machine. This is the ++machine on which you run your compiles and which has your source ++code for the kernel. Designate a second machine as the "target" ++machine. This is the machine that will run your experimental ++kernel. ++ ++The two machines will be connected together via a serial line out ++one or the other of the COM ports of the PC. You will need a modem ++eliminator and the appropriate cables. ++ ++On the DEVELOPMENT machine you need to apply the patch for the gdb ++hooks. You have probably already done that if you are reading this ++file. ++ ++On your DEVELOPMENT machine, go to your kernel source directory and ++do "make menuconfig". Go down to the kernel hacking menu item and ++open it up. Enable the kernel gdb stub code by selecting that item. ++ ++Save and exit the menuconfig program. Then do "make clean" and ++"make bzImage" (or whatever target you want to make). This gets ++the kernel compiled with the "-g" option set -- necessary for ++debugging. ++ ++You have just built the kernel on your DEVELOPMENT machine that you ++intend to run on our TARGET machine. ++ ++To install this new kernel, use the following installation procedure. ++Remember, you are on the DEVELOPMENT machine patching the kernel source ++for the kernel that you intend to run on the TARGET machine. ++ ++Copy this kernel to your target machine using your usual procedures. ++I usually arrange to copy development:/usr/src/linux/arch/i386/boot/zImage ++to /vmlinuz on the TARGET machine via a LAN based NFS access. That is, ++I run the cp command on the target and copy from the development machine ++via the LAN. Run Lilo on the new kernel on the target machine so that it ++will boot! Then boot the kernel on the target machine. ++ ++There is an utility program named "gdbstart" in the ++development:/usr/src/linux/arch/i386/kernel directory. ++You should copy this program over to your target machine, probably into ++/sbin. This utility program is run on the target machine to ++activate the kernel hooks for the debugger. It is invoked as follows: ++ ++ gdbstart [-s speed] [-t tty-dev] ++ defaults: /dev/ttyS0 with speed unmodified by gdbstart ++ ++Don't run the program just yet. We'll get to that in a bit. ++ ++Decide on which tty port you want the machines to communicate, then ++cable them up back-to-back using the null modem. COM1 is /dev/ttyS0 ++and COM2 is /dev/ttyS1. ++ ++On the DEVELOPMENT machine, create a file called .gdbinit in the ++directory /usr/src/linux. An example .gdbinit file looks like this: ++ ++define rmt ++set remotebaud 38400 ++target remote /dev/ttyS0 ++end ++ ++Assuming that you added my gdbinit stuff to your .gdbinit, edit .gdbinit ++and find the section that looks like this: ++ ++ define rmt ++ set remotebaud 38400 ++ target remote /dev/ttyS0 ++ end ++ ++Change the "target" definition so that it specifies the tty port that ++you intend to use. Change the "remotebaud" definition to match the ++data rate that you are going to use for the com line. ++ ++On the TARGET machine I find it helpful to create shell script file ++named "debug" in the root home directory with the following contents: ++ ++ gdbstart -s 38400 -t /dev/ttyS0 < ++ EOF ++ ++This runs the gdbstart program and gives it the carriage return that ++it prompts for. This sets the data rate from the target machine's side. ++ ++You are now ready to try it out. ++ ++On your TARGET machine, freshly rebooted with your gdbstub-equipped ++kernel, type "debug" in the root home directory. The system will appear ++to hang with some messages on the screen from the debug stub. What ++it is doing is waiting for contact from the development machine. ++ ++On your DEVELOPMENT machine, cd /usr/src/linux and enter "gdb vmlinux". ++When gdb gets the symbols loaded and prompts you, enter "rmt" (that's ++the macro from the .gdbinit file that you just edited). If everything ++is working correctly you should see gdb print out a few lines indicating ++that a breakpoint has been taken. It will actually show a line of ++code in the target kernel inside the gdbstub activation code. ++ ++The gdb interaction should look something like this: ++ ++ linux-dev:/usr/src/linux# gdb vmlinux ++ GDB is free software and you are welcome to distribute copies of it ++ under certain conditions; type "show copying" to see the conditions. ++ There is absolutely no warranty for GDB; type "show warranty" for details. ++ GDB 4.15.1 (i486-slackware-linux), ++ Copyright 1995 Free Software Foundation, Inc... ++ (gdb) rmt ++ breakpoint () at i386-stub.c:750 ++ 750 } ++ (gdb) ++ ++ ++You can now use whatever gdb commands you like to set breakpoints. ++Enter "continue" to start your target machine executing again. At this ++point the target system will run at full speed until it encounters ++your breakpoint or gets a segment violation in the kernel, or whatever. ++ ++ ++Triggering gdbstub at Kernel Boot Time ++====================================== ++ ++The gdbstub patch now has the ability for gdb to connect to the kernel during ++bootup (as opposed to waiting for the system to come all the way up and then ++running the gdbstart program on the target machine). This new functionality was ++added by Scott Foehner at SGI. ++ ++To force a kernel that has been compiled with gdbstub to pause during the boot ++process and wait for a connection from gdb, the paramter "gdb" should be passed ++to the kernel. This can be done by typing "gdb" after the name of the kernel ++on the LILO command line. The patch defaults to use ttyS1 at a baud rate of ++38400. These parameters can be changed by using "gdbttyS=" and ++"gdbbaud=" on the command line. ++ ++Example: ++ ++LILO boot: linux gdb gdbttyS=1 gdbbaud=38400 ++ ++Note that this command is entered on the TARGET machine as it is booting ++the kernel that was compiled on the DEVELOPMENT machine. ++ ++An alternate approach is to place a line in the /etc/lilo.conf file on ++your TARGET machine. Under the heading for the kernel that you intend ++to boot, place a line that looks like this: ++ ++ append = "gdb gdbttyS=1 gdbbaud=38400" ++ ++This will cause the kernel to enter the gdbstub automatically at boot ++time. ++ ++BE SURE to run "lilo" after changing the /etc/lilo.conf file. ++ ++ ++The "gdbstart" Program ++===================== ++ ++This utility program is used to set up the com port and data rate ++for the connection from the target system to the development system. ++Its usage has been described above. ++ ++This version of the patch uses the same tty ioctl for kernel versions ++2.0.30 onwards. Thus, the gdbstart utility does not need to be re-compiled ++to install the patch in a later version of the kernel. The ioctl added ++to the kernel for this purpose is far enough "off the end" of existing ++ioctls (as of 2.1.120) that it should not interfere with any new kernel ++tty ioctls for quite some time (famous last words). ++ ++The source for the gdbstart program resides in the arch/i386/kernel directory. ++ ++ ++Debugging hints ++=============== ++ ++You can break into the target machine at any time from the development ++machine by typing ^C. If the target machine has interrupts enabled ++this will stop it in the kernel and enter the debugger. ++ ++There is unfortunately no way of breaking into the kernel if it is ++in a loop with interrupts disabled, so if this happens to you then ++you need to place exploratory breakpoints or printk's into the kernel ++to find out where it is looping. ++ ++There is a copy of an e-mail in the kgdb distribution directory which ++describes how to create an NMI on an ISA bus machine using a paper ++clip. I have a sophisticated version of this made by wiring a push ++button switch into a PC104/ISA bus adapter card. The adapter card ++nicely furnishes wire wrap pins for all the ISA bus signals. ++ ++When you are done debugging the kernel on the target machine it is ++a good idea to leave it in a running state. This makes reboots ++faster, bypassing the fsck. So do a gdb "continue" as the last gdb ++command if this is possible. To terminate gdb itself on the development ++machine and leave the target machine running, type ^Z to suspend gdb ++and then kill it with "kill %1" or something similar. ++ ++If gdbstub Does Not Work ++======================== ++ ++If it doesn't work, you will have to troubleshoot it. Do the easy things ++first like double checking your cabling and data rates. You might ++try some non-kernel based programs to see if the back-to-back connection ++works properly. Just something simple like cat /etc/hosts >/dev/ttyS0 ++on one machine and cat /dev/ttyS0 on the other will tell you if you ++can send data from one machine to the other. There is no point in tearing ++out your hair in the kernel if the line doesn't work. ++ ++All of the real action takes place in the file ++/usr/src/linux/arch/i386/kernel/gdbstub.c. That is the code on the target ++machine that interacts with gdb on the development machine. In gdb you can ++turn on a debug switch with the following command: ++ ++ set remotedebug ++ ++This will print out the protocol messages that gdb is exchanging with ++the target machine. ++ ++Another place to look is /usr/src/linux/drivers/char/gdbserial.c ++That is the code that talks to the serial port on the target side. ++There might be a problem there. ++ ++If you are really desperate you can use printk debugging in the ++gdbstub code in the target kernel until you get it working. In particular, ++there is a global variable in /usr/src/linux/arch/i386/kernel/gdbstub.c ++named "remote_debug". Compile your kernel with this set to 1, rather ++than 0 and the debug stub will print out lots of stuff as it does ++what it does. ++ ++ ++Debugging Loadable Modules ++========================== ++ ++This technique comes courtesy of Edouard Parmelan ++ ++ ++When you run gdb, enter the command ++ ++source gdbinit-modules ++ ++This will read in a file of gdb macros that was installed in your ++kernel source directory with kgdb was installed. This file implements ++the following commands: ++ ++mod-list ++ Lists the loaded modules in the form ++ ++mod-print-symbols ++ Prints all the symbols in the indicated module. ++ ++mod-add-symbols ++ Loads the symbols from the object file and associates them ++ with the indicated module. ++ ++After you have loaded the module that you want to debug, use the command ++mod-list to find the of your module. Then use that ++address in the mod-add-symbols command to load your module's symbols. ++From that point onward you can debug your module as if it were a part ++of the kernel. ++ ++The file gdbinit-modules also contains a command named mod-add-lis as ++an example of how to construct a command of your own to load your ++favorite module. The idea is to "can" the pathname of the module ++in the command so you don't have to type so much. ++ ++Threads ++======= ++ ++Each process in a target machine is seen as a gdb thread. gdb thread related ++commands (info threads, thread n) can be used. ++ ++ia-32 hardware breakpoints ++========================== ++ ++gdb stub contains support for hardware breakpoints using debugging features ++of ia-32(x86) processors. These breakpoints do not need code modification. ++They use debugging registers. 4 hardware breakpoints are available in ia-32 ++processors. ++ ++Each hardware breakpoint can be of one of the following three types. ++1. Execution breakpoint - An Execution breakpoint is triggered when code at the ++ breakpoint address is executed. ++ ++ As limited number of hardware breakpoints are available, it is advisable ++ to use software breakpoints ( break command ) instead of execution ++ hardware breakpoints, unless modification of code is to be avoided. ++ ++2. Write breakpoint - A write breakpoint is triggered when memory location at the ++ breakpoint address is written. ++ ++ A write or can be placed for data of variable length. Length of a write ++ breakpoint indicates length of the datatype to be watched. Length is 1 ++ for 1 byte data , 2 for 2 byte data, 3 for 4 byte data. ++ ++3. Access breakpoint - An access breakpoint is triggered when memory location at ++ the breakpoint address is either read or written. ++ ++ Access breakpoints also have lengths similar to write breakpoints. ++ ++IO breakpoints in ia-32 are not supported. ++ ++Since gdb stub at present does not use the protocol used by gdb for hardware ++breakpoints, hardware breakpoints are accessed through gdb macros. gdb macros ++for hardware breakpoints are described below. ++ ++hwebrk - Places an execution breakpoint ++ hwebrk breakpointno address ++hwwbrk - Places a write breakpoint ++ hwwbrk breakpointno length address ++hwabrk - Places an access breakpoint ++ hwabrk breakpointno length address ++hwrmbrk - Removes a breakpoint ++ hwrmbrk breakpointno ++exinfo - Tells whether a software or hardware breakpoint has occured. ++ Prints number of the hardware breakpoint if a hardware breakpoint has ++ occured. ++ ++Arguments required by these commands are as follows ++breakpointno - 0 to 3 ++length - 1 to 3 ++address - Memory location in hex digits ( without 0x ) e.g c015e9bc ++ ++MP support ++========== ++ ++When a breakpoint occurs or user issues a break ( Ctrl + C ) to gdb client, ++all the processors are forced to enter the debugger. Current thread ++corresponds to the thread running on the processor where breakpoint occured. ++Threads running on other processor(s) appear similar to other non running ++threads in the 'info threads' output. ++ ++ia-32 hardware debugging registers on all processors are set to same values. ++Hence any hardware breakpoints may occur on any processor. ++ ++gdb troubleshooting ++=================== ++ ++1. gdb hangs ++Kill it. restart gdb. Connect to target machine. ++ ++2. gdb cannot connect to target machine (after killing a gdb and restarting ++another) ++If the target machine was not inside debugger when you killed gdb, gdb cannot ++connect because the target machine won't respond. ++In this case echo "Ctrl+C"(ascii 3) in the serial line. ++e.g. echo -e "\003" > /dev/ttyS1 ++This forces that target machine into debugger after which you can connect. ++ ++3. gdb cannot connect even after echoing Ctrl+C into serial line ++Try changing serial line settings min to 1 and time to 0 ++e.g. stty min 1 time 0 < /dev/ttyS1 ++Try echoing again ++ ++check serial line speed and set it to correct value if required ++e.g. stty ispeed 115200 ospeed 115200 < /dev/ttyS1 ++ ++Final Items ++=========== ++ ++I picked up this code from Dave Grothe and enhanced it. ++ ++If you make some really cool modification to this stuff, or if you ++fix a bug, please let me know. ++ ++Amit S. Kale ++ ++ ++(First kgdb by David Grothe ) ++ ++(modified by Tigran Aivazian ) ++ Putting gdbstub into the kernel config menu. ++ ++(modified by Scott Foehner ) ++ Hooks for entering gdbstub at boot time. ++ ++(modified by Amit S. Kale ) ++ Threads, ia-32 hw debugging, mp support, console support, ++ nmi watchdog handling. +--- /dev/null 2003-01-30 03:24:37.000000000 -0700 ++++ linux-2.4.18-p4smp-braam/drivers/char/gdbserial.c 2003-07-09 13:15:23.000000000 -0600 +@@ -0,0 +1,280 @@ ++/* ++ * Serial interface GDB stub ++ * ++ * Written (hacked together) by David Grothe (dave@gcom.com) ++ * ++ * Modified by Scott Foehner (sfoehner@engr.sgi.com) to allow connect ++ * on boot-up ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#undef PRNT /* define for debug printing */ ++ ++#define GDB_BUF_SIZE 512 /* power of 2, please */ ++ ++static char gdb_buf[GDB_BUF_SIZE] ; ++static int gdb_buf_in_inx ; ++static atomic_t gdb_buf_in_cnt ; ++static int gdb_buf_out_inx ; ++ ++extern void set_debug_traps(void) ; /* GDB routine */ ++extern struct serial_state * gdb_serial_setup(int ttyS, int baud); ++extern void shutdown_for_gdb(struct async_struct * info) ; ++ /* in serial.c */ ++ ++int gdb_irq; ++int gdb_port; ++int gdb_ttyS = 1; /* Default: ttyS1 */ ++int gdb_baud = 38400; ++int gdb_enter = 0; /* Default: do not do gdb_hook on boot */ ++int gdb_initialized = 0; ++ ++static int initialized = -1; ++ ++/* ++ * Get a byte from the hardware data buffer and return it ++ */ ++static int read_data_bfr(void) ++{ ++ if (inb(gdb_port + UART_LSR) & UART_LSR_DR) ++ return(inb(gdb_port + UART_RX)); ++ ++ return( -1 ) ; ++ ++} /* read_data_bfr */ ++ ++ ++/* ++ * Get a char if available, return -1 if nothing available. ++ * Empty the receive buffer first, then look at the interface hardware. ++ */ ++static int read_char(void) ++{ ++ if (atomic_read(&gdb_buf_in_cnt) != 0) /* intr routine has q'd chars */ ++ { ++ int chr ; ++ ++ chr = gdb_buf[gdb_buf_out_inx++] ; ++ gdb_buf_out_inx &= (GDB_BUF_SIZE - 1) ; ++ atomic_dec(&gdb_buf_in_cnt) ; ++ return(chr) ; ++ } ++ ++ return(read_data_bfr()) ; /* read from hardware */ ++ ++} /* read_char */ ++ ++/* ++ * Wait until the interface can accept a char, then write it. ++ */ ++static void write_char(int chr) ++{ ++ while ( !(inb(gdb_port + UART_LSR) & UART_LSR_THRE) ) ; ++ ++ outb(chr, gdb_port+UART_TX); ++ ++} /* write_char */ ++ ++/* ++ * This is the receiver interrupt routine for the GDB stub. ++ * It will receive a limited number of characters of input ++ * from the gdb host machine and save them up in a buffer. ++ * ++ * When the gdb stub routine getDebugChar() is called it ++ * draws characters out of the buffer until it is empty and ++ * then reads directly from the serial port. ++ * ++ * We do not attempt to write chars from the interrupt routine ++ * since the stubs do all of that via putDebugChar() which ++ * writes one byte after waiting for the interface to become ++ * ready. ++ * ++ * The debug stubs like to run with interrupts disabled since, ++ * after all, they run as a consequence of a breakpoint in ++ * the kernel. ++ * ++ * Perhaps someone who knows more about the tty driver than I ++ * care to learn can make this work for any low level serial ++ * driver. ++ */ ++static void gdb_interrupt(int irq, void *dev_id, struct pt_regs * regs) ++{ ++ int chr ; ++ int iir ; ++ ++ do ++ { ++ chr = read_data_bfr() ; ++ iir = inb(gdb_port + UART_IIR) ; ++#ifdef PRNT ++ printk("gdb_interrupt: chr=%02x '%c' after read iir=%02x\n", chr, ++ chr > ' ' && chr < 0x7F ? chr : ' ', iir) ; ++#endif ++ if (chr < 0) continue ; ++ ++ if (chr == 3) /* Ctrl-C means remote interrupt */ ++ { ++ breakpoint(); ++ continue ; ++ } ++ ++ if (atomic_read(&gdb_buf_in_cnt) >= GDB_BUF_SIZE) ++ { /* buffer overflow, clear it */ ++ gdb_buf_in_inx = 0 ; ++ atomic_set(&gdb_buf_in_cnt, 0) ; ++ gdb_buf_out_inx = 0 ; ++ break ; ++ } ++ ++ gdb_buf[gdb_buf_in_inx++] = chr ; ++ gdb_buf_in_inx &= (GDB_BUF_SIZE - 1) ; ++ atomic_inc(&gdb_buf_in_cnt) ; ++ } ++ while (iir & UART_IIR_RDI); ++ ++} /* gdb_interrupt */ ++ ++/* ++ * Just a NULL routine for testing. ++ */ ++void gdb_null(void) ++{ ++} /* gdb_null */ ++ ++ ++int gdb_hook(void) ++{ ++ int retval ; ++ struct serial_state *ser; ++ ++#ifdef CONFIG_SMP ++ if (smp_num_cpus > KGDB_MAX_NO_CPUS) { ++ printk("kgdb: too manu cpus. Cannot enable debugger with more than 8 cpus\n"); ++ return (-1); ++ } ++#endif ++ ++ /* ++ * Call first time just to get the ser ptr ++ */ ++ if((ser = gdb_serial_setup(gdb_ttyS, gdb_baud)) == 0) { ++ printk ("gdb_serial_setup() error"); ++ return(-1); ++ } ++ ++ gdb_port = ser->port; ++ gdb_irq = ser->irq; ++ ++ if (ser->info != NULL) ++ { ++ shutdown_for_gdb(ser->info) ; ++ /* ++ * Call second time to do the setup now that we have ++ * shut down the previous user of the interface. ++ */ ++ gdb_serial_setup(gdb_ttyS, gdb_baud) ; ++ } ++ ++ retval = request_irq(gdb_irq, ++ gdb_interrupt, ++ SA_INTERRUPT, ++ "GDB-stub", NULL); ++ if (retval == 0) ++ initialized = 1; ++ else ++ { ++ initialized = 0; ++ printk("gdb_hook: request_irq(irq=%d) failed: %d\n", gdb_irq, retval); ++ } ++ ++ /* ++ * Call GDB routine to setup the exception vectors for the debugger ++ */ ++ set_debug_traps() ; ++ ++ /* ++ * Call the breakpoint() routine in GDB to start the debugging ++ * session. ++ */ ++ printk("Waiting for connection from remote gdb... ") ; ++ breakpoint() ; ++ gdb_null() ; ++ ++ printk("Connected.\n"); ++ ++ gdb_initialized = 1; ++ return(0) ; ++ ++} /* gdb_hook_interrupt2 */ ++ ++/* ++ * getDebugChar ++ * ++ * This is a GDB stub routine. It waits for a character from the ++ * serial interface and then returns it. If there is no serial ++ * interface connection then it returns a bogus value which will ++ * almost certainly cause the system to hang. ++ */ ++int getDebugChar(void) ++{ ++ volatile int chr ; ++ ++#ifdef PRNT ++ printk("getDebugChar: ") ; ++#endif ++ ++ while ( (chr = read_char()) < 0 ) ; ++ ++ ++#ifdef PRNT ++ printk("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ') ; ++#endif ++ return(chr) ; ++ ++} /* getDebugChar */ ++ ++/* ++ * putDebugChar ++ * ++ * This is a GDB stub routine. It waits until the interface is ready ++ * to transmit a char and then sends it. If there is no serial ++ * interface connection then it simply returns to its caller, having ++ * pretended to send the char. ++ */ ++void putDebugChar(int chr) ++{ ++#ifdef PRNT ++ printk("putDebugChar: chr=%02x '%c'\n", chr, ++ chr > ' ' && chr < 0x7F ? chr : ' ') ; ++#endif ++ ++ write_char(chr) ; /* this routine will wait */ ++ ++} /* putDebugChar */ ++ +--- linux-2.4.18-p4smp/drivers/char/Makefile~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/drivers/char/Makefile 2003-07-09 13:15:23.000000000 -0600 +@@ -142,6 +142,7 @@ ifeq ($(CONFIG_DUMMY_KEYB),y) + KEYBD = dummy_keyb.o + endif + ++obj-$(CONFIG_X86_REMOTE_DEBUG) += gdbserial.o + obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o + obj-$(CONFIG_SERIAL) += $(SERIAL) + obj-$(CONFIG_SERIAL_HCDP) += hcdp_serial.o +--- linux-2.4.18-p4smp/drivers/char/serial.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/drivers/char/serial.c 2003-07-09 13:15:23.000000000 -0600 +@@ -4,6 +4,7 @@ + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o ++ * Copyright (C) 2000 VERITAS Software Corporation. + * + * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now + * much more extensible to support other serial cards based on the +@@ -34,6 +35,10 @@ + * 4/98: Added changes to support the ARM architecture proposed by + * Russell King + * ++ * 3/99: Added TIOCGDB for remote debugging with gdb if compiled with ++ * CONFIG_X86_REMOTE_DEBUG ++ * Tigran Aivazian ++ * + * 5/99: Updated to include support for the XR16C850 and ST16C654 + * uarts. Stuart MacDonald + * +@@ -66,6 +71,8 @@ + * to waiting processes + * Sapan Bhatia + * ++ * 10/00: added console support for kgdb. Amit Kale ++ * + */ + + static char *serial_version = "5.05c"; +@@ -215,7 +222,7 @@ static char *serial_revdate = "2001-07-0 + #include + #endif + #include +-#ifdef CONFIG_SERIAL_CONSOLE ++#if defined(CONFIG_SERIAL_CONSOLE) || defined (CONFIG_GDB_CONSOLE) + #include + #endif + #ifdef ENABLE_SERIAL_PCI +@@ -235,6 +242,9 @@ static char *serial_revdate = "2001-07-0 + #if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ + #include "serial_compat.h" + #endif ++#ifdef CONFIG_X86_REMOTE_DEBUG ++#include ++#endif + + #include + #include +@@ -1586,6 +1596,12 @@ static void shutdown(struct async_struct + + spin_unlock_irqrestore( &info->irq_spinlock, flags); + } ++#ifdef CONFIG_X86_REMOTE_DEBUG ++void shutdown_for_gdb(struct async_struct * info) ++{ ++ shutdown(info) ; ++} ++#endif + + #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ + static int baud_table[] = { +@@ -2698,6 +2714,12 @@ static int rs_ioctl(struct tty_struct *t + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + return 0; + ++#ifdef CONFIG_X86_REMOTE_DEBUG ++ case TIOCGDB: ++ gdb_ttyS = MINOR(tty->device) & 0x03F ; ++ gdb_baud = tty_get_baud_rate(tty) ; ++ return gdb_hook(); ++#endif + default: + return -ENOIOCTLCMD; + } +@@ -6043,6 +6065,129 @@ void __init serial_console_init(void) + #endif + + /* ++ * ------------------------------------------------------------ ++ * Serial GDB driver (most in gdbserial.c) ++ * ------------------------------------------------------------ ++ */ ++ ++#ifdef CONFIG_X86_REMOTE_DEBUG ++#ifdef CONFIG_GDB_CONSOLE ++static struct console gdbcons = { ++ name: "gdb", ++ write: gdb_console_write, ++ flags: CON_PRINTBUFFER | CON_ENABLED, ++ index: -1, ++}; ++#endif ++ ++ ++/* ++ * Takes: ++ * ttyS - integer specifying which serial port to use for debugging ++ * baud - baud rate of specified serial port ++ * Returns: ++ * port for use by the gdb serial driver ++ */ ++struct serial_state * ++gdb_serial_setup(int ttyS, int baud) ++{ ++ struct serial_state *ser; ++ unsigned cval; ++ int bits = 8; ++ int parity = 'n'; ++ int cflag = CREAD | HUPCL | CLOCAL; ++ int quot = 0; ++ ++ /* ++ * Now construct a cflag setting. ++ */ ++ switch(baud) { ++ case 1200: ++ cflag |= B1200; ++ break; ++ case 2400: ++ cflag |= B2400; ++ break; ++ case 4800: ++ cflag |= B4800; ++ break; ++ case 19200: ++ cflag |= B19200; ++ break; ++ case 38400: ++ cflag |= B38400; ++ break; ++ case 57600: ++ cflag |= B57600; ++ break; ++ case 115200: ++ cflag |= B115200; ++ break; ++ case 9600: ++ default: ++ cflag |= B9600; ++ break; ++ } ++ switch(bits) { ++ case 7: ++ cflag |= CS7; ++ break; ++ default: ++ case 8: ++ cflag |= CS8; ++ break; ++ } ++ switch(parity) { ++ case 'o': case 'O': ++ cflag |= PARODD; ++ break; ++ case 'e': case 'E': ++ cflag |= PARENB; ++ break; ++ } ++ ++ /* ++ * Divisor, bytesize and parity ++ */ ++ ++ ser = rs_table + ttyS; ++ ser->flags &= ~ASYNC_BOOT_AUTOCONF; ++ quot = ser->baud_base / baud; ++ cval = cflag & (CSIZE | CSTOPB); ++ cval >>= 4; ++ if (cflag & PARENB) ++ cval |= UART_LCR_PARITY; ++ if (!(cflag & PARODD)) ++ cval |= UART_LCR_EPAR; ++ ++ /* ++ * Disable UART interrupts, set DTR and RTS high ++ * and set speed. ++ */ ++ cval = 0x3; ++ outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ ++ outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ ++ outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ ++ outb(cval, ser->port + UART_LCR); /* reset DLAB */ ++ outb(UART_IER_RDI, ser->port + UART_IER); /* turn on interrupts*/ ++ outb(UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); ++ ++ /* ++ * If we read 0xff from the LSR, there is no UART here. ++ */ ++ if (inb(ser->port + UART_LSR) == 0xff) ++ return 0; ++ return ser; ++} ++#ifdef CONFIG_GDB_CONSOLE ++void __init gdb_console_init(void) ++{ ++ register_console(&gdbcons); ++} ++#endif ++#endif /* CONFIG_X86_REMOTE_DEBUG */ ++ ++/* + Local variables: + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" + End: +--- linux-2.4.18-p4smp/drivers/char/tty_io.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/drivers/char/tty_io.c 2003-07-09 13:15:23.000000000 -0600 +@@ -90,6 +90,9 @@ + #include + #include + #include ++#ifdef CONFIG_GDB_CONSOLE ++#include ++#endif + + #include + #include +@@ -2233,6 +2236,9 @@ void __init console_init(void) + #ifdef CONFIG_AU1000_SERIAL_CONSOLE + au1000_serial_console_init(); + #endif ++#ifdef CONFIG_GDB_CONSOLE ++ gdb_console_init(); ++#endif + #ifdef CONFIG_SERIAL_CONSOLE + #if (defined(CONFIG_8xx) || defined(CONFIG_8260)) + console_8xx_init(); +--- linux-2.4.18-p4smp/include/asm-i386/ioctls.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/include/asm-i386/ioctls.h 2003-07-09 13:15:23.000000000 -0600 +@@ -68,6 +68,7 @@ + #define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ + #define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ + #define FIOQSIZE 0x5460 ++#define TIOCGDB 0x547F /* enable GDB stub mode on this tty */ + + /* Used for packet mode */ + #define TIOCPKT_DATA 0 +--- linux-2.4.18-p4smp/include/asm-i386/page.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/include/asm-i386/page.h 2003-07-09 13:15:23.000000000 -0600 +@@ -95,6 +95,9 @@ typedef struct { unsigned long pgprot; } + * undefined" opcode for parsing in the trap handler. + */ + ++#ifdef CONFIG_X86_REMOTE_DEBUG ++#define BUG() KGDB_ASSERT("BUG", 0) ++#else + #if 1 /* Set to zero for a slightly smaller kernel */ + #define BUG() \ + __asm__ __volatile__( "ud2\n" \ +@@ -104,6 +107,7 @@ typedef struct { unsigned long pgprot; } + #else + #define BUG() __asm__ __volatile__("ud2\n") + #endif ++#endif + + #define PAGE_BUG(page) do { \ + BUG(); \ +--- linux-2.4.18-p4smp/include/asm-i386/processor.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/include/asm-i386/processor.h 2003-07-09 13:20:27.000000000 -0600 +@@ -390,6 +390,9 @@ struct thread_struct { + /* IO permissions */ + int ioperm; + unsigned long io_bitmap[IO_BITMAP_SIZE+1]; ++#ifdef CONFIG_X86_REMOTE_DEBUG ++ struct pt_regs *kgdbregs; ++#endif + /* performance counters */ + struct vperfctr *perfctr; + }; +--- linux-2.4.18-p4smp/include/linux/dcache.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/include/linux/dcache.h 2003-07-09 13:15:23.000000000 -0600 +@@ -4,6 +4,7 @@ + #ifdef __KERNEL__ + + #include ++#include + #include + + /* +--- /dev/null 2003-01-30 03:24:37.000000000 -0700 ++++ linux-2.4.18-p4smp-braam/include/linux/gdb.h 2003-07-09 13:15:23.000000000 -0600 +@@ -0,0 +1,63 @@ ++#ifndef _GDB_H_ ++#define _GDB_H_ ++ ++/* ++ * Copyright (C) 2001 Amit S. Kale ++ */ ++ ++/* gdb locks */ ++#define KGDB_MAX_NO_CPUS 8 ++ ++extern int gdb_enter; /* 1 = enter debugger on boot */ ++extern int gdb_ttyS; ++extern int gdb_baud; ++extern int gdb_initialized; ++ ++extern int gdb_hook(void); ++extern void breakpoint(void); ++ ++typedef int gdb_debug_hook(int trapno, ++ int signo, ++ int err_code, ++ struct pt_regs *regs); ++extern gdb_debug_hook *linux_debug_hook; ++ ++extern volatile unsigned kgdb_lock; ++ ++extern volatile int kgdb_memerr_expected; ++ ++extern volatile unsigned kgdb_step; ++ ++struct console; ++void gdb_console_write(struct console *co, const char *s, ++ unsigned count); ++void gdb_console_init(void); ++ ++extern volatile int procindebug[KGDB_MAX_NO_CPUS]; ++ ++void gdb_wait(struct pt_regs *regs); ++ ++#define KGDB_ASSERT(message, condition) do { \ ++ if (!(condition)) { \ ++ printk("kgdb assertion failed: %s\n", message); \ ++ asm ("int $0x3"); \ ++ } \ ++} while (0) ++ ++#define KA_VALID_ERRNO(errno) ((errno) > 0 && (errno) <= EMEDIUMTYPE) ++ ++#define KA_VALID_PTR_ERR(ptr) KA_VALID_ERRNO(-PTR_ERR(ptr)) ++ ++#define KA_VALID_KPTR(ptr) (!(ptr) || \ ++ ((void *)(ptr) >= (void *)PAGE_OFFSET && \ ++ (void *)(ptr) < ERR_PTR(-EMEDIUMTYPE))) ++ ++#define KA_VALID_PTRORERR(errptr) (KA_VALID_KPTR(errptr) || KA_VALID_PTR_ERR(errptr)) ++ ++#ifndef CONFIG_SMP ++#define KA_HELD_GKL() 1 ++#else ++#define KA_HELD_GKL() (current->lock_depth >= 0) ++#endif ++ ++#endif /* _GDB_H_ */ +--- linux-2.4.18-p4smp/include/linux/sched.h~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/include/linux/sched.h 2003-07-09 13:15:23.000000000 -0600 +@@ -159,7 +159,9 @@ extern int set_user(uid_t new_ruid, int + + #define MAX_SCHEDULE_TIMEOUT LONG_MAX + extern signed long FASTCALL(schedule_timeout(signed long timeout)); +-asmlinkage void schedule(void); ++asmlinkage void do_schedule(void); ++asmlinkage void kern_schedule(void); ++asmlinkage void kern_do_schedule(struct pt_regs); + + extern int schedule_task(struct tq_struct *task); + extern void flush_scheduled_tasks(void); +@@ -1025,6 +1027,15 @@ static inline int need_resched(void) + return unlikely(current->need_resched); + } + ++static inline void schedule(void) ++{ ++#ifdef CONFIG_KGDB_THREAD ++ kern_schedule(); ++#else ++ do_schedule(); ++#endif ++} ++ + #endif /* __KERNEL__ */ + + #endif +--- linux-2.4.18-p4smp/init/main.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/init/main.c 2003-07-09 13:15:23.000000000 -0600 +@@ -57,6 +57,10 @@ + #include + #endif + ++#ifdef CONFIG_X86_REMOTE_DEBUG ++#include ++#endif ++ + #ifdef CONFIG_ISAPNP + #include + #endif +@@ -476,6 +480,11 @@ asmlinkage void __init start_kernel(void + * make syscalls (and thus be locked). + */ + smp_init(); ++#ifdef CONFIG_X86_REMOTE_DEBUG ++ if (gdb_enter) { ++ gdb_hook(); /* right at boot time */ ++ } ++#endif + + /* Do the rest non-__init'ed, we're now alive */ + rest_init(); +--- linux-2.4.18-p4smp/kernel/ksyms.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/kernel/ksyms.c 2003-07-09 13:15:23.000000000 -0600 +@@ -478,7 +478,10 @@ EXPORT_SYMBOL(sleep_on); + EXPORT_SYMBOL(sleep_on_timeout); + EXPORT_SYMBOL(interruptible_sleep_on); + EXPORT_SYMBOL(interruptible_sleep_on_timeout); +-EXPORT_SYMBOL(schedule); ++EXPORT_SYMBOL(do_schedule); ++#ifdef CONFIG_KGDB_THREAD ++EXPORT_SYMBOL(kern_schedule); ++#endif + EXPORT_SYMBOL(schedule_timeout); + EXPORT_SYMBOL(sys_sched_yield); + EXPORT_SYMBOL(set_user_nice); +--- linux-2.4.18-p4smp/kernel/sched.c~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/kernel/sched.c 2003-07-09 13:15:23.000000000 -0600 +@@ -797,7 +797,7 @@ void scheduling_functions_start_here(voi + /* + * 'schedule()' is the main scheduler function. + */ +-asmlinkage void schedule(void) ++asmlinkage void do_schedule(void) + { + task_t *prev, *next; + runqueue_t *rq; +@@ -879,6 +879,22 @@ switch_tasks: + goto need_resched; + } + ++asmlinkage void user_schedule(void) ++{ ++#ifdef CONFIG_KGDB_THREAD ++ current->thread.kgdbregs = NULL; ++#endif ++ do_schedule(); ++} ++ ++#ifdef CONFIG_KGDB_THREAD ++asmlinkage void kern_do_schedule(struct pt_regs regs) ++{ ++ current->thread.kgdbregs = ®s; ++ do_schedule(); ++} ++#endif ++ + /* + * The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just + * wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve +--- linux-2.4.18-p4smp/Makefile~linux-2.4.18-14-kgdb-1.6 2003-07-09 13:14:53.000000000 -0600 ++++ linux-2.4.18-p4smp-braam/Makefile 2003-07-09 13:15:23.000000000 -0600 +@@ -101,9 +101,13 @@ CPPFLAGS := -D__KERNEL__ -I$(HPATH) + + CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ + -fno-strict-aliasing -fno-common ++ifeq ($(CONFIG_X86_REMOTE_DEBUG),y) ++CFLAGS += -g ++else + ifndef CONFIG_FRAME_POINTER + CFLAGS += -fomit-frame-pointer + endif ++endif + AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) + + ifeq ($(CONFIG_MCL_COREDUMP),y) + +_ diff --git a/lustre/kernel_patches/patches/vfs_intent-2.4.20-hp.patch b/lustre/kernel_patches/patches/vfs_intent-2.4.20-hp.patch new file mode 100644 index 0000000..55046ea --- /dev/null +++ b/lustre/kernel_patches/patches/vfs_intent-2.4.20-hp.patch @@ -0,0 +1,1793 @@ + fs/dcache.c | 19 ++ + fs/exec.c | 15 +- + fs/namei.c | 321 ++++++++++++++++++++++++++++++++++++++-------- + fs/namespace.c | 28 +++- + fs/open.c | 123 +++++++++++++++-- + fs/proc/base.c | 3 + fs/stat.c | 50 ++++--- + include/linux/dcache.h | 54 +++++++ + include/linux/fs.h | 30 ++++ + include/linux/fs_struct.h | 4 + kernel/exit.c | 3 + kernel/fork.c | 3 + kernel/ksyms.c | 1 + 13 files changed, 552 insertions(+), 102 deletions(-) + +--- linux/fs/exec.c~vfs_intent-2.4.20-hp Sat Jul 12 14:04:49 2003 ++++ linux-mmonroe/fs/exec.c Sat Jul 12 14:05:24 2003 +@@ -116,8 +116,9 @@ asmlinkage long sys_uselib(const char * + struct file * file; + struct nameidata nd; + int error; ++ struct lookup_intent it = { .it_op = IT_OPEN, .it_flags = O_RDONLY }; + +- error = user_path_walk(library, &nd); ++ error = user_path_walk_it(library, &nd, &it); + if (error) + goto out; + +@@ -129,7 +130,8 @@ asmlinkage long sys_uselib(const char * + if (error) + goto exit; + +- file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ file = dentry_open_it(nd.dentry, nd.mnt, O_RDONLY, &it); ++ intent_release(&it); + error = PTR_ERR(file); + if (IS_ERR(file)) + goto out; +@@ -372,8 +374,9 @@ struct file *open_exec(const char *name) + struct inode *inode; + struct file *file; + int err = 0; ++ struct lookup_intent it = { .it_op = IT_OPEN, .it_flags = O_RDONLY }; + +- err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd); ++ err = path_lookup_it(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd, &it); + file = ERR_PTR(err); + if (!err) { + inode = nd.dentry->d_inode; +@@ -385,7 +388,8 @@ struct file *open_exec(const char *name) + err = -EACCES; + file = ERR_PTR(err); + if (!err) { +- file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ file = dentry_open_it(nd.dentry, nd.mnt, O_RDONLY, &it); ++ intent_release(&it); + if (!IS_ERR(file)) { + err = deny_write_access(file); + if (err) { +@@ -397,6 +401,7 @@ out: + return file; + } + } ++ intent_release(&it); + path_release(&nd); + } + goto out; +@@ -1128,7 +1133,7 @@ int do_coredump(long signr, struct pt_re + goto close_fail; + if (!file->f_op->write) + goto close_fail; +- if (do_truncate(file->f_dentry, 0) != 0) ++ if (do_truncate(file->f_dentry, 0, 0) != 0) + goto close_fail; + + retval = binfmt->core_dump(signr, regs, file); +--- linux/fs/dcache.c~vfs_intent-2.4.20-hp Thu Nov 28 15:53:15 2002 ++++ linux-mmonroe/fs/dcache.c Sat Jul 12 14:05:24 2003 +@@ -181,6 +181,13 @@ int d_invalidate(struct dentry * dentry) + spin_unlock(&dcache_lock); + return 0; + } ++ ++ /* network invalidation by Lustre */ ++ if (dentry->d_flags & DCACHE_LUSTRE_INVALID) { ++ spin_unlock(&dcache_lock); ++ return 0; ++ } ++ + /* + * Check whether to do a partial shrink_dcache + * to get rid of unused child entries. +@@ -830,13 +837,19 @@ void d_delete(struct dentry * dentry) + * Adds a dentry to the hash according to its name. + */ + +-void d_rehash(struct dentry * entry) ++void __d_rehash(struct dentry * entry, int lock) + { + struct list_head *list = d_hash(entry->d_parent, entry->d_name.hash); + if (!list_empty(&entry->d_hash)) BUG(); +- spin_lock(&dcache_lock); ++ if (lock) spin_lock(&dcache_lock); + list_add(&entry->d_hash, list); +- spin_unlock(&dcache_lock); ++ if (lock) spin_unlock(&dcache_lock); ++} ++EXPORT_SYMBOL(__d_rehash); ++ ++void d_rehash(struct dentry * entry) ++{ ++ __d_rehash(entry, 1); + } + + #define do_switch(x,y) do { \ +--- linux/fs/namespace.c~vfs_intent-2.4.20-hp Thu Nov 28 15:53:15 2002 ++++ linux-mmonroe/fs/namespace.c Sat Jul 12 14:05:24 2003 +@@ -99,6 +99,7 @@ static void detach_mnt(struct vfsmount * + { + old_nd->dentry = mnt->mnt_mountpoint; + old_nd->mnt = mnt->mnt_parent; ++ UNPIN(old_nd->dentry, old_nd->mnt, 1); + mnt->mnt_parent = mnt; + mnt->mnt_mountpoint = mnt->mnt_root; + list_del_init(&mnt->mnt_child); +@@ -110,6 +111,7 @@ static void attach_mnt(struct vfsmount * + { + mnt->mnt_parent = mntget(nd->mnt); + mnt->mnt_mountpoint = dget(nd->dentry); ++ PIN(nd->dentry, nd->mnt, 1); + list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry)); + list_add(&mnt->mnt_child, &nd->mnt->mnt_mounts); + nd->dentry->d_mounted++; +@@ -485,14 +487,17 @@ static int do_loopback(struct nameidata + { + struct nameidata old_nd; + struct vfsmount *mnt = NULL; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + int err = mount_is_safe(nd); + if (err) + return err; + if (!old_name || !*old_name) + return -EINVAL; +- err = path_lookup(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd); +- if (err) ++ err = path_lookup_it(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd, &it); ++ if (err) { ++ intent_release(&it); + return err; ++ } + + down_write(¤t->namespace->sem); + err = -EINVAL; +@@ -515,6 +520,7 @@ static int do_loopback(struct nameidata + } + + up_write(¤t->namespace->sem); ++ intent_release(&it); + path_release(&old_nd); + return err; + } +@@ -698,6 +704,7 @@ long do_mount(char * dev_name, char * di + unsigned long flags, void *data_page) + { + struct nameidata nd; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + int retval = 0; + int mnt_flags = 0; + +@@ -722,10 +729,11 @@ long do_mount(char * dev_name, char * di + flags &= ~(MS_NOSUID|MS_NOEXEC|MS_NODEV); + + /* ... and get the mountpoint */ +- retval = path_lookup(dir_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd); +- if (retval) ++ retval = path_lookup_it(dir_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd, &it); ++ if (retval) { ++ intent_release(&it); + return retval; +- ++ } + if (flags & MS_REMOUNT) + retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags, + data_page); +@@ -736,6 +744,8 @@ long do_mount(char * dev_name, char * di + else + retval = do_add_mount(&nd, type_page, flags, mnt_flags, + dev_name, data_page); ++ ++ intent_release(&it); + path_release(&nd); + return retval; + } +@@ -901,6 +911,8 @@ asmlinkage long sys_pivot_root(const cha + { + struct vfsmount *tmp; + struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd; ++ struct lookup_intent new_it = { .it_op = IT_GETATTR }; ++ struct lookup_intent old_it = { .it_op = IT_GETATTR }; + int error; + + if (!capable(CAP_SYS_ADMIN)) +@@ -908,14 +920,14 @@ asmlinkage long sys_pivot_root(const cha + + lock_kernel(); + +- error = __user_walk(new_root, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd); ++ error = __user_walk_it(new_root, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd, &new_it); + if (error) + goto out0; + error = -EINVAL; + if (!check_mnt(new_nd.mnt)) + goto out1; + +- error = __user_walk(put_old, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd); ++ error = __user_walk_it(put_old, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd, &old_it); + if (error) + goto out1; + +@@ -970,8 +982,10 @@ out2: + up(&old_nd.dentry->d_inode->i_zombie); + up_write(¤t->namespace->sem); + path_release(&user_nd); ++ intent_release(&old_it); + path_release(&old_nd); + out1: ++ intent_release(&new_it); + path_release(&new_nd); + out0: + unlock_kernel(); +--- linux/fs/namei.c~vfs_intent-2.4.20-hp Sat Jul 12 14:04:48 2003 ++++ linux-mmonroe/fs/namei.c Sat Jul 12 14:08:01 2003 +@@ -94,6 +94,13 @@ + * XEmacs seems to be relying on it... + */ + ++void intent_release(struct lookup_intent *it) ++{ ++ if (it && it->it_op_release) ++ it->it_op_release(it); ++ ++} ++ + /* In order to reduce some races, while at the same time doing additional + * checking and hopefully speeding things up, we copy filenames to the + * kernel data space before using them.. +@@ -260,10 +267,19 @@ void path_release(struct nameidata *nd) + * Internal lookup() using the new generic dcache. + * SMP-safe + */ +-static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags) ++static struct dentry *cached_lookup(struct dentry *parent, struct qstr *name, ++ int flags, struct lookup_intent *it) + { + struct dentry * dentry = d_lookup(parent, name); + ++ if (dentry && dentry->d_op && dentry->d_op->d_revalidate_it) { ++ if (!dentry->d_op->d_revalidate_it(dentry, flags, it) && ++ !d_invalidate(dentry)) { ++ dput(dentry); ++ dentry = NULL; ++ } ++ return dentry; ++ } else + if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { + if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) { + dput(dentry); +@@ -281,11 +297,14 @@ static struct dentry * cached_lookup(str + * make sure that nobody added the entry to the dcache in the meantime.. + * SMP-safe + */ +-static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, int flags) ++static struct dentry *real_lookup(struct dentry *parent, struct qstr *name, ++ int flags, struct lookup_intent *it) + { + struct dentry * result; + struct inode *dir = parent->d_inode; + ++again: ++ + down(&dir->i_sem); + /* + * First re-do the cached lookup just in case it was created +@@ -300,6 +319,9 @@ static struct dentry * real_lookup(struc + result = ERR_PTR(-ENOMEM); + if (dentry) { + lock_kernel(); ++ if (dir->i_op->lookup_it) ++ result = dir->i_op->lookup_it(dir, dentry, it, flags); ++ else + result = dir->i_op->lookup(dir, dentry); + unlock_kernel(); + if (result) +@@ -321,6 +343,12 @@ static struct dentry * real_lookup(struc + dput(result); + result = ERR_PTR(-ENOENT); + } ++ } else if (result->d_op && result->d_op->d_revalidate_it) { ++ if (!result->d_op->d_revalidate_it(result, flags, it) && ++ !d_invalidate(result)) { ++ dput(result); ++ goto again; ++ } + } + return result; + } +@@ -332,7 +360,8 @@ static struct dentry * real_lookup(struc + * Without that kind of total limit, nasty chains of consecutive + * symlinks can cause almost arbitrarily long lookups. + */ +-static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd) ++static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd, ++ struct lookup_intent *it) + { + int err; + if (current->link_count >= 5) +@@ -346,10 +375,18 @@ static inline int do_follow_link(struct + current->link_count++; + current->total_link_count++; + UPDATE_ATIME(dentry->d_inode); ++ nd->it = it; + err = dentry->d_inode->i_op->follow_link(dentry, nd); ++ if (!err && it != NULL && !(it->it_int_flags & IT_FL_FOLLOWED)) { ++ /* vfs_follow_link was never called */ ++ intent_release(it); ++ path_release(nd); ++ err = -ENOLINK; ++ } + current->link_count--; + return err; + loop: ++ intent_release(it); + path_release(nd); + return -ELOOP; + } +@@ -379,15 +416,26 @@ int follow_up(struct vfsmount **mnt, str + return __follow_up(mnt, dentry); + } + +-static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry) ++static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry, ++ struct lookup_intent *it) + { + struct vfsmount *mounted; + + spin_lock(&dcache_lock); + mounted = lookup_mnt(*mnt, *dentry); + if (mounted) { ++ int opc = 0, mode = 0; + *mnt = mntget(mounted); + spin_unlock(&dcache_lock); ++ if (it) { ++ opc = it->it_op; ++ mode = it->it_mode; ++ } ++ intent_release(it); ++ if (it) { ++ it->it_op = opc; ++ it->it_mode = mode; ++ } + dput(*dentry); + mntput(mounted->mnt_parent); + *dentry = dget(mounted->mnt_root); +@@ -399,7 +447,7 @@ static inline int __follow_down(struct v + + int follow_down(struct vfsmount **mnt, struct dentry **dentry) + { +- return __follow_down(mnt,dentry); ++ return __follow_down(mnt,dentry,NULL); + } + + static inline void follow_dotdot(struct nameidata *nd) +@@ -435,7 +483,7 @@ static inline void follow_dotdot(struct + mntput(nd->mnt); + nd->mnt = parent; + } +- while (d_mountpoint(nd->dentry) && __follow_down(&nd->mnt, &nd->dentry)) ++ while (d_mountpoint(nd->dentry) && __follow_down(&nd->mnt, &nd->dentry, NULL)) + ; + } + +@@ -447,7 +495,8 @@ static inline void follow_dotdot(struct + * + * We expect 'base' to be positive and a directory. + */ +-int link_path_walk(const char * name, struct nameidata *nd) ++int link_path_walk_it(const char *name, struct nameidata *nd, ++ struct lookup_intent *it) + { + struct dentry *dentry; + struct inode *inode; +@@ -520,15 +569,15 @@ int link_path_walk(const char * name, st + break; + } + /* This does the actual lookups.. */ +- dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE); ++ dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE, NULL); + if (!dentry) { +- dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE); ++ dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE, NULL); + err = PTR_ERR(dentry); + if (IS_ERR(dentry)) + break; + } + /* Check mountpoints.. */ +- while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry)) ++ while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry, NULL)) + ; + + err = -ENOENT; +@@ -540,7 +589,7 @@ int link_path_walk(const char * name, st + goto out_dput; + + if (inode->i_op->follow_link) { +- err = do_follow_link(dentry, nd); ++ err = do_follow_link(dentry, nd, NULL); + dput(dentry); + if (err) + goto return_err; +@@ -556,7 +605,7 @@ int link_path_walk(const char * name, st + nd->dentry = dentry; + } + err = -ENOTDIR; +- if (!inode->i_op->lookup) ++ if (!inode->i_op->lookup && !inode->i_op->lookup_it) + break; + continue; + /* here ends the main loop */ +@@ -583,19 +632,19 @@ last_component: + if (err < 0) + break; + } +- dentry = cached_lookup(nd->dentry, &this, 0); ++ dentry = cached_lookup(nd->dentry, &this, 0, it); + if (!dentry) { +- dentry = real_lookup(nd->dentry, &this, 0); ++ dentry = real_lookup(nd->dentry, &this, 0, it); + err = PTR_ERR(dentry); + if (IS_ERR(dentry)) + break; + } +- while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry)) ++ while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry, it)) + ; + inode = dentry->d_inode; + if ((lookup_flags & LOOKUP_FOLLOW) + && inode && inode->i_op && inode->i_op->follow_link) { +- err = do_follow_link(dentry, nd); ++ err = do_follow_link(dentry, nd, it); + dput(dentry); + if (err) + goto return_err; +@@ -609,7 +658,8 @@ last_component: + goto no_inode; + if (lookup_flags & LOOKUP_DIRECTORY) { + err = -ENOTDIR; +- if (!inode->i_op || !inode->i_op->lookup) ++ if (!inode->i_op || ++ (!inode->i_op->lookup && !inode->i_op->lookup_it)) + break; + } + goto return_base; +@@ -633,6 +683,23 @@ return_reval: + * Check the cached dentry for staleness. + */ + dentry = nd->dentry; ++ revalidate_again: ++ if (dentry && dentry->d_op && dentry->d_op->d_revalidate_it) { ++ err = -ESTALE; ++ if (!dentry->d_op->d_revalidate_it(dentry, 0, it)) { ++ struct dentry *new; ++ err = permission(dentry->d_parent->d_inode, ++ MAY_EXEC); ++ if (err) ++ break; ++ new = real_lookup(dentry->d_parent, ++ &dentry->d_name, 0, NULL); ++ d_invalidate(dentry); ++ dput(dentry); ++ dentry = new; ++ goto revalidate_again; ++ } ++ } else + if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { + err = -ESTALE; + if (!dentry->d_op->d_revalidate(dentry, 0)) { +@@ -646,15 +713,28 @@ out_dput: + dput(dentry); + break; + } ++ if (err) ++ intent_release(it); + path_release(nd); + return_err: + return err; + } + ++int link_path_walk(const char * name, struct nameidata *nd) ++{ ++ return link_path_walk_it(name, nd, NULL); ++} ++ ++int path_walk_it(const char * name, struct nameidata *nd, struct lookup_intent *it) ++{ ++ current->total_link_count = 0; ++ return link_path_walk_it(name, nd, it); ++} ++ + int path_walk(const char * name, struct nameidata *nd) + { + current->total_link_count = 0; +- return link_path_walk(name, nd); ++ return link_path_walk_it(name, nd, NULL); + } + + /* SMP-safe */ +@@ -739,6 +819,17 @@ walk_init_root(const char *name, struct + } + + /* SMP-safe */ ++int path_lookup_it(const char *path, unsigned flags, struct nameidata *nd, ++ struct lookup_intent *it) ++{ ++ int error = 0; ++ if (path_init(path, flags, nd)) ++ error = path_walk_it(path, nd, it); ++ return error; ++} ++ ++ ++/* SMP-safe */ + int path_lookup(const char *path, unsigned flags, struct nameidata *nd) + { + int error = 0; +@@ -753,6 +844,7 @@ int path_init(const char *name, unsigned + { + nd->last_type = LAST_ROOT; /* if there are only slashes... */ + nd->flags = flags; ++ nd->it = NULL; + if (*name=='/') + return walk_init_root(name,nd); + read_lock(¤t->fs->lock); +@@ -767,7 +859,8 @@ int path_init(const char *name, unsigned + * needs parent already locked. Doesn't follow mounts. + * SMP-safe. + */ +-struct dentry * lookup_hash(struct qstr *name, struct dentry * base) ++struct dentry * lookup_hash_it(struct qstr *name, struct dentry * base, ++ struct lookup_intent *it) + { + struct dentry * dentry; + struct inode *inode; +@@ -790,13 +883,16 @@ struct dentry * lookup_hash(struct qstr + goto out; + } + +- dentry = cached_lookup(base, name, 0); ++ dentry = cached_lookup(base, name, 0, it); + if (!dentry) { + struct dentry *new = d_alloc(base, name); + dentry = ERR_PTR(-ENOMEM); + if (!new) + goto out; + lock_kernel(); ++ if (inode->i_op->lookup_it) ++ dentry = inode->i_op->lookup_it(inode, new, it, 0); ++ else + dentry = inode->i_op->lookup(inode, new); + unlock_kernel(); + if (!dentry) +@@ -808,6 +904,12 @@ out: + return dentry; + } + ++struct dentry * lookup_hash(struct qstr *name, struct dentry * base) ++{ ++ return lookup_hash_it(name, base, NULL); ++} ++ ++ + /* SMP-safe */ + struct dentry * lookup_one_len(const char * name, struct dentry * base, int len) + { +@@ -829,7 +931,7 @@ struct dentry * lookup_one_len(const cha + } + this.hash = end_name_hash(hash); + +- return lookup_hash(&this, base); ++ return lookup_hash_it(&this, base, NULL); + access: + return ERR_PTR(-EACCES); + } +@@ -860,6 +962,23 @@ int __user_walk(const char *name, unsign + return err; + } + ++int __user_walk_it(const char *name, unsigned flags, struct nameidata *nd, ++ struct lookup_intent *it) ++{ ++ char *tmp; ++ int err; ++ ++ tmp = getname(name); ++ err = PTR_ERR(tmp); ++ if (!IS_ERR(tmp)) { ++ err = 0; ++ if (path_init(tmp, flags, nd)) ++ err = path_walk_it(tmp, nd, it); ++ putname(tmp); ++ } ++ return err; ++} ++ + /* + * It's inline, so penalty for filesystems that don't use sticky bit is + * minimal. +@@ -955,7 +1074,8 @@ static inline int lookup_flags(unsigned + return retval; + } + +-int vfs_create(struct inode *dir, struct dentry *dentry, int mode) ++static int vfs_create_it(struct inode *dir, struct dentry *dentry, int mode, ++ struct lookup_intent *it) + { + int error; + +@@ -968,12 +1088,15 @@ int vfs_create(struct inode *dir, struct + goto exit_lock; + + error = -EACCES; /* shouldn't it be ENOSYS? */ +- if (!dir->i_op || !dir->i_op->create) ++ if (!dir->i_op || (!dir->i_op->create && !dir->i_op->create_it)) + goto exit_lock; + + DQUOT_INIT(dir); + lock_kernel(); +- error = dir->i_op->create(dir, dentry, mode); ++ if (dir->i_op->create_it) ++ error = dir->i_op->create_it(dir, dentry, mode, it); ++ else ++ error = dir->i_op->create(dir, dentry, mode); + unlock_kernel(); + exit_lock: + up(&dir->i_zombie); +@@ -982,6 +1105,11 @@ exit_lock: + return error; + } + ++int vfs_create(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ return vfs_create_it(dir, dentry, mode, NULL); ++} ++ + /* + * open_namei() + * +@@ -996,7 +1124,8 @@ exit_lock: + * for symlinks (where the permissions are checked later). + * SMP-safe + */ +-int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) ++int open_namei_it(const char *pathname, int flag, int mode, ++ struct nameidata *nd, struct lookup_intent *it) + { + int acc_mode, error = 0; + struct inode *inode; +@@ -1010,7 +1139,7 @@ int open_namei(const char * pathname, in + * The simplest case - just a plain lookup. + */ + if (!(flag & O_CREAT)) { +- error = path_lookup(pathname, lookup_flags(flag), nd); ++ error = path_lookup_it(pathname, lookup_flags(flag), nd, it); + if (error) + return error; + dentry = nd->dentry; +@@ -1020,6 +1149,10 @@ int open_namei(const char * pathname, in + /* + * Create - we need to know the parent. + */ ++ if (it) { ++ it->it_mode = mode; ++ it->it_op |= IT_CREAT; ++ } + error = path_lookup(pathname, LOOKUP_PARENT, nd); + if (error) + return error; +@@ -1035,7 +1168,7 @@ int open_namei(const char * pathname, in + + dir = nd->dentry; + down(&dir->d_inode->i_sem); +- dentry = lookup_hash(&nd->last, nd->dentry); ++ dentry = lookup_hash_it(&nd->last, nd->dentry, it); + + do_last: + error = PTR_ERR(dentry); +@@ -1044,11 +1177,12 @@ do_last: + goto exit; + } + ++ it->it_mode = mode; + /* Negative dentry, just create the file */ + if (!dentry->d_inode) { + if (!IS_POSIXACL(dir->d_inode)) + mode &= ~current->fs->umask; +- error = vfs_create(dir->d_inode, dentry, mode); ++ error = vfs_create_it(dir->d_inode, dentry, mode, it); + up(&dir->d_inode->i_sem); + dput(nd->dentry); + nd->dentry = dentry; +@@ -1073,7 +1207,7 @@ do_last: + error = -ELOOP; + if (flag & O_NOFOLLOW) + goto exit_dput; +- while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry)); ++ while (__follow_down(&nd->mnt,&dentry,it) && d_mountpoint(dentry)); + } + error = -ENOENT; + if (!dentry->d_inode) +@@ -1152,7 +1286,7 @@ ok: + if (!error) { + DQUOT_INIT(inode); + +- error = do_truncate(dentry, 0); ++ error = do_truncate(dentry, 0, 1); + } + put_write_access(inode); + if (error) +@@ -1164,8 +1298,10 @@ ok: + return 0; + + exit_dput: ++ intent_release(it); + dput(dentry); + exit: ++ intent_release(it); + path_release(nd); + return error; + +@@ -1184,7 +1320,16 @@ do_link: + * are done. Procfs-like symlinks just set LAST_BIND. + */ + UPDATE_ATIME(dentry->d_inode); ++ nd->it = it; + error = dentry->d_inode->i_op->follow_link(dentry, nd); ++ if (error) { ++ intent_release(it); ++ } else if (it != NULL && !(it->it_int_flags & IT_FL_FOLLOWED)) { ++ /* vfs_follow_link was never called */ ++ intent_release(it); ++ path_release(nd); ++ error = -ENOLINK; ++ } + dput(dentry); + if (error) + return error; +@@ -1206,13 +1351,20 @@ do_link: + } + dir = nd->dentry; + down(&dir->d_inode->i_sem); +- dentry = lookup_hash(&nd->last, nd->dentry); ++ dentry = lookup_hash_it(&nd->last, nd->dentry, it); + putname(nd->last.name); + goto do_last; + } + ++int open_namei(const char *pathname, int flag, int mode, struct nameidata *nd) ++{ ++ return open_namei_it(pathname, flag, mode, nd, NULL); ++} ++ ++ + /* SMP-safe */ +-static struct dentry *lookup_create(struct nameidata *nd, int is_dir) ++static struct dentry *lookup_create(struct nameidata *nd, int is_dir, ++ struct lookup_intent *it) + { + struct dentry *dentry; + +@@ -1220,7 +1372,7 @@ static struct dentry *lookup_create(stru + dentry = ERR_PTR(-EEXIST); + if (nd->last_type != LAST_NORM) + goto fail; +- dentry = lookup_hash(&nd->last, nd->dentry); ++ dentry = lookup_hash_it(&nd->last, nd->dentry, it); + if (IS_ERR(dentry)) + goto fail; + if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode) +@@ -1276,7 +1428,16 @@ asmlinkage long sys_mknod(const char * f + error = path_lookup(tmp, LOOKUP_PARENT, &nd); + if (error) + goto out; +- dentry = lookup_create(&nd, 0); ++ ++ if (nd.dentry->d_inode->i_op->mknod_raw) { ++ struct inode_operations *op = nd.dentry->d_inode->i_op; ++ error = op->mknod_raw(&nd, mode, dev); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto out2; ++ } ++ ++ dentry = lookup_create(&nd, 0, NULL); + error = PTR_ERR(dentry); + + if (!IS_POSIXACL(nd.dentry->d_inode)) +@@ -1298,6 +1459,7 @@ asmlinkage long sys_mknod(const char * f + dput(dentry); + } + up(&nd.dentry->d_inode->i_sem); ++out2: + path_release(&nd); + out: + putname(tmp); +@@ -1345,7 +1507,14 @@ asmlinkage long sys_mkdir(const char * p + error = path_lookup(tmp, LOOKUP_PARENT, &nd); + if (error) + goto out; +- dentry = lookup_create(&nd, 1); ++ if (nd.dentry->d_inode->i_op->mkdir_raw) { ++ struct inode_operations *op = nd.dentry->d_inode->i_op; ++ error = op->mkdir_raw(&nd, mode); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto out2; ++ } ++ dentry = lookup_create(&nd, 1, NULL); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + if (!IS_POSIXACL(nd.dentry->d_inode)) +@@ -1354,6 +1523,7 @@ asmlinkage long sys_mkdir(const char * p + dput(dentry); + } + up(&nd.dentry->d_inode->i_sem); ++out2: + path_release(&nd); + out: + putname(tmp); +@@ -1454,8 +1624,16 @@ asmlinkage long sys_rmdir(const char * p + error = -EBUSY; + goto exit1; + } ++ if (nd.dentry->d_inode->i_op->rmdir_raw) { ++ struct inode_operations *op = nd.dentry->d_inode->i_op; ++ ++ error = op->rmdir_raw(&nd); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto exit1; ++ } + down(&nd.dentry->d_inode->i_sem); +- dentry = lookup_hash(&nd.last, nd.dentry); ++ dentry = lookup_hash_it(&nd.last, nd.dentry, NULL); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + error = vfs_rmdir(nd.dentry->d_inode, dentry); +@@ -1513,8 +1691,15 @@ asmlinkage long sys_unlink(const char * + error = -EISDIR; + if (nd.last_type != LAST_NORM) + goto exit1; ++ if (nd.dentry->d_inode->i_op->unlink_raw) { ++ struct inode_operations *op = nd.dentry->d_inode->i_op; ++ error = op->unlink_raw(&nd); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto exit1; ++ } + down(&nd.dentry->d_inode->i_sem); +- dentry = lookup_hash(&nd.last, nd.dentry); ++ dentry = lookup_hash_it(&nd.last, nd.dentry, NULL); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + /* Why not before? Because we want correct error value */ +@@ -1581,15 +1766,23 @@ asmlinkage long sys_symlink(const char * + error = path_lookup(to, LOOKUP_PARENT, &nd); + if (error) + goto out; +- dentry = lookup_create(&nd, 0); ++ if (nd.dentry->d_inode->i_op->symlink_raw) { ++ struct inode_operations *op = nd.dentry->d_inode->i_op; ++ error = op->symlink_raw(&nd, from); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto out2; ++ } ++ dentry = lookup_create(&nd, 0, NULL); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + error = vfs_symlink(nd.dentry->d_inode, dentry, from); + dput(dentry); + } + up(&nd.dentry->d_inode->i_sem); ++ out2: + path_release(&nd); +-out: ++ out: + putname(to); + } + putname(from); +@@ -1665,7 +1858,14 @@ asmlinkage long sys_link(const char * ol + error = -EXDEV; + if (old_nd.mnt != nd.mnt) + goto out_release; +- new_dentry = lookup_create(&nd, 0); ++ if (nd.dentry->d_inode->i_op->link_raw) { ++ struct inode_operations *op = nd.dentry->d_inode->i_op; ++ error = op->link_raw(&old_nd, &nd); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto out_release; ++ } ++ new_dentry = lookup_create(&nd, 0, NULL); + error = PTR_ERR(new_dentry); + if (!IS_ERR(new_dentry)) { + error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); +@@ -1709,7 +1909,7 @@ exit: + * locking]. + */ + int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry) ++ struct inode *new_dir, struct dentry *new_dentry) + { + int error; + struct inode *target; +@@ -1788,7 +1988,7 @@ out_unlock: + } + + int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, +- struct inode *new_dir, struct dentry *new_dentry) ++ struct inode *new_dir, struct dentry *new_dentry) + { + int error; + +@@ -1876,9 +2076,18 @@ static inline int do_rename(const char * + if (newnd.last_type != LAST_NORM) + goto exit2; + ++ if (old_dir->d_inode->i_op->rename_raw) { ++ lock_kernel(); ++ error = old_dir->d_inode->i_op->rename_raw(&oldnd, &newnd); ++ unlock_kernel(); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto exit2; ++ } ++ + double_lock(new_dir, old_dir); + +- old_dentry = lookup_hash(&oldnd.last, old_dir); ++ old_dentry = lookup_hash_it(&oldnd.last, old_dir, NULL); + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) + goto exit3; +@@ -1894,16 +2103,16 @@ static inline int do_rename(const char * + if (newnd.last.name[newnd.last.len]) + goto exit4; + } +- new_dentry = lookup_hash(&newnd.last, new_dir); ++ new_dentry = lookup_hash_it(&newnd.last, new_dir, NULL); + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit4; + ++ + lock_kernel(); + error = vfs_rename(old_dir->d_inode, old_dentry, + new_dir->d_inode, new_dentry); + unlock_kernel(); +- + dput(new_dentry); + exit4: + dput(old_dentry); +@@ -1954,20 +2163,28 @@ out: + } + + static inline int +-__vfs_follow_link(struct nameidata *nd, const char *link) ++__vfs_follow_link(struct nameidata *nd, const char *link, ++ struct lookup_intent *it) + { + int res = 0; + char *name; + if (IS_ERR(link)) + goto fail; + ++ if (it == NULL) ++ it = nd->it; ++ else if (it != nd->it) ++ printk("it != nd->it: tell phil@clusterfs.com\n"); ++ if (it != NULL) ++ it->it_int_flags |= IT_FL_FOLLOWED; ++ + if (*link == '/') { + path_release(nd); + if (!walk_init_root(link, nd)) + /* weird __emul_prefix() stuff did it */ + goto out; + } +- res = link_path_walk(link, nd); ++ res = link_path_walk_it(link, nd, it); + out: + if (current->link_count || res || nd->last_type!=LAST_NORM) + return res; +@@ -1989,7 +2206,13 @@ fail: + + int vfs_follow_link(struct nameidata *nd, const char *link) + { +- return __vfs_follow_link(nd, link); ++ return __vfs_follow_link(nd, link, NULL); ++} ++ ++int vfs_follow_link_it(struct nameidata *nd, const char *link, ++ struct lookup_intent *it) ++{ ++ return __vfs_follow_link(nd, link, it); + } + + /* get the link contents into pagecache */ +@@ -2031,7 +2254,7 @@ int page_follow_link(struct dentry *dent + { + struct page *page = NULL; + char *s = page_getlink(dentry, &page); +- int res = __vfs_follow_link(nd, s); ++ int res = __vfs_follow_link(nd, s, NULL); + if (page) { + kunmap(page); + page_cache_release(page); +--- linux/fs/open.c~vfs_intent-2.4.20-hp Thu Nov 28 15:53:15 2002 ++++ linux-mmonroe/fs/open.c Sat Jul 12 14:05:24 2003 +@@ -19,6 +19,8 @@ + #include + + #define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m)) ++extern int path_walk_it(const char *name, struct nameidata *nd, ++ struct lookup_intent *it); + + int vfs_statfs(struct super_block *sb, struct statfs *buf) + { +@@ -95,9 +97,10 @@ void fd_install(unsigned int fd, struct + write_unlock(&files->file_lock); + } + +-int do_truncate(struct dentry *dentry, loff_t length) ++int do_truncate(struct dentry *dentry, loff_t length, int called_from_open) + { + struct inode *inode = dentry->d_inode; ++ struct inode_operations *op = dentry->d_inode->i_op; + int error; + struct iattr newattrs; + +@@ -108,7 +111,13 @@ int do_truncate(struct dentry *dentry, l + down(&inode->i_sem); + newattrs.ia_size = length; + newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; +- error = notify_change(dentry, &newattrs); ++ if (called_from_open) ++ newattrs.ia_valid |= ATTR_FROM_OPEN; ++ if (op->setattr_raw) { ++ newattrs.ia_valid |= ATTR_RAW; ++ error = op->setattr_raw(inode, &newattrs); ++ } else ++ error = notify_change(dentry, &newattrs); + up(&inode->i_sem); + return error; + } +@@ -118,12 +127,13 @@ static inline long do_sys_truncate(const + struct nameidata nd; + struct inode * inode; + int error; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + + error = -EINVAL; + if (length < 0) /* sorry, but loff_t says... */ + goto out; + +- error = user_path_walk(path, &nd); ++ error = user_path_walk_it(path, &nd, &it); + if (error) + goto out; + inode = nd.dentry->d_inode; +@@ -163,11 +173,13 @@ static inline long do_sys_truncate(const + error = locks_verify_truncate(inode, NULL, length); + if (!error) { + DQUOT_INIT(inode); +- error = do_truncate(nd.dentry, length); ++ intent_release(&it); ++ error = do_truncate(nd.dentry, length, 0); + } + put_write_access(inode); + + dput_and_out: ++ intent_release(&it); + path_release(&nd); + out: + return error; +@@ -215,7 +227,7 @@ static inline long do_sys_ftruncate(unsi + + error = locks_verify_truncate(inode, file, length); + if (!error) +- error = do_truncate(dentry, length); ++ error = do_truncate(dentry, length, 0); + out_putf: + fput(file); + out: +@@ -260,11 +272,13 @@ asmlinkage long sys_utime(char * filenam + struct inode * inode; + struct iattr newattrs; + +- error = user_path_walk(filename, &nd); ++ error = user_path_walk_it(filename, &nd, NULL); + if (error) + goto out; + inode = nd.dentry->d_inode; + ++ /* this is safe without a Lustre lock because it only depends ++ on the super block */ + error = -EROFS; + if (IS_RDONLY(inode)) + goto dput_and_out; +@@ -279,11 +293,25 @@ asmlinkage long sys_utime(char * filenam + goto dput_and_out; + + newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; +- } else { ++ } ++ ++ if (inode->i_op->setattr_raw) { ++ struct inode_operations *op = nd.dentry->d_inode->i_op; ++ ++ newattrs.ia_valid |= ATTR_RAW; ++ error = op->setattr_raw(inode, &newattrs); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto dput_and_out; ++ } ++ ++ error = -EPERM; ++ if (!times) { + if (current->fsuid != inode->i_uid && + (error = permission(inode,MAY_WRITE)) != 0) + goto dput_and_out; + } ++ + error = notify_change(nd.dentry, &newattrs); + dput_and_out: + path_release(&nd); +@@ -304,12 +332,14 @@ asmlinkage long sys_utimes(char * filena + struct inode * inode; + struct iattr newattrs; + +- error = user_path_walk(filename, &nd); ++ error = user_path_walk_it(filename, &nd, NULL); + + if (error) + goto out; + inode = nd.dentry->d_inode; + ++ /* this is safe without a Lustre lock because it only depends ++ on the super block */ + error = -EROFS; + if (IS_RDONLY(inode)) + goto dput_and_out; +@@ -324,7 +354,20 @@ asmlinkage long sys_utimes(char * filena + newattrs.ia_atime = times[0].tv_sec; + newattrs.ia_mtime = times[1].tv_sec; + newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; +- } else { ++ } ++ ++ if (inode->i_op->setattr_raw) { ++ struct inode_operations *op = nd.dentry->d_inode->i_op; ++ ++ newattrs.ia_valid |= ATTR_RAW; ++ error = op->setattr_raw(inode, &newattrs); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto dput_and_out; ++ } ++ ++ error = -EPERM; ++ if (!utimes) { + if (current->fsuid != inode->i_uid && + (error = permission(inode,MAY_WRITE)) != 0) + goto dput_and_out; +@@ -347,6 +390,7 @@ asmlinkage long sys_access(const char * + int old_fsuid, old_fsgid; + kernel_cap_t old_cap; + int res; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + + if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ + return -EINVAL; +@@ -364,13 +408,14 @@ asmlinkage long sys_access(const char * + else + current->cap_effective = current->cap_permitted; + +- res = user_path_walk(filename, &nd); ++ res = user_path_walk_it(filename, &nd, &it); + if (!res) { + res = permission(nd.dentry->d_inode, mode); + /* SuS v2 requires we report a read only fs too */ + if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode) + && !special_file(nd.dentry->d_inode->i_mode)) + res = -EROFS; ++ intent_release(&it); + path_release(&nd); + } + +@@ -385,8 +430,9 @@ asmlinkage long sys_chdir(const char * f + { + int error; + struct nameidata nd; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + +- error = __user_walk(filename,LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY,&nd); ++ error = __user_walk_it(filename,LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY,&nd, &it); + if (error) + goto out; + +@@ -397,6 +443,7 @@ asmlinkage long sys_chdir(const char * f + set_fs_pwd(current->fs, nd.mnt, nd.dentry); + + dput_and_out: ++ intent_release(&it); + path_release(&nd); + out: + return error; +@@ -436,9 +483,10 @@ asmlinkage long sys_chroot(const char * + { + int error; + struct nameidata nd; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + +- error = __user_walk(filename, LOOKUP_POSITIVE | LOOKUP_FOLLOW | +- LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); ++ error = __user_walk_it(filename, LOOKUP_POSITIVE | LOOKUP_FOLLOW | ++ LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd, &it); + if (error) + goto out; + +@@ -454,6 +502,7 @@ asmlinkage long sys_chroot(const char * + set_fs_altroot(); + error = 0; + dput_and_out: ++ intent_release(&it); + path_release(&nd); + out: + return error; +@@ -508,6 +557,18 @@ asmlinkage long sys_chmod(const char * f + if (IS_RDONLY(inode)) + goto dput_and_out; + ++ if (inode->i_op->setattr_raw) { ++ struct inode_operations *op = nd.dentry->d_inode->i_op; ++ ++ newattrs.ia_mode = mode; ++ newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; ++ newattrs.ia_valid |= ATTR_RAW; ++ error = op->setattr_raw(inode, &newattrs); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ goto dput_and_out; ++ } ++ + error = -EPERM; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + goto dput_and_out; +@@ -538,6 +599,20 @@ static int chown_common(struct dentry * + error = -EROFS; + if (IS_RDONLY(inode)) + goto out; ++ ++ if (inode->i_op->setattr_raw) { ++ struct inode_operations *op = dentry->d_inode->i_op; ++ ++ newattrs.ia_uid = user; ++ newattrs.ia_gid = group; ++ newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; ++ newattrs.ia_valid |= ATTR_RAW; ++ error = op->setattr_raw(inode, &newattrs); ++ /* the file system wants to use normal vfs path now */ ++ if (error != -EOPNOTSUPP) ++ return error; ++ } ++ + error = -EPERM; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + goto out; +@@ -638,10 +713,12 @@ asmlinkage long sys_fchown(unsigned int + * for the internal routines (ie open_namei()/follow_link() etc). 00 is + * used by symlinks. + */ ++ + struct file *filp_open(const char * filename, int flags, int mode) + { + int namei_flags, error; + struct nameidata nd; ++ struct lookup_intent it = { .it_op = IT_OPEN, .it_flags = flags }; + + namei_flags = flags; + if ((namei_flags+1) & O_ACCMODE) +@@ -649,14 +726,15 @@ struct file *filp_open(const char * file + if (namei_flags & O_TRUNC) + namei_flags |= 2; + +- error = open_namei(filename, namei_flags, mode, &nd); +- if (!error) +- return dentry_open(nd.dentry, nd.mnt, flags); ++ error = open_namei_it(filename, namei_flags, mode, &nd, &it); ++ if (error) ++ return ERR_PTR(error); + +- return ERR_PTR(error); ++ return dentry_open_it(nd.dentry, nd.mnt, flags, &it); + } + +-struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) ++struct file *dentry_open_it(struct dentry *dentry, struct vfsmount *mnt, ++ int flags, struct lookup_intent *it) + { + struct file * f; + struct inode *inode; +@@ -693,12 +771,15 @@ struct file *dentry_open(struct dentry * + } + + if (f->f_op && f->f_op->open) { ++ f->f_it = it; + error = f->f_op->open(inode,f); ++ f->f_it = NULL; + if (error) + goto cleanup_all; + } + f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); + ++ intent_release(it); + return f; + + cleanup_all: +@@ -713,11 +794,17 @@ cleanup_all: + cleanup_file: + put_filp(f); + cleanup_dentry: ++ intent_release(it); + dput(dentry); + mntput(mnt); + return ERR_PTR(error); + } + ++struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) ++{ ++ return dentry_open_it(dentry, mnt, flags, NULL); ++} ++ + /* + * Find an empty file descriptor entry, and mark it busy. + */ +--- linux/fs/stat.c~vfs_intent-2.4.20-hp Thu Sep 13 16:04:43 2001 ++++ linux-mmonroe/fs/stat.c Sat Jul 12 14:05:24 2003 +@@ -17,10 +17,12 @@ + * Revalidate the inode. This is required for proper NFS attribute caching. + */ + static __inline__ int +-do_revalidate(struct dentry *dentry) ++do_revalidate(struct dentry *dentry, struct lookup_intent *it) + { + struct inode * inode = dentry->d_inode; +- if (inode->i_op && inode->i_op->revalidate) ++ if (inode->i_op && inode->i_op->revalidate_it) ++ return inode->i_op->revalidate_it(dentry, it); ++ else if (inode->i_op && inode->i_op->revalidate) + return inode->i_op->revalidate(dentry); + return 0; + } +@@ -135,13 +137,15 @@ static int cp_new_stat(struct inode * in + asmlinkage long sys_stat(char * filename, struct __old_kernel_stat * statbuf) + { + struct nameidata nd; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + int error; + +- error = user_path_walk(filename, &nd); ++ error = user_path_walk_it(filename, &nd, &it); + if (!error) { +- error = do_revalidate(nd.dentry); ++ error = do_revalidate(nd.dentry, &it); + if (!error) + error = cp_old_stat(nd.dentry->d_inode, statbuf); ++ intent_release(&it); + path_release(&nd); + } + return error; +@@ -151,13 +155,15 @@ asmlinkage long sys_stat(char * filename + asmlinkage long sys_newstat(char * filename, struct stat * statbuf) + { + struct nameidata nd; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + int error; + +- error = user_path_walk(filename, &nd); ++ error = user_path_walk_it(filename, &nd, &it); + if (!error) { +- error = do_revalidate(nd.dentry); ++ error = do_revalidate(nd.dentry, &it); + if (!error) + error = cp_new_stat(nd.dentry->d_inode, statbuf); ++ intent_release(&it); + path_release(&nd); + } + return error; +@@ -172,13 +178,15 @@ asmlinkage long sys_newstat(char * filen + asmlinkage long sys_lstat(char * filename, struct __old_kernel_stat * statbuf) + { + struct nameidata nd; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + int error; + +- error = user_path_walk_link(filename, &nd); ++ error = user_path_walk_link_it(filename, &nd, &it); + if (!error) { +- error = do_revalidate(nd.dentry); ++ error = do_revalidate(nd.dentry, &it); + if (!error) + error = cp_old_stat(nd.dentry->d_inode, statbuf); ++ intent_release(&it); + path_release(&nd); + } + return error; +@@ -189,13 +197,15 @@ asmlinkage long sys_lstat(char * filenam + asmlinkage long sys_newlstat(char * filename, struct stat * statbuf) + { + struct nameidata nd; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + int error; + +- error = user_path_walk_link(filename, &nd); ++ error = user_path_walk_link_it(filename, &nd, &it); + if (!error) { +- error = do_revalidate(nd.dentry); ++ error = do_revalidate(nd.dentry, &it); + if (!error) + error = cp_new_stat(nd.dentry->d_inode, statbuf); ++ intent_release(&it); + path_release(&nd); + } + return error; +@@ -216,7 +226,7 @@ asmlinkage long sys_fstat(unsigned int f + if (f) { + struct dentry * dentry = f->f_dentry; + +- err = do_revalidate(dentry); ++ err = do_revalidate(dentry, NULL); + if (!err) + err = cp_old_stat(dentry->d_inode, statbuf); + fput(f); +@@ -235,7 +245,7 @@ asmlinkage long sys_newfstat(unsigned in + if (f) { + struct dentry * dentry = f->f_dentry; + +- err = do_revalidate(dentry); ++ err = do_revalidate(dentry, NULL); + if (!err) + err = cp_new_stat(dentry->d_inode, statbuf); + fput(f); +@@ -257,7 +267,7 @@ asmlinkage long sys_readlink(const char + + error = -EINVAL; + if (inode->i_op && inode->i_op->readlink && +- !(error = do_revalidate(nd.dentry))) { ++ !(error = do_revalidate(nd.dentry, NULL))) { + UPDATE_ATIME(inode); + error = inode->i_op->readlink(nd.dentry, buf, bufsiz); + } +@@ -333,12 +343,14 @@ asmlinkage long sys_stat64(char * filena + { + struct nameidata nd; + int error; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + +- error = user_path_walk(filename, &nd); ++ error = user_path_walk_it(filename, &nd, &it); + if (!error) { +- error = do_revalidate(nd.dentry); ++ error = do_revalidate(nd.dentry, &it); + if (!error) + error = cp_new_stat64(nd.dentry->d_inode, statbuf); ++ intent_release(&it); + path_release(&nd); + } + return error; +@@ -348,12 +360,14 @@ asmlinkage long sys_lstat64(char * filen + { + struct nameidata nd; + int error; ++ struct lookup_intent it = { .it_op = IT_GETATTR }; + +- error = user_path_walk_link(filename, &nd); ++ error = user_path_walk_link_it(filename, &nd, &it); + if (!error) { +- error = do_revalidate(nd.dentry); ++ error = do_revalidate(nd.dentry, &it); + if (!error) + error = cp_new_stat64(nd.dentry->d_inode, statbuf); ++ intent_release(&it); + path_release(&nd); + } + return error; +@@ -368,7 +382,7 @@ asmlinkage long sys_fstat64(unsigned lon + if (f) { + struct dentry * dentry = f->f_dentry; + +- err = do_revalidate(dentry); ++ err = do_revalidate(dentry, NULL); + if (!err) + err = cp_new_stat64(dentry->d_inode, statbuf); + fput(f); +--- linux/fs/proc/base.c~vfs_intent-2.4.20-hp Sat Jul 12 14:04:46 2003 ++++ linux-mmonroe/fs/proc/base.c Sat Jul 12 14:05:24 2003 +@@ -481,6 +481,9 @@ static int proc_pid_follow_link(struct d + + error = inode->u.proc_i.op.proc_get_link(inode, &nd->dentry, &nd->mnt); + nd->last_type = LAST_BIND; ++ ++ if (nd->it != NULL) ++ nd->it->it_int_flags |= IT_FL_FOLLOWED; + out: + return error; + } +--- linux/include/linux/dcache.h~vfs_intent-2.4.20-hp Thu Nov 28 15:53:15 2002 ++++ linux-mmonroe/include/linux/dcache.h Sat Jul 12 14:06:04 2003 +@@ -5,8 +5,47 @@ + + #include + #include ++#include + #include + ++#define IT_OPEN 0x0001 ++#define IT_CREAT 0x0002 ++#define IT_READDIR 0x0004 ++#define IT_GETATTR 0x0008 ++#define IT_LOOKUP 0x0010 ++#define IT_UNLINK 0x0020 ++#define IT_GETXATTR 0x0040 ++#define IT_EXEC 0x0080 ++#define IT_PIN 0x0100 ++ ++#define IT_FL_LOCKED 0x0001 ++#define IT_FL_FOLLOWED 0x0002 /* set by vfs_follow_link */ ++ ++#define INTENT_MAGIC 0x19620323 ++ ++struct lookup_intent { ++ int it_op; ++ void (*it_op_release)(struct lookup_intent *); ++ int it_magic; ++ int it_mode; ++ int it_flags; ++ int it_disposition; ++ int it_status; ++ int it_int_flags; ++ __u64 it_lock_handle[2]; ++ int it_lock_mode; ++ void *it_data; ++}; ++ ++static inline void intent_init(struct lookup_intent *it, int op, int flags) ++{ ++ memset(it, 0, sizeof(*it)); ++ it->it_magic = INTENT_MAGIC; ++ it->it_op = op; ++ it->it_flags = flags; ++} ++ ++ + /* + * linux/include/linux/dcache.h + * +@@ -91,8 +130,22 @@ struct dentry_operations { + int (*d_delete)(struct dentry *); + void (*d_release)(struct dentry *); + void (*d_iput)(struct dentry *, struct inode *); ++ int (*d_revalidate_it)(struct dentry *, int, struct lookup_intent *); ++ void (*d_pin)(struct dentry *, struct vfsmount * , int); ++ void (*d_unpin)(struct dentry *, struct vfsmount *, int); + }; + ++#define PIN(de,mnt,flag) if (de->d_op && de->d_op->d_pin) \ ++ de->d_op->d_pin(de, mnt, flag); ++#define UNPIN(de,mnt,flag) if (de->d_op && de->d_op->d_unpin) \ ++ de->d_op->d_unpin(de, mnt, flag); ++ ++ ++/* defined in fs/namei.c */ ++extern void intent_release(struct lookup_intent *it); ++/* defined in fs/dcache.c */ ++extern void __d_rehash(struct dentry * entry, int lock); ++ + /* the dentry parameter passed to d_hash and d_compare is the parent + * directory of the entries to be compared. It is used in case these + * functions need any directory specific information for determining +@@ -124,6 +177,7 @@ d_iput: no no yes + * s_nfsd_free_path semaphore will be down + */ + #define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */ ++#define DCACHE_LUSTRE_INVALID 0x0010 /* Lustre invalidated */ + + extern spinlock_t dcache_lock; + +--- linux/include/linux/fs.h~vfs_intent-2.4.20-hp Sat Jul 12 14:05:20 2003 ++++ linux-mmonroe/include/linux/fs.h Sat Jul 12 14:05:24 2003 +@@ -340,6 +340,9 @@ extern void set_bh_page(struct buffer_he + #define ATTR_MTIME_SET 256 + #define ATTR_FORCE 512 /* Not a change, but a change it */ + #define ATTR_ATTR_FLAG 1024 ++#define ATTR_RAW 0x0800 /* file system, not vfs will massage attrs */ ++#define ATTR_FROM_OPEN 0x1000 /* called from open path, ie O_TRUNC */ ++#define ATTR_CTIME_SET 0x2000 + + /* + * This is the Inode Attributes structure, used for notify_change(). It +@@ -576,6 +579,7 @@ struct file { + + /* needed for tty driver, and maybe others */ + void *private_data; ++ struct lookup_intent *f_it; + + /* preallocated helper kiobuf to speedup O_DIRECT */ + struct kiobuf *f_iobuf; +@@ -697,6 +701,7 @@ struct nameidata { + struct qstr last; + unsigned int flags; + int last_type; ++ struct lookup_intent *it; + }; + + /* +@@ -817,7 +822,8 @@ extern int vfs_symlink(struct inode *, s + extern int vfs_link(struct dentry *, struct inode *, struct dentry *); + extern int vfs_rmdir(struct inode *, struct dentry *); + extern int vfs_unlink(struct inode *, struct dentry *); +-extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); ++int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry); + + /* + * File types +@@ -877,21 +883,32 @@ struct file_operations { + + struct inode_operations { + int (*create) (struct inode *,struct dentry *,int); ++ int (*create_it) (struct inode *,struct dentry *,int, struct lookup_intent *); + struct dentry * (*lookup) (struct inode *,struct dentry *); ++ struct dentry * (*lookup_it) (struct inode *,struct dentry *, struct lookup_intent *, int flags); + int (*link) (struct dentry *,struct inode *,struct dentry *); ++ int (*link_raw) (struct nameidata *,struct nameidata *); + int (*unlink) (struct inode *,struct dentry *); ++ int (*unlink_raw) (struct nameidata *); + int (*symlink) (struct inode *,struct dentry *,const char *); ++ int (*symlink_raw) (struct nameidata *,const char *); + int (*mkdir) (struct inode *,struct dentry *,int); ++ int (*mkdir_raw) (struct nameidata *,int); + int (*rmdir) (struct inode *,struct dentry *); ++ int (*rmdir_raw) (struct nameidata *); + int (*mknod) (struct inode *,struct dentry *,int,int); ++ int (*mknod_raw) (struct nameidata *,int,dev_t); + int (*rename) (struct inode *, struct dentry *, + struct inode *, struct dentry *); ++ int (*rename_raw) (struct nameidata *, struct nameidata *); + int (*readlink) (struct dentry *, char *,int); + int (*follow_link) (struct dentry *, struct nameidata *); + void (*truncate) (struct inode *); + int (*permission) (struct inode *, int); + int (*revalidate) (struct dentry *); ++ int (*revalidate_it) (struct dentry *, struct lookup_intent *); + int (*setattr) (struct dentry *, struct iattr *); ++ int (*setattr_raw) (struct inode *, struct iattr *); + int (*getattr) (struct dentry *, struct iattr *); + int (*setxattr) (struct dentry *, const char *, void *, size_t, int); + ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); +@@ -1088,10 +1105,14 @@ static inline int get_lease(struct inode + + asmlinkage long sys_open(const char *, int, int); + asmlinkage long sys_close(unsigned int); /* yes, it's really unsigned */ +-extern int do_truncate(struct dentry *, loff_t start); ++extern int do_truncate(struct dentry *, loff_t start, int called_from_open); + + extern struct file *filp_open(const char *, int, int); + extern struct file * dentry_open(struct dentry *, struct vfsmount *, int); ++extern int open_namei_it(const char *filename, int namei_flags, int mode, ++ struct nameidata *nd, struct lookup_intent *it); ++extern struct file *dentry_open_it(struct dentry *dentry, struct vfsmount *mnt, ++ int flags, struct lookup_intent *it); + extern int filp_close(struct file *, fl_owner_t id); + extern char * getname(const char *); + +@@ -1353,6 +1374,7 @@ typedef int (*read_actor_t)(read_descrip + extern loff_t default_llseek(struct file *file, loff_t offset, int origin); + + extern int FASTCALL(__user_walk(const char *, unsigned, struct nameidata *)); ++extern int FASTCALL(__user_walk_it(const char *, unsigned, struct nameidata *, struct lookup_intent *it)); + extern int FASTCALL(path_init(const char *, unsigned, struct nameidata *)); + extern int FASTCALL(path_walk(const char *, struct nameidata *)); + extern int FASTCALL(path_lookup(const char *, unsigned, struct nameidata *)); +@@ -1364,6 +1386,8 @@ extern struct dentry * lookup_one_len(co + extern struct dentry * lookup_hash(struct qstr *, struct dentry *); + #define user_path_walk(name,nd) __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd) + #define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd) ++#define user_path_walk_it(name,nd,it) __user_walk_it(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd, it) ++#define user_path_walk_link_it(name,nd,it) __user_walk_it(name, LOOKUP_POSITIVE, nd, it) + + extern void inode_init_once(struct inode *); + extern void iput(struct inode *); +@@ -1499,6 +1523,8 @@ extern struct file_operations generic_ro + + extern int vfs_readlink(struct dentry *, char *, int, const char *); + extern int vfs_follow_link(struct nameidata *, const char *); ++extern int vfs_follow_link_it(struct nameidata *, const char *, ++ struct lookup_intent *it); + extern int page_readlink(struct dentry *, char *, int); + extern int page_follow_link(struct dentry *, struct nameidata *); + extern struct inode_operations page_symlink_inode_operations; +--- linux/include/linux/fs_struct.h~vfs_intent-2.4.20-hp Fri Jul 13 15:10:44 2001 ++++ linux-mmonroe/include/linux/fs_struct.h Sat Jul 12 14:05:24 2003 +@@ -34,10 +34,12 @@ static inline void set_fs_root(struct fs + write_lock(&fs->lock); + old_root = fs->root; + old_rootmnt = fs->rootmnt; ++ PIN(dentry, mnt, 1); + fs->rootmnt = mntget(mnt); + fs->root = dget(dentry); + write_unlock(&fs->lock); + if (old_root) { ++ UNPIN(old_root, old_rootmnt, 1); + dput(old_root); + mntput(old_rootmnt); + } +@@ -57,10 +59,12 @@ static inline void set_fs_pwd(struct fs_ + write_lock(&fs->lock); + old_pwd = fs->pwd; + old_pwdmnt = fs->pwdmnt; ++ PIN(dentry, mnt, 0); + fs->pwdmnt = mntget(mnt); + fs->pwd = dget(dentry); + write_unlock(&fs->lock); + if (old_pwd) { ++ UNPIN(old_pwd, old_pwdmnt, 0); + dput(old_pwd); + mntput(old_pwdmnt); + } +--- linux/kernel/ksyms.c~vfs_intent-2.4.20-hp Sat Jul 12 14:05:21 2003 ++++ linux-mmonroe/kernel/ksyms.c Sat Jul 12 14:05:24 2003 +@@ -296,6 +296,7 @@ EXPORT_SYMBOL(read_cache_page); + EXPORT_SYMBOL(set_page_dirty); + EXPORT_SYMBOL(vfs_readlink); + EXPORT_SYMBOL(vfs_follow_link); ++EXPORT_SYMBOL(vfs_follow_link_it); + EXPORT_SYMBOL(page_readlink); + EXPORT_SYMBOL(page_follow_link); + EXPORT_SYMBOL(page_symlink_inode_operations); +--- linux/kernel/fork.c~vfs_intent-2.4.20-hp Sat Jul 12 14:04:49 2003 ++++ linux-mmonroe/kernel/fork.c Sat Jul 12 14:05:24 2003 +@@ -388,10 +388,13 @@ static inline struct fs_struct *__copy_f + fs->umask = old->umask; + read_lock(&old->lock); + fs->rootmnt = mntget(old->rootmnt); ++ PIN(old->pwd, old->pwdmnt, 0); ++ PIN(old->root, old->rootmnt, 1); + fs->root = dget(old->root); + fs->pwdmnt = mntget(old->pwdmnt); + fs->pwd = dget(old->pwd); + if (old->altroot) { ++ PIN(old->altroot, old->altrootmnt, 1); + fs->altrootmnt = mntget(old->altrootmnt); + fs->altroot = dget(old->altroot); + } else { +--- linux/kernel/exit.c~vfs_intent-2.4.20-hp Sat Jul 12 14:04:49 2003 ++++ linux-mmonroe/kernel/exit.c Sat Jul 12 14:05:24 2003 +@@ -239,11 +239,14 @@ static inline void __put_fs_struct(struc + { + /* No need to hold fs->lock if we are killing it */ + if (atomic_dec_and_test(&fs->count)) { ++ UNPIN(fs->pwd, fs->pwdmnt, 0); ++ UNPIN(fs->root, fs->rootmnt, 1); + dput(fs->root); + mntput(fs->rootmnt); + dput(fs->pwd); + mntput(fs->pwdmnt); + if (fs->altroot) { ++ UNPIN(fs->altroot, fs->altrootmnt, 1); + dput(fs->altroot); + mntput(fs->altrootmnt); + } + +_ diff --git a/lustre/kernel_patches/pc/ext3-map_inode_page.pc b/lustre/kernel_patches/pc/ext3-map_inode_page.pc new file mode 100644 index 0000000..6631de4 --- /dev/null +++ b/lustre/kernel_patches/pc/ext3-map_inode_page.pc @@ -0,0 +1,3 @@ +fs/ext3/inode.c +fs/ext3/ext3-exports.c +fs/ext3/Makefile diff --git a/lustre/kernel_patches/pc/ext3_map_inode_page_chaos.pc b/lustre/kernel_patches/pc/ext3_map_inode_page_chaos.pc new file mode 100644 index 0000000..9ed5141 --- /dev/null +++ b/lustre/kernel_patches/pc/ext3_map_inode_page_chaos.pc @@ -0,0 +1,2 @@ +fs/ext3/inode.c +fs/ext3/ext3-exports.c diff --git a/lustre/kernel_patches/pc/linux-2.4.18-14-kgdb-1.6.pc b/lustre/kernel_patches/pc/linux-2.4.18-14-kgdb-1.6.pc new file mode 100644 index 0000000..44dc7de --- /dev/null +++ b/lustre/kernel_patches/pc/linux-2.4.18-14-kgdb-1.6.pc @@ -0,0 +1,26 @@ +arch/i386/config.in +arch/i386/kernel/entry.S +arch/i386/kernel/gdbstart.c +arch/i386/kernel/gdbstub.c +arch/i386/kernel/Makefile +arch/i386/kernel/nmi.c +arch/i386/kernel/signal.c +arch/i386/kernel/traps.c +arch/i386/Makefile +arch/i386/mm/fault.c +Documentation/Configure.help +Documentation/i386/gdb-serial.txt +drivers/char/gdbserial.c +drivers/char/Makefile +drivers/char/serial.c +drivers/char/tty_io.c +include/asm-i386/ioctls.h +include/asm-i386/page.h +include/asm-i386/processor.h +include/linux/dcache.h +include/linux/gdb.h +include/linux/sched.h +init/main.c +kernel/ksyms.c +kernel/sched.c +Makefile diff --git a/lustre/kernel_patches/pc/vfs_intent-2.4.20-hp.pc b/lustre/kernel_patches/pc/vfs_intent-2.4.20-hp.pc new file mode 100644 index 0000000..f3375a3 --- /dev/null +++ b/lustre/kernel_patches/pc/vfs_intent-2.4.20-hp.pc @@ -0,0 +1,14 @@ +fs/exec.c +fs/dcache.c +fs/namespace.c +fs/namei.c +fs/nfsd/vfs.c +fs/open.c +fs/stat.c +fs/proc/base.c +include/linux/dcache.h +include/linux/fs.h +include/linux/fs_struct.h +kernel/ksyms.c +kernel/fork.c +kernel/exit.c diff --git a/lustre/kernel_patches/series/chaos-kgdb b/lustre/kernel_patches/series/chaos-kgdb new file mode 100644 index 0000000..933af4a1 --- /dev/null +++ b/lustre/kernel_patches/series/chaos-kgdb @@ -0,0 +1,22 @@ +linux-2.4.18-14-kgdb-1.6.patch +dev_read_only.patch +exports.patch +kmem_cache_validate.patch +lustre_version.patch +vfs_intent-2.4.18-18-chaos65.patch +invalidate_show.patch +iod-rmap-exports.patch +export-truncate.patch +htree-ext3-2.4.18.patch +linux-2.4.18ea-0.8.26.patch +ext3-2.4-ino_t.patch +ext3-2.4.18-ino_sb_macro.patch +ext3-orphan_lock.patch +ext3-delete_thread-2.4.18.patch +extN-misc-fixup.patch +extN-noread.patch +extN-wantedi.patch +extN-san.patch +extN-2.4.18-ino_sb_fixup.patch +ext3_map_inode_page_chaos.patch +iopen-2.4.18.patch diff --git a/lustre/kernel_patches/txt/ext3-map_inode_page.txt b/lustre/kernel_patches/txt/ext3-map_inode_page.txt new file mode 100644 index 0000000..010cdb7 --- /dev/null +++ b/lustre/kernel_patches/txt/ext3-map_inode_page.txt @@ -0,0 +1,3 @@ +DESC +(undescribed patch) +EDESC diff --git a/lustre/kernel_patches/txt/ext3_map_inode_page_chaos.txt b/lustre/kernel_patches/txt/ext3_map_inode_page_chaos.txt new file mode 100644 index 0000000..010cdb7 --- /dev/null +++ b/lustre/kernel_patches/txt/ext3_map_inode_page_chaos.txt @@ -0,0 +1,3 @@ +DESC +(undescribed patch) +EDESC -- 1.8.3.1